Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 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
13 changes: 11 additions & 2 deletions src/Layout.re
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,18 @@ module Sidebar = {
};

[@react.component]
let make = (~innerContainerId=?, ~fullHeight=false, ~children=React.null) =>
let make =
(
~domRef=?,
~innerContainerId=?,
~fullHeight=false,
~children=React.null,
) =>
<div
name="Sidebar" id=?innerContainerId style={Styles.sidebar(~fullHeight)}>
name="Sidebar"
ref=?domRef
id=?innerContainerId
style={Styles.sidebar(~fullHeight)}>
children
</div>;
};
Expand Down
199 changes: 81 additions & 118 deletions src/ReshowcaseUi.re
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ module TopPanel = {
</div>;
};

let rightSidebarId = "rightSidebar";
// let rightSidebarId = "rightSidebar";

module Link = {
[@react.component]
Expand Down Expand Up @@ -611,9 +611,9 @@ module DemoUnitSidebar = {
<PropBox key=propName propName>
<input
type_="number"
min=string_of_int(min)
max=string_of_int(max)
value=string_of_int(value)
min={string_of_int(min)}
max={string_of_int(max)}
value={string_of_int(value)}
style=Styles.textInput
onChange={event =>
onIntChange(
Expand All @@ -633,9 +633,9 @@ module DemoUnitSidebar = {
<PropBox key=propName propName>
<input
type_="number"
min=string_of_float(min)
max=string_of_float(max)
value=string_of_float(value)
min={string_of_float(min)}
max={string_of_float(max)}
value={string_of_float(value)}
style=Styles.textInput
onChange={event =>
onFloatChange(
Expand Down Expand Up @@ -712,38 +712,9 @@ module DemoUnit = {
->(ReactDOM.Style.unsafeAddProp("WebkitOverflowScrolling", "touch"));
};

let getRightSidebarElement = (): option(Dom.element) =>
Window.window##parent##document##getElementById(rightSidebarId)
->Js.Nullable.toOption;

[@react.component]
let make = (~demoUnit: Configs.demoUnitProps => React.element) => {
let (parentWindowRightSidebarElem, setParentWindowRightSidebarElem) =
React.useState(() => None);

React.useEffect0(() => {
switch (getRightSidebarElement()) {
| Some(elem) => setParentWindowRightSidebarElem(_ => Some(elem))
| None => ()
};
None;
});
React.useEffect0(() => {
Window.addMessageListener(event =>
if (Window.window##parent === event##source) {
let message: string = event##data;
switch (message->Window.Message.fromStringOpt) {
| Some(RightSidebarDisplayed) =>
switch (getRightSidebarElement()) {
| Some(elem) => setParentWindowRightSidebarElem(_ => Some(elem))
| None => ()
}
| None => Js.Console.error("Unexpected message received")
};
}
);
None;
});
let make =
(~demoUnit: Configs.demoUnitProps => React.element, ~sidebarElem=?) => {
let (state, dispatch) =
React.useReducer(
(state, action) =>
Expand Down Expand Up @@ -867,7 +838,7 @@ module DemoUnit = {

<div name="DemoUnit" style=Styles.container>
<div style=Styles.contents> {demoUnit(props)} </div>
{switch (parentWindowRightSidebarElem) {
{switch (sidebarElem) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I also refactored the sidebar to use reference instead of getting elements by id, which seems much more fragile and caused runtime exceptions for me.

| None => React.null
| Some(element) =>
ReactDOM.createPortal(
Expand Down Expand Up @@ -909,22 +880,12 @@ module DemoUnitFrame = {
(),
);

let useFullframeUrl: bool = [%mel.raw
{js|typeof USE_FULL_IFRAME_URL === "boolean" ? USE_FULL_IFRAME_URL : false|js}
];
Comment on lines -912 to -914

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this feature removed?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

my bad. I was not sure what it was doing and how to debug it. Will check


[@react.component]
let make =
(~queryString: string, ~responsiveMode, ~onLoad: Js.t('a) => unit) => {
let iframePath = if (useFullframeUrl) {"demo/index.html"} else {"demo"};
let make = (~responsiveMode, ~onLoad: Js.t('a) => unit, ~children) => {
let (body, setBody) = React.useState(_ => None);

<div name="DemoUnitFrame" style={container(responsiveMode)}>
<iframe
onLoad={event => {
let iframe = event->React.Event.Synthetic.target;
let window = iframe##contentWindow;
onLoad(window);
}}
src={(iframePath ++ {js|?iframe=true&|js}) ++ queryString}
style={ReactDOM.Style.make(
~height=
switch (responsiveMode) {
Expand All @@ -939,7 +900,18 @@ module DemoUnitFrame = {
~border="none",
(),
)}
/>
onLoad={event => {
let iframe = event->React.Event.Synthetic.target;
let body =
iframe##contentWindow##document->Option.flatMap(d => d##body);
setBody(_ => body);
onLoad(iframe##contentWindow);
}}>
{switch (body) {
| None => React.null
| Some(body) => ReactDOM.createPortal(children, body)
}}
</iframe>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is the core change. Instead of referencing iframe with demo, we just render demo using portal in persistent iframe

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Have you thought about how this change might affect stateful changes that the iframe keeps from one demo to another? E.g. if some iframe loads some styles in the page head then they might "leak" from one demo to the next.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

No, I didn't. I assumed this is the responsibility of the consumer: do a cleanup on unmount.

</div>;
};
};
Expand Down Expand Up @@ -1017,29 +989,20 @@ module App = {
let make = (~demos: Demos.t) => {
let url = ReasonReactRouter.useUrl();
let urlSearchParams = url.search->URLSearchParams.make;
let (sidebarElem, setSidebarElem) = React.useState(_ => None);
let route =
switch (
urlSearchParams->(URLSearchParams.get("iframe")),
urlSearchParams->(URLSearchParams.get("demo")),
) {
| (Some("true"), Some(demoName)) => Unit(urlSearchParams, demoName)
| (_, Some(_)) => Demo(url.search)
| (_, Some(demoName)) => Demo(demoName)
| _ => Home
};

let (loadedIframeWindow: option(Js.t('a)), setLoadedIframeWindow) =
React.useState(() => None);

let (iframeKey, setIframeKey) =
React.useState(() => Js.Date.now()->Float.toString);

React.useEffect1(
() => {
setIframeKey(_ => Js.Date.now()->Float.toString);
None;
},
[|url|],
);
let (showRightSidebar, toggleShowRightSidebar) =
React.useState(() =>
LocalStorage.localStorage
Expand Down Expand Up @@ -1089,6 +1052,17 @@ module App = {
};

<div name="App" style=Styles.app>
{switch (route) {
| Unit(_, _) => React.null
| Demo(_)
| Home =>
<DemoListSidebar
demos
urlSearchParams
isCategoriesCollapsedByDefault
onToggleCollapsedCategoriesByDefault
/>
}}
{switch (route) {
| Unit(urlSearchParams, demoName) =>
let demoUnit = Demos.findDemo(urlSearchParams, demoName, demos);
Expand All @@ -1097,64 +1071,53 @@ module App = {
->(Option.map(demoUnit => <DemoUnit demoUnit />))
->(Option.getWithDefault("Demo not found"->React.string))}
</div>;
| Demo(queryString) =>
<>
<DemoListSidebar
demos
urlSearchParams
isCategoriesCollapsedByDefault
onToggleCollapsedCategoriesByDefault
| Demo(demoName) =>
let demoUnit =
Demos.findDemo(urlSearchParams, demoName, demos)
->(
Option.map(demoUnit =>
<DemoUnit demoUnit key={url.search} ?sidebarElem />
)
)
->(Option.getWithDefault("Demo not found"->React.string));
<div name="Content" style=Styles.right>
<TopPanel
isSidebarHidden={!showRightSidebar}
responsiveMode
onRightSidebarToggle={() => {
toggleShowRightSidebar(_ => !showRightSidebar);
switch (loadedIframeWindow) {
| Some(window) when !showRightSidebar =>
Window.postMessage(window, RightSidebarDisplayed)
Comment on lines +1086 to +1087

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I am not sure why it's done via "signalling", but decided to keep it this way for now to limit the scope of this PR

| None
| _ => ()
};
}}
onSetResponsiveMode
/>
<div name="Content" style=Styles.right>
<TopPanel
isSidebarHidden={!showRightSidebar}
responsiveMode
onRightSidebarToggle={() => {
toggleShowRightSidebar(_ => !showRightSidebar);
switch (loadedIframeWindow) {
| Some(window) when !showRightSidebar =>
Window.postMessage(window, RightSidebarDisplayed)
| None
| _ => ()
};
}}
onSetResponsiveMode
/>
<div name="Demo" style=Styles.demo>
<div style=Styles.demoContents>
<DemoUnitFrame
key={"DemoUnitFrame" ++ iframeKey}
queryString
responsiveMode
onLoad={iframeWindow =>
setLoadedIframeWindow(_ => Some(iframeWindow))
}
/>
</div>
{if (showRightSidebar) {
<Sidebar
key={"Sidebar" ++ iframeKey}
innerContainerId=rightSidebarId
/>;
} else {
React.null;
}}
<div name="Demo" style=Styles.demo>
<div style=Styles.demoContents>
<DemoUnitFrame
responsiveMode
onLoad={iframeWindow => {
setLoadedIframeWindow(_ => Some(iframeWindow))
}}>
demoUnit
</DemoUnitFrame>
</div>
{showRightSidebar
? <Sidebar
domRef={ReactDOM.Ref.callbackDomRef(node =>
setSidebarElem(_ => node->Js.Nullable.toOption)
)}
/>
: React.null}
</div>
</>

</div>;
| Home =>
<>
<DemoListSidebar
demos
urlSearchParams
isCategoriesCollapsedByDefault
onToggleCollapsedCategoriesByDefault
/>
<div style=Styles.empty>
<div style=Styles.emptyText> "Pick a demo"->React.string </div>
</div>
</>
<div style=Styles.empty>
<div style=Styles.emptyText> "Pick a demo"->React.string </div>
</div>
}}
</div>;
};
Expand Down