You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beam.apache.org by pa...@apache.org on 2022/09/29 18:16:00 UTC
[beam] branch master updated: Send JavaScript messages to Playground iframes when switching the language in docs (#22361) (#22960)
This is an automated email from the ASF dual-hosted git repository.
pabloem pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push:
new 577953c5ac9 Send JavaScript messages to Playground iframes when switching the language in docs (#22361) (#22960)
577953c5ac9 is described below
commit 577953c5ac9dd3d022b664c21da846307ac45191
Author: alexeyinkin <al...@akvelon.com>
AuthorDate: Thu Sep 29 22:15:52 2022 +0400
Send JavaScript messages to Playground iframes when switching the language in docs (#22361) (#22960)
* Send JavaScript messages to Playground iframes when switching the language in docs (#22361)
* Change to the production URL (#22361)
* Fix after internal review (#22361)
* Trigger a stand build (#22361)
* Trigger build (#22361)
* Remove !important from CSS (#22361)
---
website/www/site/assets/js/language-switch-v2.js | 268 ++++++++++++++-------
.../www/site/assets/scss/_capability-matrix.scss | 4 +-
website/www/site/assets/scss/_global.sass | 6 +-
website/www/site/assets/scss/_playground.sass | 8 +
.../www/site/assets/scss/_syntax-highlighting.scss | 7 -
.../site/content/en/documentation/runners/flink.md | 6 +-
.../site/content/en/documentation/runners/jet.md | 4 +-
.../site/content/en/documentation/runners/spark.md | 4 +-
.../content/en/documentation/runners/twister2.md | 2 +-
.../site/layouts/shortcodes/language-switcher.html | 8 +-
.../www/site/layouts/shortcodes/playground.html | 49 ++++
.../layouts/shortcodes/playground_snippet.html | 12 +
12 files changed, 268 insertions(+), 110 deletions(-)
diff --git a/website/www/site/assets/js/language-switch-v2.js b/website/www/site/assets/js/language-switch-v2.js
index c251aaac07e..2a559fb57ec 100644
--- a/website/www/site/assets/js/language-switch-v2.js
+++ b/website/www/site/assets/js/language-switch-v2.js
@@ -11,8 +11,8 @@
// the License.
$(document).ready(function() {
- function getElementData(element) {
- const clickedLangSwitchEl = element.target;
+ function getElementData(event) {
+ const clickedLangSwitchEl = event.target;
const elPreviousOffsetFromViewPort = clickedLangSwitchEl.getBoundingClientRect().top;
return {
elPreviousOffsetFromViewPort,
@@ -26,17 +26,17 @@ $(document).ready(function() {
$('html, body').scrollTop(elCurrentHeight - elPreviousOffsetFromViewPort);
}
+ const loadTime = new Date();
+
function Switcher(conf) {
- var id = conf["class-prefix"],
- def = conf["default"],
- langs = [];
- var prefix = id + "-";
+ const name = conf["name"];
+
return {
- "id": id,
- "selector": "[class^=" + prefix + "]:not(.no-toggle)",
- "wrapper": prefix + "switcher", // Parent wrapper-class.
- "default": prefix + def, // Default type to display.
- "dbKey": id, // Local Storage Key
+ ...conf,
+ "uniqueValues": new Set(),
+ "selector": `[class^=${name}-]:not(.no-toggle)`, // Ex: [class^=language-]:not(.no-toggle)
+ "wrapper": `${name}-switcher`, // Parent wrapper-class.
+ "localStorageKey": name,
/**
* @desc Generate bootstrapped like nav template,
@@ -44,44 +44,52 @@ $(document).ready(function() {
* @param array $types - list of supported types.
* @return string - html template, which is bootstrapped nav tabs.
*/
- "navHtml": function(types) {
- var lists = "";
- var selectors = "";
-
- types.forEach(function(type) {
- var name = type.replace(prefix, "");
- name = (name === "py")? "python": name;
- name = name.charAt(0).toUpperCase() + name.slice(1);
- selectors += " " + type;
- lists += "<li class=\"langSwitch-content\" data-type=\"" + type + "\">";
- lists += name + "</li>";
- });
- return "<div class=\"" + this.wrapper + selectors + "\"> \
- <ul class=\"nav nav-tabs\">" + lists + "</ul> </div>";
+ "navHtml": function (values) {
+ let lists = "";
+ let classes = "";
+
+ for (const value of values) {
+ const title = this.valueToTabTitle(value);
+ classes += ` ${name}-${value}`;
+ lists += `<li class="langSwitch-content" data-value="${value}">${title}</li>`;
+ }
+
+ // Ex: language-switcher language-java language-py language-go
+ return `<div class="${this.wrapper + classes}"><ul class="nav nav-tabs">${lists}</ul></div>`;
},
- /**
- * @desc Extract language from provided text.
- * @param string $text - string containing language, e.g language-python.
- * @return string - cleaned name of language, e.g python.
- */
- "parseName": function(str) {
- var re = new RegExp(prefix + "(\\w+)");
- var parse = re.exec(str);
- return (parse) ? parse[1] : "";
+
+ "valueToTabTitle": function (value) {
+ switch (value) {
+ case 'py': return 'Python';
+ case 'typescript': return 'TypeScript';
+ }
+
+ return value.charAt(0).toUpperCase() + value.slice(1);
},
+
/**
* @desc Add Navigation tabs on top of parent code blocks.
*/
"addTabs": function() {
var _self = this;
- $("div"+_self.selector).each(function() {
- if ($(this).prev().is("div"+_self.selector)) {
- return;
+ // Iterate over all content blocks to insert tabs before.
+ // If multiple content blocks go in a row, only insert tabs before the first one.
+ $("div" + _self.selector).each(function() { // Ex: div[class^=language-]:not(.no-toggle)
+ if ($(this).prev().is("div" + _self.selector)) {
+ return; // Not the first one.
}
- $(this).before(_self.navHtml(_self.lookup($(this), [])));
+
+ const values = _self.findNextSiblingsValues($(this));
+ for (const value of values) {
+ _self.uniqueValues.add(value);
+ }
+
+ const tabsHtml = _self.navHtml(values);
+ $(this).before(tabsHtml);
});
},
+
/**
* @desc Search next sibling and if it's also a code block, then store
its type and move on to the next element. It will keep
@@ -89,81 +97,167 @@ $(document).ready(function() {
* @param object $el - jQuery object, from where to start searching.
* @param array $lang - list to hold types, found while searching.
* @return array - list of types found.
- */
- "lookup": function(el, lang) {
- if (!el.is("div"+this.selector)) {
- langs = lang;
- return lang;
+ */
+ "findNextSiblingsValues": function(el, lang) {
+ if (!el.is("div" + this.selector)) {
+ return [];
+ }
+
+ const prefix = `${name}-`;
+ for (const cls of el[0].classList) {
+ if (cls.startsWith(prefix)) {
+ return [
+ cls.replace(prefix, ""),
+ ...this.findNextSiblingsValues(el.next()),
+ ];
+ }
}
- lang.push(el.attr("class").split(" ")[0])
- return this.lookup(el.next(), lang)
+ return [];
},
+
"bindEvents": function() {
var _self = this;
- $("." + _self.wrapper + " ul li").click(function(el) {
- // Making type preferences presistance, for user.
- localStorage.setItem(_self.dbKey, $(this).data("type"));
+ $(`.${_self.wrapper} li`).click(function(event) {
+ localStorage.setItem(_self.localStorageKey, $(this).data("value"));
// Set scroll to new position because Safari and Firefox
// can't do it automatically, only Chrome under the hood
// detects the correct position of viewport
- const clickedLangSwitchData = getElementData(el);
- _self.toggle();
+ const clickedLangSwitchData = getElementData(event);
+ _self.toggle(false);
setScrollToNewPosition(clickedLangSwitchData);
});
},
- "toggle": function() {
- var pref=localStorage.getItem(this.dbKey) || this.default;
- var isPrefSelected = false;
-
- // Adjusting active elements in navigation header.
- $("." + this.wrapper + " li").removeClass("active").each(function() {
- if ($(this).data("type") === pref) {
- $(this).addClass("active");
- isPrefSelected = true;
+
+ "toggle": function(isInitial) {
+ let value = localStorage.getItem(this.localStorageKey) || this.default;
+ let hasTabForValue = $(`.${this.wrapper} li[data-value="${value}"]`).length > 0; // Ex: .language-switcher li[data-value="java"]
+
+ if (!hasTabForValue) {
+ // if there's a code block for the default language,
+ // set the preferred language to the default language
+ if (this.uniqueValues.has(this.default)) {
+ value = this.default;
+ } else {
+ // otherwise set the preferred language to the first available
+ // language, so we don't have a page with no code blocks
+ value = [...this.uniqueValues][0];
}
- });
+ }
+
+ $(`.${this.wrapper} li[data-value="${value}"]`).addClass("active");
+ $(`.${this.wrapper} li[data-value!="${value}"]`).removeClass("active");
- if (!isPrefSelected) {
- // if there's a code block for the default language,
- // set the preferred language to the default language
- if (langs.includes(this.default)) {
- pref = this.default;
- // otherwise set the preferred language to the first available
- // language, so we don't have a page with no code blocks
- } else {
- pref = langs[0];
- }
-
- $("." + this.wrapper + " li").each(function() {
- if ($(this).data("type") === pref) {
- $(this).addClass("active");
- }
- });
- }
// Swapping visibility of code blocks.
- $(this.selector).hide();
- $("nav"+this.selector).show();
+ $(this.selector).hide(); // Ex: [class^=language-]:not(.no-toggle)
+ $("nav" + this.selector).show();
// make sure that runner and shell snippets are still visible after changing language
- $("code"+this.selector).show();
- $("." + pref).show();
+ $("code" + this.selector).show();
+ $(`.${name}-${value}`).show();
//add refresh method because html elements are added/deleted after changing language
$('[data-spy="scroll"]').each(function () {
$(this).scrollspy('refresh');
});
+
+ if (this.onChanged) {
+ this.onChanged(value, isInitial);
+ }
},
"render": function(wrapper) {
this.addTabs();
this.bindEvents();
- this.toggle();
- }
+ this.toggle(true);
+ },
};
}
- Switcher({"class-prefix":"language","default":"java"}).render();
- Switcher({"class-prefix":"runner","default":"direct"}).render();
- Switcher({"class-prefix":"shell","default":"unix"}).render();
- Switcher({"class-prefix":"version"}).render();
+ Switcher({
+ "name": "language",
+ "default": "java",
+
+ "onChanged": function (lang, isInitial) {
+ if (isInitial) {
+ this.onInit(lang);
+ } else {
+ this.onChangedAfterLoaded(lang);
+ }
+ },
+
+ /**
+ * @desc Called after language is determined for the first time.
+ * Modifies the iframes' URLs by adding the language so we don't need to
+ * send messages to them. This is cheap when the page only started loading.
+ */
+ "onInit": function (lang) {
+ const playgroundIframes = $(".code-snippet-playground iframe").get();
+ const sdk = this.langToSdk(lang);
+
+ for (const iframe of playgroundIframes) {
+ const url = new URL(iframe.src);
+ const searchParams = new URLSearchParams(url.search);
+ searchParams.set("sdk", sdk);
+ url.search = searchParams.toString();
+ iframe.src = url.href;
+ }
+ },
+
+ /**
+ * @desc Called when the user switched the language tab manually.
+ */
+ "onChangedAfterLoaded": function (lang) {
+ const playgroundIframes = $(".code-snippet-playground iframe").get();
+ const message = {
+ type: "SetSdk",
+ sdk: this.langToSdk(lang),
+ };
+
+ const _self = this;
+ let attempts = 30;
+
+ // If another cycle of sending these messages is running, stop it.
+ clearInterval(this.interval);
+
+ const sendMessage = function () {
+ for (const iframe of playgroundIframes) {
+ iframe.contentWindow.postMessage(message, '*');
+ }
+
+ if (attempts-- === 0) {
+ clearInterval(_self.interval);
+ }
+ };
+
+ if (!this.areFramesLoaded()) {
+ // The guess is that the iframes may not have loaded yet.
+ // If we just send a message, Flutter may have not yet set its listener.
+ // So send the message with intervals in hope some of them are received.
+ // Playground ignores duplicate messages in a row.
+ this.interval = setInterval(sendMessage, 1000);
+ }
+
+ sendMessage();
+ },
+
+ "langToSdk": function (lang) {
+ switch (lang) {
+ case "py": return "python";
+ }
+ return lang;
+ },
+
+ /**
+ * @desc A rough guess if the embedded iframes are loaded, timer-based.
+ * Experiments hint that ~10 seconds is sufficient on slowest devices.
+ */
+ "areFramesLoaded": function () {
+ const millisecondsAgo = new Date() - loadTime;
+ return millisecondsAgo >= 30000;
+ },
+ }).render();
+
+ Switcher({"name": "runner", "default": "direct"}).render();
+ Switcher({"name": "shell", "default": "unix"}).render();
+ Switcher({"name": "version"}).render();
});
diff --git a/website/www/site/assets/scss/_capability-matrix.scss b/website/www/site/assets/scss/_capability-matrix.scss
index 4f0d16b6114..a3b59a84441 100644
--- a/website/www/site/assets/scss/_capability-matrix.scss
+++ b/website/www/site/assets/scss/_capability-matrix.scss
@@ -304,7 +304,9 @@ nav.runner-switcher {
border-top-left-radius: 6px;
border-top-right-radius: 6px;
border-bottom: none;
- padding-left: 0 !important;
+ &:not(:last-child) {
+ border-right: none;
+ }
&:hover {
cursor: pointer
}
diff --git a/website/www/site/assets/scss/_global.sass b/website/www/site/assets/scss/_global.sass
index a8b20d87946..100965a8935 100644
--- a/website/www/site/assets/scss/_global.sass
+++ b/website/www/site/assets/scss/_global.sass
@@ -109,7 +109,7 @@ body
display: block
.code-snippet, pre
- background: rgba(255, 109, 0, 0.03) !important
+ background: rgba(255, 109, 0, 0.03)
border-radius: 8px
border-top-left-radius: 0
border: solid 0.6px #ff6d05
@@ -126,7 +126,7 @@ body
border: none
padding: 0
margin-top: 36px
- background: initial !important
+ background: initial
a
float: right
margin-left: 12px
@@ -152,7 +152,7 @@ body
.without_switcher
border-top-left-radius: 8px
pre
- background: initial !important
+ background: initial
table
margin-top: 24px
diff --git a/website/www/site/assets/scss/_playground.sass b/website/www/site/assets/scss/_playground.sass
index c4a978949a1..0f1b507fc68 100644
--- a/website/www/site/assets/scss/_playground.sass
+++ b/website/www/site/assets/scss/_playground.sass
@@ -17,6 +17,14 @@
@import "media"
+.code-snippet-playground
+ padding: 0
+ background: none
+
+ iframe
+ border: none
+ border-top-right-radius: 8px
+
.playground-section
padding: 15px 30px 15px
h1
diff --git a/website/www/site/assets/scss/_syntax-highlighting.scss b/website/www/site/assets/scss/_syntax-highlighting.scss
index 42c6301a401..8a347d3f74f 100644
--- a/website/www/site/assets/scss/_syntax-highlighting.scss
+++ b/website/www/site/assets/scss/_syntax-highlighting.scss
@@ -16,12 +16,6 @@
* Syntax highlighting styles
*/
.highlight {
- //background: #fff;
-
- .chroma {
- background: #eef;
- }
-
.c {
color: #998;
font-style: italic;
@@ -239,7 +233,6 @@ pre {
ul {
li {
margin-bottom:0 !important;
- padding-left: 0 !important;
&:hover {
cursor: pointer;
}
diff --git a/website/www/site/content/en/documentation/runners/flink.md b/website/www/site/content/en/documentation/runners/flink.md
index b79fadc7a85..4188dc9e9ab 100644
--- a/website/www/site/content/en/documentation/runners/flink.md
+++ b/website/www/site/content/en/documentation/runners/flink.md
@@ -66,9 +66,9 @@ Please use the switcher below to select the appropriate mode for the Runner:
<nav class="language-switcher">
<strong>Adapt for:</strong>
<ul>
- <li data-type="language-java">Classic (Java)</li>
- <li data-type="language-py">Portable (Python)</li>
- <li data-type="language-portable">Portable (Java/Python/Go)</li>
+ <li data-value="java">Classic (Java)</li>
+ <li data-value="py">Portable (Python)</li>
+ <li data-value="portable">Portable (Java/Python/Go)</li>
</ul>
</nav>
diff --git a/website/www/site/content/en/documentation/runners/jet.md b/website/www/site/content/en/documentation/runners/jet.md
index 9f55771251d..3b110ad86e6 100644
--- a/website/www/site/content/en/documentation/runners/jet.md
+++ b/website/www/site/content/en/documentation/runners/jet.md
@@ -93,8 +93,8 @@ Download latest Hazelcast Jet version compatible with the Beam you are using fro
<nav class="version-switcher">
<strong>Adapt for:</strong>
<ul>
- <li data-type="version-jet3">Hazelcast Jet 3.x</li>
- <li data-type="version-jet4">Hazelcast Jet 4.x</li>
+ <li data-value="jet3">Hazelcast Jet 3.x</li>
+ <li data-value="jet4">Hazelcast Jet 4.x</li>
</ul>
</nav>
diff --git a/website/www/site/content/en/documentation/runners/spark.md b/website/www/site/content/en/documentation/runners/spark.md
index db4b505d2d6..ff6fa3cc47a 100644
--- a/website/www/site/content/en/documentation/runners/spark.md
+++ b/website/www/site/content/en/documentation/runners/spark.md
@@ -59,8 +59,8 @@ the portable Runner. For more information on portability, please visit the
<nav class="language-switcher">
<strong>Adapt for:</strong>
<ul>
- <li data-type="language-java">Non portable (Java)</li>
- <li data-type="language-py">Portable (Java/Python/Go)</li>
+ <li data-value="java">Non portable (Java)</li>
+ <li data-value="py">Portable (Java/Python/Go)</li>
</ul>
</nav>
diff --git a/website/www/site/content/en/documentation/runners/twister2.md b/website/www/site/content/en/documentation/runners/twister2.md
index e63762f2dfa..ba15c010fa7 100644
--- a/website/www/site/content/en/documentation/runners/twister2.md
+++ b/website/www/site/content/en/documentation/runners/twister2.md
@@ -88,7 +88,7 @@ deployments and how to get them setup visit [Twister2 Docs](https://twister2.org
<nav class="version-switcher">
<strong>Adapt for:</strong>
<ul>
- <li data-type="version-twister2-0.6.0">Twister2 0.6.0</li>
+ <li data-value="twister2-0.6.0">Twister2 0.6.0</li>
</ul>
</nav>
diff --git a/website/www/site/layouts/shortcodes/language-switcher.html b/website/www/site/layouts/shortcodes/language-switcher.html
index a6bfe115fef..855ee64972f 100644
--- a/website/www/site/layouts/shortcodes/language-switcher.html
+++ b/website/www/site/layouts/shortcodes/language-switcher.html
@@ -15,16 +15,16 @@
<ul>
{{ range $lang := .Params }}
{{ if eq $lang "java" }}
- <li data-type="language-java" class="active">Java SDK</li>
+ <li data-value="java" class="active">Java SDK</li>
{{ end }}
{{ if eq $lang "py" }}
- <li data-type="language-py">Python SDK</li>
+ <li data-value="py">Python SDK</li>
{{ end }}
{{ if eq $lang "go" }}
- <li data-type="language-go">Go SDK</li>
+ <li data-value="go">Go SDK</li>
{{ end }}
{{ if eq $lang "typescript" }}
- <li data-type="language-typescript">Typescript SDK</li>
+ <li data-value="typescript">TypeScript SDK</li>
{{ end }}
{{ end }}
</ul>
diff --git a/website/www/site/layouts/shortcodes/playground.html b/website/www/site/layouts/shortcodes/playground.html
new file mode 100644
index 00000000000..4dc3eaf09e7
--- /dev/null
+++ b/website/www/site/layouts/shortcodes/playground.html
@@ -0,0 +1,49 @@
+{{/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License. See accompanying LICENSE file.
+*/}}
+{{/*
+Embedding example:
+
+{{< playground height="700px" >}}
+{{< playground_snippet language="java" example="SDK_JAVA/PRECOMPILED_OBJECT_TYPE_KATA/AggregationCount" >}}
+{{< playground_snippet language="py" example="SDK_PYTHON/PRECOMPILED_OBJECT_TYPE_KATA/AggregationCount" >}}
+{{< playground_snippet language="go" example="SDK_GO/PRECOMPILED_OBJECT_TYPE_EXAMPLE/MinimalWordCount" >}}
+{{< playground_snippet language="scio" example="SDK_SCIO/PRECOMPILED_OBJECT_TYPE_EXAMPLE/MinimalWordCount" >}}
+{{< /playground >}}
+
+*/}}
+<div class="playground-wrapper">
+ <div class="playground-snippets">
+ {{ .Inner }}
+ </div>
+ {{ $snippetsList := slice }}
+ {{ $divMatches := findRE "<div class=\"[^\"]+(playground-snippet)\"(.*)</div>" .Inner }}
+
+ {{ range $divMatches }}
+ {{ $attributeRegex := "data-sdk=\"(?P<sdk>\\w+)\" data-example=\"([^\"]+)\"" }}
+ {{ $sdk := replaceRE ".*data-sdk=\"(\\w+)\".*" "$1" . }}
+ {{ $example := replaceRE ".*example=\"([^\"]+)\".*" "$1" . }}
+ {{ $json := printf "%s%s%s%s%s" "{\"sdk\":\"" $sdk "\",\"example\":\"" $example "\"}" }}
+ {{ $snippetsList = append $json $snippetsList }}
+ {{ end}}
+
+ {{ $snippets := printf "%s%s%s" "[" (delimit $snippetsList ",") "]" }}
+ {{ $editable := 1 }}{{ if isset .Params "editable" }}{{ $editable = index .Params "editable" }}{{ end }}
+ <div class="code-snippet code-snippet-playground">
+ <iframe
+ src="https://play.beam.apache.org/embedded?editable={{ $editable }}&examples={{ $snippets }}"
+ width="100%"
+ height="{{ .Get "height" }}"
+ class="playground"
+ allow="clipboard-write"
+ ></iframe>
+ </div>
+</div>
diff --git a/website/www/site/layouts/shortcodes/playground_snippet.html b/website/www/site/layouts/shortcodes/playground_snippet.html
new file mode 100644
index 00000000000..4c4ce501168
--- /dev/null
+++ b/website/www/site/layouts/shortcodes/playground_snippet.html
@@ -0,0 +1,12 @@
+{{/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License. See accompanying LICENSE file.
+*/}}
+{{ $sdk := .Get "language" }}{{ if eq $sdk "py" }}{{ $sdk = "python" }}{{ end }}<div class="language-{{ .Get "language" }} playground-snippet" data-sdk="{{ $sdk }}" data-example="{{ .Get "example" }}"></div>