diff --git a/site/src/App/Code/Code.tsx b/site/src/App/Code/Code.tsx index 254ea8490e..747a938b34 100644 --- a/site/src/App/Code/Code.tsx +++ b/site/src/App/Code/Code.tsx @@ -34,6 +34,7 @@ import * as styleRefs from './Code.treat'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import editorTheme from './editorTheme'; import { ThemedExample } from '../ThemeSetting'; +import usePlayroomScope from '../../../../lib/playroom/useScope'; const formatSnippet = memoize( (snippet) => @@ -158,26 +159,35 @@ export const CodeBlock = ({ interface CodeProps { playroom?: boolean; + displayCode?: string; collapsedByDefault?: boolean; - children: ReactChild; + children: + | ReactChild + | ((playroomScope: ReturnType) => ReactChild); } export default ({ playroom = true, collapsedByDefault = false, + displayCode, children, }: CodeProps) => { const [hideCode, setHideCode] = useState(collapsedByDefault); const { playroomUrl } = useConfig(); + const playroomScope = usePlayroomScope(); const snippet = formatSnippet( - typeof children === 'string' - ? children - : reactElementToJSXString(children, { - useBooleanShorthandSyntax: false, - showDefaultProps: false, - showFunctions: false, - filterProps: ['onChange', 'onBlur', 'onFocus'], - }), + displayCode ?? + (typeof children === 'string' + ? children + : reactElementToJSXString( + typeof children === 'function' ? children(playroomScope) : children, + { + useBooleanShorthandSyntax: false, + showDefaultProps: false, + showFunctions: false, + filterProps: ['onChange', 'onBlur', 'onFocus'], + }, + )), ); return ( @@ -189,7 +199,11 @@ export default ({ > {typeof children !== 'string' && ( - {children} + + {typeof children === 'function' + ? children(playroomScope) + : children} + )} {hideCode ? null : {snippet}} diff --git a/site/src/App/Navigation/Navigation.tsx b/site/src/App/Navigation/Navigation.tsx index 433f973ed2..3bf2c62d9f 100644 --- a/site/src/App/Navigation/Navigation.tsx +++ b/site/src/App/Navigation/Navigation.tsx @@ -122,8 +122,9 @@ export const Navigation = ({ children }: NavigationProps) => { ( + + + + Playroom Prototyping + + + This guide aims to teach you how to make your Playroom designs + interactive, and assumes you already have a basic level of familiarity + with Playroom. + + + + + Introducing state + + Braid components, when used in Playroom, manage their own state internally + by default. For example, if you use a{' '} + Checkbox component, + you’re able to toggle the checkbox on and off. + + + + + + This might seem like an odd thing to point out—after all, how else is a + checkbox supposed to work? Well, typically in React we manage the state + from outside the component. + + + As a basic example, if we wanted our checkbox to be checked, we can set + its checked prop to true: + + + + + + Equally, we can set its checked prop to{' '} + false: + + + + + + Note that we are now unable to change the state of the checkbox by + clicking on it. Go ahead—try clicking the examples now. You’ll see that it + no longer has any effect. This is obviously a bit useless, so let’s fix + that. + + Getting and setting state + + Within Braid’s Playroom, we’re provided with setState and{' '} + getState functions that allow us to manage the overall + state of our prototype. If we wire them up to the checkbox, it looks + something like this: + + + `} + > + {({ getState, setState }) => ( + + )} + + + Here you can see that we’re binding the state of checkbox to a piece of + state called checked. Note that we could have called this + piece of state whatever we want, so you’d typically give it a name more + appropriate to your design, e.g.{' '} + {"getState('showMoreInfo')"}. + + + Okay, so we’re seemingly back where we started with an interactive + checkbox. Why is this useful? Well, by managing the state externally,{' '} + our checkbox is now able to affect other parts of the UI. + + + As a minimal example, let’s make the checkbox toggle the visibility of + another element: + + + + + {getState('checked') && ( + + Good job! You checked the checkbox! + + )} + + `} + > + {({ getState, setState }) => ( + + + + {getState('checked') && ( + + Good job! You checked the checkbox! + + )} + + )} + + + We could also use the checkbox state to toggle the visibility of two + alternate elements: + + + + + {getState('checked') ? ( + + Good job! You checked the checkbox! + + ) : ( + + Oops! You haven’t checked the checkbox! + + )} + + `} + > + {({ getState, setState }) => ( + + + + {getState('checked') ? ( + + Good job! You checked the checkbox! + + ) : ( + + Oops! You haven’t checked the checkbox! + + )} + + )} + + + We can bind to more complicated state too, like text values within{' '} + TextField components: + + + + + + + + {getState('firstName') && getState('lastName') ? ( + + 👋 Hello {getState('firstName')} {getState('lastName')}! + + ) : null} + + + `} + > + {({ getState, setState }) => ( + + + + + + + {getState('firstName') && getState('lastName') ? ( + + 👋 Hello {getState('firstName')} {getState('lastName')}! + + ) : null} + + + )} + + + + It’s not just about form elements either. For example, we might want to + provide a Button that, via + an onClick handler, toggles the open{' '} + state of a Drawer: + + + + + + + setState('drawer', false)} + > + + + + `} + > + {({ getState, setState }) => ( + + + + + + setState('drawer', false)} + > + + + + )} + + + Navigating between screens + + We can also leverage state to simulate having multiple screens by using a + piece of state called screen. + + + In this example we’re also making use of the resetState{' '} + function to go back to the original screen. + + + + {!getState('screen') && ( + + + Home + + + + + + )} + + {getState('screen') === 'Welcome' && ( + + + 👋 Welcome! + + + + + + + )} + + `} + > + {({ getState, setState, resetState }) => ( + + {!getState('screen') && ( + + + Home + + + + + + )} + + {getState('screen') === 'Welcome' && ( + + + 👋 Welcome! + + + + + + + )} + + )} + + + What’s next? + + If you’ve come this far, it’s likely that you’ll still have some + questions. Please reach out so we can give you a hand, and hopefully feed + improvements back to the site. We’re going to keep iterating on the + prototyping experience over time, so any feedback you have would be + greatly appreciated! + + +); + +const page: Page = { + title: 'Playroom Prototyping', + component: PlayroomPrototyping, +}; + +export default page;