Skip to content

Resizing iframe heights for storybook examples #3789

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
73b8b33
Testing solution for iframe heights for storybook examples
babsdenney Feb 10, 2025
024893c
Update storybook-preview.html
babsdenney Feb 10, 2025
445acfd
Update head.html
babsdenney Feb 10, 2025
b6bed8c
Update head.html
babsdenney Feb 10, 2025
412c5a7
Update storybook-preview.html
babsdenney Feb 10, 2025
14a9701
Update storybook-preview.html
babsdenney Feb 10, 2025
5ec1537
Update storybook-preview.html
babsdenney Feb 10, 2025
01bfef1
Update storybook-preview.html
babsdenney Feb 10, 2025
820e775
Update storybook-preview.html
babsdenney Feb 10, 2025
b0283e3
Update storybook-preview.html
babsdenney Feb 10, 2025
d9d81d2
Update storybook-preview.html
babsdenney Feb 10, 2025
9f11939
Update storybook-preview.html
babsdenney Feb 10, 2025
bc697f4
Update storybook-preview.html
babsdenney Feb 10, 2025
949cda6
Update storybook-preview.html
babsdenney Feb 10, 2025
a2ba690
Update storybook-preview.html
babsdenney Feb 10, 2025
059a1d4
Update storybook-preview.html
babsdenney Feb 10, 2025
3a019a0
Update storybook-preview.html
babsdenney Feb 10, 2025
829c278
Update head.html
babsdenney Feb 10, 2025
253b9aa
Update head.html
babsdenney Feb 10, 2025
3185e84
Update head.html
babsdenney Feb 10, 2025
10d1f6f
Update head.html
babsdenney Feb 10, 2025
dd86ef2
Update head.html
babsdenney Feb 10, 2025
1113b25
Asked copilot to help fix this code
babsdenney Apr 22, 2025
1f43cda
Revert "Asked copilot to help fix this code"
babsdenney Apr 22, 2025
9e6464a
Used copilot to fix the code
babsdenney Apr 22, 2025
dd7e204
Merge branch 'main' into 771-Adjust-iframe-height-to-fit-its-Storyboo…
babsdenney Apr 22, 2025
6354264
Fixed error in code from copilot
babsdenney Apr 22, 2025
41ecd4d
Trying to fix a bug that is preventing some of the iframes from loading
babsdenney Apr 22, 2025
0b68412
Fixed the bug with some of the iframes not loading.
babsdenney Apr 22, 2025
c2de8b5
Asked copilot to fix the resize issue
babsdenney Apr 22, 2025
f435a8e
Fixing bug copilot implemented
babsdenney Apr 22, 2025
2de21c6
New solution from copilot
babsdenney Apr 22, 2025
2788927
Still not working, asked to fix the code
babsdenney Apr 22, 2025
9932169
Added a solution from chatgpt to compare
babsdenney Apr 23, 2025
b322a1f
Added class to fix blank iframe bug
babsdenney Apr 23, 2025
88afb82
Update storybook-preview.html
babsdenney Apr 23, 2025
23ce14a
Removing minimum height
babsdenney Apr 23, 2025
9dc845c
Manually added 10px to the height
babsdenney Apr 23, 2025
52cd9b6
Adding some more height to the iframe
babsdenney Apr 23, 2025
176024b
Added more height to fix scroll on some of the iframes
babsdenney Apr 23, 2025
f9cc21c
Added a feature to manually add sizes to iframes if needed
babsdenney Apr 23, 2025
a123ec3
Updating script to remove bug
babsdenney Apr 23, 2025
c221552
Removed iframe resize from back to top component
babsdenney Apr 23, 2025
2cb4d44
Manually added sizing for modals
babsdenney Apr 23, 2025
05b1c73
Added a way to manually add a height if autoresize is false
babsdenney Apr 23, 2025
cc9e497
Merge branch 'main' into 771-Adjust-iframe-height-to-fit-its-Storyboo…
babsdenney Apr 23, 2025
fe0a1a9
Update head.html
babsdenney Apr 23, 2025
18cdc5b
Removed commented out styles
babsdenney Apr 29, 2025
081ac91
Merge branch 'main' into 771-Adjust-iframe-height-to-fit-its-Storyboo…
babsdenney Apr 30, 2025
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
2 changes: 1 addition & 1 deletion src/_components/back-to-top.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ anchors:

### Desktop

{% include storybook-preview.html height="300px" story="components-va-back-to-top--default" link_text="va-back-to-top" %}
{% include storybook-preview.html height="300px" story="components-va-back-to-top--default" link_text="va-back-to-top" auto_resize=false %}

## Usage

Expand Down
24 changes: 12 additions & 12 deletions src/_components/modal/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,51 +22,51 @@ anchors:

### Default

{% include storybook-preview.html story="uswds-va-modal--default" link_text="va-modal" %}
{% include storybook-preview.html story="uswds-va-modal--default" link_text="va-modal" auto_resize=false height="300px" %}

### Info

{% include storybook-preview.html story="uswds-va-modal--info" link_text="va-modal info" %}
{% include storybook-preview.html story="uswds-va-modal--info" link_text="va-modal info" auto_resize=false height="320px" %}

### Continue

{% include storybook-preview.html story="uswds-va-modal--continue" link_text="va-modal continue" %}
{% include storybook-preview.html story="uswds-va-modal--continue" link_text="va-modal continue" auto_resize=false height="340px" %}

### Success

{% include storybook-preview.html story="uswds-va-modal--success" link_text="va-modal success" %}
{% include storybook-preview.html story="uswds-va-modal--success" link_text="va-modal success" auto_resize=false height="320px" %}

### Warning

{% include storybook-preview.html story="uswds-va-modal--warning" link_text="va-modal warning" %}
{% include storybook-preview.html story="uswds-va-modal--warning" link_text="va-modal warning" auto_resize=false height="320px" %}

### Error

{% include storybook-preview.html story="uswds-va-modal--error" link_text="va-modal error" %}
{% include storybook-preview.html story="uswds-va-modal--error" link_text="va-modal error" auto_resize=false height="320px" %}

### Click outside to close

{% include storybook-preview.html story="uswds-va-modal--click-outside-to-close" link_text="va-modal click outside to close" %}
{% include storybook-preview.html story="uswds-va-modal--click-outside-to-close" link_text="va-modal click outside to close" auto_resize=false height="300px" %}

### Without buttons

{% include storybook-preview.html story="uswds-va-modal--without-buttons" link_text="va-modal without buttons" %}
{% include storybook-preview.html story="uswds-va-modal--without-buttons" link_text="va-modal without buttons" auto_resize=false height="240px" %}

### Without title

{% include storybook-preview.html story="uswds-va-modal--without-title" link_text="va-modal without title" %}
{% include storybook-preview.html story="uswds-va-modal--without-title" link_text="va-modal without title" auto_resize=false height="240px" %}

### With nested web components

{% include storybook-preview.html story="uswds-va-modal--with-nested-web-components" link_text="va-modal with nested web components" %}
{% include storybook-preview.html story="uswds-va-modal--with-nested-web-components" link_text="va-modal with nested web components" auto_resize=false height="410px" %}

### Large

{% include storybook-preview.html story="uswds-va-modal--large" link_text="va-modal large" %}
{% include storybook-preview.html story="uswds-va-modal--large" link_text="va-modal large" auto_resize=false height="360px" %}

### With forced action

{% include storybook-preview.html story="uswds-va-modal--with-forced-action" link_text="va-modal with forced action" %}
{% include storybook-preview.html story="uswds-va-modal--with-forced-action" link_text="va-modal with forced action" auto_resize=false height="300px" %}

## Usage

Expand Down
2 changes: 1 addition & 1 deletion src/_includes/head.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@

<script src="{{ '/assets/javascripts/polyfills/array.from.js' | prepend: site.baseurl | append: cacheBust }}"></script>

</head>
</head>
68 changes: 62 additions & 6 deletions src/_includes/storybook-preview.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
{% assign storybook_path = site.storybook_path %}
{% if include.is_mobile %}{% assign storybook_path = site.storybook_mobile_path %}{% endif %}
{% assign storybook_iframe_path = storybook_path | append: '/iframe.html?id=' | append: include.story | append: '&amp;viewMode=story' %}
{% if include.is_mobile %}
{% assign storybook_path = site.storybook_mobile_path %}
{% endif %}
{% assign storybook_iframe_path = storybook_path | append: '/iframe.html?id=' | append: include.story | append: '&viewMode=story' %}

<style>
iframe {
border: none;
width: 100%;
opacity: 0;
transition: opacity 0.3s ease;
display: block;
}
</style>

{% if include.scrollable %}
<div
Expand All @@ -20,15 +32,59 @@
style="max-width: {{ include.width | default: '100%' }}"
>
<iframe
width="100%"
height="{{ include.height }}"
class="storybook-embed"
id="{{ include.story }}"
src="{{ storybook_iframe_path }}"
title="Storybook: {{ include.story }}"
{% if include.auto_resize == false %}
data-auto-resize="false"
style="width: 100%; height: {{ include.height | default: '150px' }}; border: none;"
{% else %}
data-auto-resize="true"
style="width: 100%; border: none;"
{% endif %}
></iframe>
</div>
{% endif %}

{% assign story_name_prefix = '' %}
{% if include.is_mobile %}{% assign story_name_prefix = 'va-mobile_' %}{% endif %}
{% if include.is_mobile %}
Copy link
Contributor

Choose a reason for hiding this comment

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

It does not seem to work for the mobile stories:

https://dev-design.va.gov/3789/components/alert/#mobile

Screenshot 2025-04-29 at 8 32 11 AM

Copy link
Collaborator

Choose a reason for hiding this comment

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

Barb accounted for this under known issues:

It seems to ignore sizing for mobile iframes. See alerts

There are only 7 mobile components thus far. I'm willing to let this go until we figure out where the mobile components are going to ultimately live.

{% assign story_name_prefix = 'va-mobile_' %}
{% endif %}
{% assign storybook_preview_path = site.storybook_path | append: '/?path=/story/' | append: story_name_prefix | append: include.story %}

[{% if include.link_text %}<img aria-hidden="true" role="img" src="{{ site.baseurl }}/images/storybook.svg" class="site-component-resources-links__icon" width="16px" alt="Storybook logo">View {{ include.link_text}}{% else %}See this{% endif %} in Storybook]({{ storybook_preview_path }})
[<img aria-hidden="true" role="img" src="{{ site.baseurl }}/images/storybook.svg" class="site-component-resources-links__icon" width="16px" alt="Storybook logo">View {{ include.link_text | default: 'this' }} in Storybook]({{ storybook_preview_path }})

<script>
function resizeIframes() {
const iframes = document.querySelectorAll('iframe.storybook-embed');

iframes.forEach((iframe) => {
// Skip if data-auto-resize is explicitly false
if (iframe.dataset.autoResize === 'false') return;
Copy link
Contributor

@jamigibbs jamigibbs Apr 29, 2025

Choose a reason for hiding this comment

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

In what situations would we not want it to resize? Are there many stories that we have to manually set the data-auto-resize to false?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Barb gave the example of Back to Top. I suspect there are a few more.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I also used this feature to turn off the resize for the modals so we could manually apply the heights. I was thinking of manually applying the height to some of the components that still have a scroll bar, like the progress bar and expandable alert, so we can get to a state where there is no scrolling in the iframes.


try {
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
if (!iframeDoc) return;

const newHeight = iframeDoc.body.scrollHeight;

if (iframe.dataset.lastHeight != newHeight) {
iframe.style.height = newHeight + 35 + 'px';
iframe.dataset.lastHeight = newHeight;
}
} catch (err) {
console.warn('Cannot access iframe content due to cross-origin restrictions:', err);
}
});
}

setInterval(resizeIframes, 500);
Copy link
Contributor

Choose a reason for hiding this comment

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

There might be some concern with using setInterval. This will keep running infinitely every 500 milliseconds and I wonder if it would introduce performance issues.

Is it possible to just use setTimeout? Or did we find that to be difficult to determine the delay value?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is what allows it to be responsive to the user interacting with the components in the iFrames, which I quite like. But I see the performance concerns. We could either give up on that or find another way to trigger the resize. But that seems difficult given that the components are in iFrames. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would be ideal to fix the performance issues. I asked ChatGPT about this just now. Two prompts about improving performance and then also keeping the feature of dynamically resizing iframes.


Great follow-up — the answer depends on which method you choose and whether your iframes are same-origin or cross-origin.

Here's a breakdown of the options and their behavior when components inside the iframe (like modals, accordions, tabs, etc.) cause the height to change dynamically:


✅ 1. Using postMessage + ResizeObserver (inside Storybook)

☑️ Best for cross-origin setups
☑️ Resizes when content changes dynamically
☑️ Efficient — no polling

This works even when content expands later (e.g., a modal opens, an accordion expands), because ResizeObserver tracks content size changes in the iframe and posts new height to the parent.

🛠️ Requires adding this JavaScript to the Storybook's preview-body.html:

<script>
(function () {
  const id = window.frameElement?.id || 'storybook';
  let lastHeight = 0;

function postHeight() {
const height = document.documentElement.scrollHeight;
if (height !== lastHeight) {
lastHeight = height;
window.parent.postMessage({
type: 'storybook-resize',
id,
height
}, '*');
}
}

const observer = new ResizeObserver(postHeight);
observer.observe(document.body);

// Fallback: post periodically in case ResizeObserver misses changes
setInterval(postHeight, 500);

// Trigger once on load
window.addEventListener('DOMContentLoaded', postHeight);
})();
</script>

And the parent page just listens:

window.addEventListener('message', (event) => {
  if (event.data?.type === 'storybook-resize') {
    const iframe = document.getElementById(event.data.id);
    if (iframe) {
      iframe.style.height = event.data.height + 'px';
    }
  }
});

✅ Result: Resizes whenever content changes — no constant polling on parent.


⚠️ 2. Using parent-side setInterval (your current approach)

☑️ Works with same-origin iframes only
☒ May not detect all content changes (e.g., transitions, async rendering)
☒ Can be performance-intensive if run too frequently

Even if you're using setInterval every 500ms, it might miss resizes if the content animates in slowly or you have React portals (like modals outside the main DOM flow).

Also, iframes from another domain cannot be read by JavaScript in the parent (security restriction), so the interval will fail unless they're same-origin.


✅ Best Practice Summary

Approach Cross-Origin Dynamic Content Performance
postMessage + ResizeObserver
Parent-side setInterval ❌ (X-origin) ⚠️ Sometimes
Manual height via include

💡 Recommendation:

Use the ResizeObserver + postMessage approach for dynamic resizing with minimal performance overhead — especially when your Storybook iframe is hosted separately (Netlify, GitHub Pages, etc.).

Would you like me to give you a drop-in version for your setup that only touches the parent and the preview-body.html inside Storybook?

Copy link
Contributor

Choose a reason for hiding this comment

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

dynamically resizing iframes

In that case, using ResizeObserver would be preferred if it's possible. This is the approach we use for a few component-library components as well.

But it will require updating the Storybook package too in order to send a message. I think that's how Andres was originally going to try and approach the problem:

We might be able to dispatch an event in Storybook and update the height when we know this element exists in the DOM as a workaround.

Your AI suggestion for the ResizeObserver approach still wants to use setInterval as a fallback anyway.

// Fallback: post periodically in case ResizeObserver misses changes
setInterval(postHeight, 500);

I guess for the sake of time, we can go with the single setInterval approach but I suspect that, if there are issues loading Storybook (like for local development), we might see some odd behavior.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Given that it would be a fallback we could also push that setInterval time out a bit (1000? 1500?)

window.addEventListener('load', resizeIframes);

document.querySelectorAll('iframe.storybook-embed').forEach((iframe) => {
iframe.addEventListener('load', () => {
iframe.style.opacity = 1;
});
});
</script>
Loading