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

Transfer search parameters from page URL to jupyterlite #108

Merged
merged 7 commits into from
Sep 11, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/directives/jupyterlite.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,8 @@ You can also pass a Notebook file to open automatically:
:prompt: Try JupyterLite!
:prompt_color: #00aa42
```

The directive `search_params` allows to transfer some search parameters from the documentation URL to the Jupyterlite URL.\
Jupyterlite will then be able to fetch these parameters from its own URL.\
For example `:search_params: ["param1", "param2"]` will transfer the parameters *param1* and *param2*.
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to provide a practical example that really applies to JupyterLite? Like "theme" for replite?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why not, but I don't really know how it can be displayed to user.

The simplest solution I can find is to use something like

%%javascript
alert(window.location.search);

in a notebook, formatting a bit better the attributes.

Otherwise I didn't find a way to share value between javascript and python in a notebook, using xeus kernel.
It would be better to display the values in an output rather than using an alert.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, let's merge it as is for now!

Use a boolean value to transfer all or none of the parameters (default to none): `:search_params: True`
24 changes: 24 additions & 0 deletions jupyterlite_sphinx/jupyterlite_sphinx.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,27 @@ window.jupyterliteShowIframe = (tryItButtonId, iframeSrc) => {
tryItButton.classList.remove('jupyterlite_sphinx_try_it_button_unclicked');
tryItButton.classList.add('jupyterlite_sphinx_try_it_button_clicked');
}

window.jupyterliteConcatSearchParams = (iframeSrc, params) => {
const baseURL = window.location.origin;
const iframeUrl = new URL(iframeSrc, baseURL);

let pageParams = new URLSearchParams(window.location.search);

if (params === true) {
params = Array.from(pageParams.keys());
} else if (params === false) {
params = [];
} else if (!Array.isArray(params)) {
console.error('The search parameters are not an array');
}

params.forEach(param => {
value = pageParams.get(param);
if (value !== null) {
iframeUrl.searchParams.append(param, value);
}
});

return iframeUrl.toString().replace(baseURL, '');
}
52 changes: 45 additions & 7 deletions jupyterlite_sphinx/jupyterlite_sphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import tempfile
from warnings import warn
import glob
import re

from pathlib import Path

Expand Down Expand Up @@ -51,6 +52,7 @@ def __init__(
height="100%",
prompt=False,
prompt_color=None,
search_params=[],
**attributes,
):
super().__init__(
Expand All @@ -60,10 +62,12 @@ def __init__(
height=height,
prompt=prompt,
prompt_color=prompt_color,
search_params=search_params,
)

def html(self):
iframe_src = self["iframe_src"]
search_params = self["search_params"]

if self["prompt"]:
prompt = (
Expand All @@ -76,13 +80,24 @@ def html(self):
placeholder_id = uuid4()
container_style = f'width: {self["width"]}; height: {self["height"]};'

return (
f"<div class=\"jupyterlite_sphinx_iframe_container\" style=\"{container_style}\" onclick=window.jupyterliteShowIframe('{placeholder_id}','{iframe_src}')>"
f' <div id={placeholder_id} class="jupyterlite_sphinx_try_it_button jupyterlite_sphinx_try_it_button_unclicked" style="background-color: {prompt_color};">'
f" {prompt}"
" </div>"
"</div>"
)
return f"""
<div
class=\"jupyterlite_sphinx_iframe_container\"
style=\"{container_style}\"
onclick=\"window.jupyterliteShowIframe(
'{placeholder_id}',
window.jupyterliteConcatSearchParams('{iframe_src}', {search_params})
)\"
>
<div
id={placeholder_id}
class=\"jupyterlite_sphinx_try_it_button jupyterlite_sphinx_try_it_button_unclicked\"
style=\"background-color: {prompt_color};\"
>
{prompt}
</div>
</div>
"""

return (
f'<iframe src="{iframe_src}"'
Expand Down Expand Up @@ -196,6 +211,7 @@ class RepliteDirective(SphinxDirective):
"theme": directives.unchanged,
"prompt": directives.unchanged,
"prompt_color": directives.unchanged,
"search_params": directives.unchanged,
}

def run(self):
Expand All @@ -205,6 +221,8 @@ def run(self):
prompt = self.options.pop("prompt", False)
prompt_color = self.options.pop("prompt_color", None)

search_params = search_params_parser(self.options.pop("search_params", ""))

prefix = os.path.relpath(
os.path.join(self.env.app.srcdir, JUPYTERLITE_DIR),
os.path.dirname(self.get_source_info()[0]),
Expand All @@ -218,6 +236,7 @@ def run(self):
prompt=prompt,
prompt_color=prompt_color,
content=self.content,
search_params=search_params,
lite_options=self.options,
)
]
Expand All @@ -233,6 +252,7 @@ class _LiteDirective(SphinxDirective):
"theme": directives.unchanged,
"prompt": directives.unchanged,
"prompt_color": directives.unchanged,
"search_params": directives.unchanged,
}

def run(self):
Expand All @@ -242,6 +262,8 @@ def run(self):
prompt = self.options.pop("prompt", False)
prompt_color = self.options.pop("prompt_color", None)

search_params = search_params_parser(self.options.pop("search_params", ""))

source_location = os.path.dirname(self.get_source_info()[0])

prefix = os.path.relpath(
Expand Down Expand Up @@ -276,6 +298,7 @@ def run(self):
height=height,
prompt=prompt,
prompt_color=prompt_color,
search_params=search_params,
lite_options=self.options,
)
]
Expand Down Expand Up @@ -476,3 +499,18 @@ def setup(app):
app.add_css_file("jupyterlite_sphinx.css")

app.add_js_file("jupyterlite_sphinx.js")


def search_params_parser(search_params: str) -> str:
pattern = re.compile(r"^\[(?:\s*[\"']{1}([^=\s\,&=\?\/]+)[\"']{1}\s*\,?)+\]$")
if not search_params:
return ""
if search_params in ["True", "False"]:
return search_params.lower()
elif pattern.match(search_params):
return search_params.replace('"', "'")
else:
raise ValueError(
'The search_params directive must be either True, False or ["param1", "param2"].\n'
'The params name shouldn\'t contain any of the following characters ["\\", "\'", """, ",", "?", "=", "&", " ").'
)