Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@ exclude:
- vendor/gems/
- vendor/ruby/
- STYLE_GUIDE.md
- "**/README.md"

include: ["CNAME"]
81 changes: 81 additions & 0 deletions _layouts/code-preview.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<!-- Displays another page inside an iframe with source code on the left and the page on the right

For maintainers: Set the url to display using the iframe_content_url variable in the frontmatter.
-->

<!doctype html>

<html lang="en">
<head>
<title>{{ page.title }} - Docs - Data Commons</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<link rel="icon" href="/assets/images/favicon.png" type="image/png" />
<link
href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@300;400;500;700"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Google+Sans+Text:wght@300;400;500;700"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Public+Sans:wght@600&family=Roboto&family=Material+Icons&display=swap"
rel="stylesheet"
/>
<link href="/assets/css/styles.css" rel="stylesheet" />
<link href="/assets/css/code-preview.css" rel="stylesheet" />
{% if page.url == "/api/web_components/" or page.parent == "Web Components"
%}
<script src="https://datacommons.org/datacommons.js"></script>
{% endif %}
<!-- Syntax highlighting for code -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css"
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
</head>
<body>
<div id="main">
{% include header.html %}
<main id="{{ main_id }}" class="container-fluid">
<div id="content">
<div id="source-code">
<h3>Source Code</h3>
<pre><code id="code-target" class="language-html">Loading source code...</code></pre>
</div>
<div id="preview">
<h3>Preview</h3>
<iframe id="iframe" src="{{ page.iframe_content_url }}"></iframe>
Copy link
Contributor

Choose a reason for hiding this comment

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

security-medium medium

The iframe's src attribute is populated directly from page.iframe_content_url. If an attacker can control this variable, they can use a javascript: URI to execute arbitrary code in the context of the page. While the escape filter prevents HTML injection, it does not block javascript: URIs. Consider adding a check to ensure the URL starts with a safe prefix like / or https://. Additionally, for security hardening, it's a good practice to add the sandbox="allow-scripts" attribute to restrict actions the content within the iframe can perform, reducing the impact of potential issues.

            <iframe id="iframe" src="{{ page.iframe_content_url | escape }}" sandbox="allow-scripts"></iframe>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good bot. Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

A follow-up (and complication) from the above suggestion: the allow-javascript is locking the iframe down heavily (normally exactly what we would want) but it is also by default turning off the allow-popups option, which has a side-effect of preventing clicks on the links like the sources (because they open in a new tab).

Since we completely control the content inside the iframe, and this content only relies on DC code, we could add allow-popups to allow the links to work.

Copy link
Contributor

Choose a reason for hiding this comment

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

One last comment that I added before in here (it was hidden because it was in a resolved conversation)!

</div>
</div>
</main>
{% include footer.html %}
</div>
<script>
// Fetch the source code for the page and display it in the source code
// div.
const fileUrl = "{{ page.iframe_content_url }}";
fetch(fileUrl)
.then((response) => {
if (!response.ok) throw new Error("Network response was not ok");
return response.text();
})
.then((data) => {
// Escape the HTML so it doesn't render as actual elements
const codeTarget = document.getElementById("code-target");
codeTarget.textContent = data;
Prism.highlightElement(codeTarget);
})
.catch((err) => {
document.getElementById("code-target").textContent =
"Error loading source: " + err.message;
});
</script>
{% include scripts.html %}
</body>
</html>
31 changes: 31 additions & 0 deletions api/web_components/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Web Components Examples

This directory contains the pages that render the side-by-side source code and rendered output for the Data Commons web components examples.

## How the examples are rendered

The examples are rendered into the `code-preview` layout in `/_layouts/code-preview.html`.

A `<script>` tag in the layout pulls the the raw HTML source of the file specified in the `iframe_content_url` frontmatter property and displays it in a `<code>` block. It then applies syntax highlighting to the code via [Prism.js](https://prismjs.com/).

The right-side live-render is an iframe that loads the same file.

## How to add a new example

1. Add a new HTML file to the assets/examples/web-components/ directory containing the full HTML for the example.
2. Create a new markdown file in this directory that uses the code-preview layout.
3. Add frontmatter to the new file following this template:

```yaml
---
layout: code-preview
title: Web components example - [SOME DESCRIPTIVE NAME HERE]
published: true
nav_exclude: true # This keeps the page from showing up in the left navigation
iframe_content_url: /assets/examples/web-components/[FILENAME].html
---
```

The `iframe_content_url` should point to the HTML file you created in step 1.

4. Finally, you can link to the new example using the path `/api/web_components/examples/[FILENAME].html`
8 changes: 8 additions & 0 deletions api/web_components/examples/all-charts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
layout: code-preview
title: All Charts
parent: All Charts - Web Components Example
published: true
nav_exclude: true
iframe_content_url: /assets/examples/web-components/all-charts.html
---
111 changes: 111 additions & 0 deletions assets/css/code-preview.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
---

/**
* Copyright 2026 Google LLC
*
* 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.
*/

/**
* CSS for the code-preview layout (e.g., /api/web_components/examples/all-charts.html)
*/

html,
body {
height: 100vh;
}

#main {
display: flex;
flex-direction: column;
height: 100%;

> main {
flex-grow: 1;
display: flex;
flex-direction: column;
min-height: 0;
padding: 0;
}
}

header,
footer {
flex-shrink: 0;
}

#content {
flex-grow: 1;
margin: 0;
min-height: 0;
display: flex;
overflow: hidden;

/* On smaller screens, show source code and preview top to bottom */
@media (max-width: 1024px) {
flex-direction: column;
}
}

#preview,
#source-code {
width: 50%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;

h3 {
margin: 0;
padding: 10px;
background: #fafcff;
border-top: 1px solid #dee2e6;
border-bottom: 1px solid #dee2e6;
flex-shrink: 0;
font-size: 1rem;
font-weight: 500;
}

/* On smaller screens, show source code and preview top to bottom */
@media (max-width: 1024px) {
width: 100%;
height: 50%;
min-height: 0;
}
}

#preview {
iframe {
border: none;
width: 100%;
flex-grow: 1;
min-height: 0;
}
}

#source-code {
pre {
margin: 0;
flex-grow: 1;
overflow: auto;
min-height: 0;
padding: 10px;
}
}

#code-target {
/* Reset specific height/overflow as pre handles it now */
height: auto;
overflow: visible;
}
Loading