From 2b7171850cf4713a1f4a57ed1411e6239f39b252 Mon Sep 17 00:00:00 2001 From: Murray Lippiatt Date: Tue, 3 Dec 2024 15:08:22 +0000 Subject: [PATCH 1/5] docs(example template): amending example template Related to https://github.com/ministryofjustice/moj-frontend/issues/958 --- docs/_includes/example.njk | 10 +++++----- docs/_includes/layouts/example.njk | 2 +- docs/assets/stylesheets/components/_copy-button.scss | 2 +- docs/assets/stylesheets/components/_example.scss | 12 +++++------- docs/assets/stylesheets/components/_prose.scss | 6 +++--- docs/assets/stylesheets/components/_tabs.scss | 12 ++++-------- 6 files changed, 19 insertions(+), 25 deletions(-) diff --git a/docs/_includes/example.njk b/docs/_includes/example.njk index adb8beca..85b32d7c 100644 --- a/docs/_includes/example.njk +++ b/docs/_includes/example.njk @@ -1,7 +1,7 @@ -
- + +
@@ -34,7 +34,7 @@
{% if arguments %} -
+
Nunjucks macro options @@ -74,7 +74,7 @@ This component is in the ‘Assets’ tab in the MoJ Figma Kit. If you’re external to MoJ, download the Kit and add it as a library to your Figma files. You’ll need to re-add the kit to update the version. -[View component in MoJ Figma Kit]({{ figmaLink }}) +

View component in MoJ Figma Kit

{% endif %} diff --git a/docs/_includes/layouts/example.njk b/docs/_includes/layouts/example.njk index e3919faa..0ec48ee4 100644 --- a/docs/_includes/layouts/example.njk +++ b/docs/_includes/layouts/example.njk @@ -3,7 +3,7 @@ {% endblock %} {% block body %} -
{{ content | safe }}
+
{{ content | safe }}
{% endblock %} {% block pageScripts %} diff --git a/docs/assets/stylesheets/components/_copy-button.scss b/docs/assets/stylesheets/components/_copy-button.scss index d202af9b..6eb53776 100644 --- a/docs/assets/stylesheets/components/_copy-button.scss +++ b/docs/assets/stylesheets/components/_copy-button.scss @@ -5,7 +5,7 @@ position: absolute; z-index: 1; top: govuk-spacing(4); - right: govuk-spacing(4); + right: govuk-spacing(8); min-width: 110px; padding: 4px 10px 2px 10px; border: 1px solid $copy-button-colour; diff --git a/docs/assets/stylesheets/components/_example.scss b/docs/assets/stylesheets/components/_example.scss index 164a79a6..57389948 100644 --- a/docs/assets/stylesheets/components/_example.scss +++ b/docs/assets/stylesheets/components/_example.scss @@ -4,11 +4,11 @@ .app-example { background-color: govuk-colour("white"); - border: 1px solid $govuk-border-colour; + border-left: 1px solid $govuk-border-colour; + border-right: 1px solid $govuk-border-colour; position: relative; } - .app-example__frame { background-color: govuk-colour("white"); border: 0; @@ -26,16 +26,14 @@ .app-example__new-window { @include govuk-font($size: 16); border: 1px solid $govuk-border-colour; - position: absolute; top: -1px; left: -1px; - background-color: white; - + background-color: govuk-colour("white"); + padding: 8px; a, a:link, a:visited { color: govuk-colour("blue"); - display: block; - margin: 8px; + // display: block; text-decoration: none; } diff --git a/docs/assets/stylesheets/components/_prose.scss b/docs/assets/stylesheets/components/_prose.scss index 269d2bbe..942643f5 100644 --- a/docs/assets/stylesheets/components/_prose.scss +++ b/docs/assets/stylesheets/components/_prose.scss @@ -102,10 +102,10 @@ } pre { - @include govuk-responsive-margin(4, "bottom"); - background: #f8f8f8; // Matches _higlight.scss + @include govuk-responsive-margin(6, "bottom"); + background: #f7f7f7; font-size: 16px; - margin: 0; + margin: govuk-spacing(6); padding: govuk-spacing(3); position: relative; overflow: auto; diff --git a/docs/assets/stylesheets/components/_tabs.scss b/docs/assets/stylesheets/components/_tabs.scss index 967a065a..7a9c228b 100644 --- a/docs/assets/stylesheets/components/_tabs.scss +++ b/docs/assets/stylesheets/components/_tabs.scss @@ -4,12 +4,12 @@ .app-tabs { background: govuk-colour("white"); - margin-bottom: govuk-spacing(6); + border-top: 0; + margin-bottom: govuk-spacing(9); } .app-tabs__list { border: 1px solid $govuk-border-colour; - border-top: 0; list-style: none; margin: 0; padding: 0; @@ -72,7 +72,7 @@ } &[aria-selected="true"] { - background-color: govuk-colour("light-grey"); + background-color: govuk-colour("white"); color: govuk-colour("black"); margin-bottom: -1px; padding-bottom: 21px; @@ -86,7 +86,7 @@ .app-tabs__panel { @include govuk-font($size: 16); - background-color: govuk-colour("light-grey"); + background-color: govuk-colour("white"); border: 1px solid $govuk-border-colour; border-top: 0; position: relative; @@ -95,10 +95,6 @@ display: none; } - *:last-of-type { - margin-bottom: 0; // Remove margin bottom from any last element - } - p { max-width: 100% !important; } From 1370d50c210ba09fced9dbc35143a537b557eadd Mon Sep 17 00:00:00 2001 From: Chris Pymm Date: Wed, 11 Dec 2024 11:00:49 +0000 Subject: [PATCH 2/5] docs(examples): fix margin and padding around example tabs --- docs/_includes/example.njk | 9 ++++++--- docs/assets/stylesheets/components/_copy-button.scss | 4 ++-- docs/assets/stylesheets/components/_tabs.scss | 2 ++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/_includes/example.njk b/docs/_includes/example.njk index 85b32d7c..a8e13352 100644 --- a/docs/_includes/example.njk +++ b/docs/_includes/example.njk @@ -24,17 +24,20 @@ {%- endif -%} -
+
+ +
```html {{ htmlCode | safe }} ```
+
{% if arguments %} -
+
Nunjucks macro options @@ -68,7 +71,7 @@ {% endif %} {% if figmaLink %} -
+
This component is in the ‘Assets’ tab in the MoJ Figma Kit. diff --git a/docs/assets/stylesheets/components/_copy-button.scss b/docs/assets/stylesheets/components/_copy-button.scss index 6eb53776..5b3d17b5 100644 --- a/docs/assets/stylesheets/components/_copy-button.scss +++ b/docs/assets/stylesheets/components/_copy-button.scss @@ -4,8 +4,8 @@ @include govuk-font(16); position: absolute; z-index: 1; - top: govuk-spacing(4); - right: govuk-spacing(8); + top: govuk-spacing(3); + right: govuk-spacing(3); min-width: 110px; padding: 4px 10px 2px 10px; border: 1px solid $copy-button-colour; diff --git a/docs/assets/stylesheets/components/_tabs.scss b/docs/assets/stylesheets/components/_tabs.scss index 7a9c228b..25a75b71 100644 --- a/docs/assets/stylesheets/components/_tabs.scss +++ b/docs/assets/stylesheets/components/_tabs.scss @@ -90,6 +90,7 @@ border: 1px solid $govuk-border-colour; border-top: 0; position: relative; + padding: govuk-spacing(3); &--hidden { display: none; @@ -100,6 +101,7 @@ } pre { + margin: 0; padding-top: govuk-spacing(9); } } From e9bc9ae2782209770efe52919630c99686ecb05b Mon Sep 17 00:00:00 2001 From: Chris Pymm Date: Wed, 11 Dec 2024 14:51:43 +0000 Subject: [PATCH 3/5] docs(examples): add dynamic resizing to ensure iframe is always the correct size On smaller screens the fixed height of the iframes often meant a lot of wasted whitespace. This commit adds and IFrameResizer class to dynamically size/resize the iframes according to the contents. This will dynamically update when the iframe content changes height using a ResizeObserver. It will also resize when elements within the iframe change using a MutationObserver, such as when the datepicker or buton menu pops up. --- docs/assets/javascript/all.js | 4 + docs/assets/javascript/iframe-resizer.js | 125 +++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 docs/assets/javascript/iframe-resizer.js diff --git a/docs/assets/javascript/all.js b/docs/assets/javascript/all.js index b4ad5a69..be04ab5a 100644 --- a/docs/assets/javascript/all.js +++ b/docs/assets/javascript/all.js @@ -4,6 +4,7 @@ import MOJFrontend from "../../../package/moj/all.js"; import Cookies from "./cookies"; import Copy from "./copy"; import Tabs from "./tabs"; +import IFrameResizer from './iframe-resizer.js' import MenuToggle from "./menu-toggle.js"; import CollapsibleNav from "./collapsible-nav.js"; @@ -23,6 +24,9 @@ $(function () { $('[data-module="app-cookies"]').each(function (e, el) { new Cookies(el).init(); }); + + const iFrames = document.querySelectorAll('iframe') + iFrames.forEach((frame) => new IFrameResizer(frame)) }); window.MOJFrontend = MOJFrontend; diff --git a/docs/assets/javascript/iframe-resizer.js b/docs/assets/javascript/iframe-resizer.js new file mode 100644 index 00000000..e67c10e6 --- /dev/null +++ b/docs/assets/javascript/iframe-resizer.js @@ -0,0 +1,125 @@ +export default class IFrameResizer { + constructor(iframe) { + this.iframe = iframe; + this.observer = null; + this.contentWindow = null; + + // Bind methods + this.init = this.init.bind(this); + this.cleanup = this.cleanup.bind(this); + this.onLoad = this.onLoad.bind(this); + this.onResize = this.onResize.bind(this); + this.onMutation = this.onMutation.bind(this); + + // Start initialization + this.iframe.addEventListener("load", this.onLoad); + } + + init() { + try { + this.contentWindow = this.iframe.contentWindow; + + // Create ResizeObserver to watch the iframe content + this.resizeObserver = new ResizeObserver((entries) => { + for (const entry of entries) { + this.onResize(entry); + } + }); + + // Create MutationObserver to watch for visibility changes + this.mutationObserver = new MutationObserver((mutations) => { + for (const mutation of mutations) { + this.onMutation(mutation); + } + }); + + // Observe the body of the iframe content + const targetNode = this.contentWindow.document.body; + this.resizeObserver.observe(targetNode); + + // Observe for attribute changes that might affect visibility + this.mutationObserver.observe(targetNode, { + attributes: true, + attributeFilter: ["style", "class"], + childList: true, + subtree: true, + }); + + // Initial size adjustment + this.adjustSize(); + } catch (error) { + console.error("Failed to initialize IframeResizer:", error); + } + } + + onLoad() { + this.init(); + } + + onMutation(mutation) { + // Check if the mutation is related to visibility + if (mutation.type === "attributes") { + const target = mutation.target; + const computedStyle = window.getComputedStyle(target); + + if ( + mutation.attributeName === "style" || + mutation.attributeName === "class" + ) { + // Check if visibility-related properties changed + if ( + computedStyle.display !== "none" || + computedStyle.visibility !== "hidden" || + computedStyle.opacity !== "0" + ) { + this.adjustSize(); + } + } + } + // Check for added/removed nodes + else if (mutation.type === "childList") { + this.adjustSize(); + } + } + + onResize(entry) { + this.adjustSize(); + } + + adjustSize() { + if (!this.contentWindow) return; + + try { + const body = this.contentWindow.document.body; + const html = this.contentWindow.document.documentElement; + const elements = body.getElementsByTagName("*"); + + let maxHeight = html.offsetHeight; + let padding = 30 + + // Check each element's bottom edge position + for (const element of elements) { + const rect = element.getBoundingClientRect(); + const bottomPos = rect.top + rect.height; + // If maxHeight is bigger, that includes the body padding, if bottomPos + // is higher, that is the exact bottom of the element, so we add some padding + maxHeight = ( maxHeight > bottomPos ? maxHeight : bottomPos + padding ) + } + + // Update iframe height + this.iframe.style.height = `${maxHeight}px`; + } catch (error) { + console.error("Failed to adjust iframe size:", error); + } + } + + cleanup() { + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + } + if (this.mutationObserver) { + this.mutationObserver.disconnect(); + } + this.iframe.removeEventListener("load", this.onLoad); + } +} From 41f88c634bc5f67808694fecdc8680b1cc8d445b Mon Sep 17 00:00:00 2001 From: Chris Pymm Date: Wed, 11 Dec 2024 17:30:48 +0000 Subject: [PATCH 4/5] docs(examples): adjust scope of the mutationObserver to ensure it triggers size adjustment --- docs/assets/javascript/iframe-resizer.js | 35 ++++++------------------ 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/docs/assets/javascript/iframe-resizer.js b/docs/assets/javascript/iframe-resizer.js index e67c10e6..ad907061 100644 --- a/docs/assets/javascript/iframe-resizer.js +++ b/docs/assets/javascript/iframe-resizer.js @@ -40,7 +40,8 @@ export default class IFrameResizer { // Observe for attribute changes that might affect visibility this.mutationObserver.observe(targetNode, { attributes: true, - attributeFilter: ["style", "class"], + attributeFilter: ["style", "class", "hidden", "aria-expanded"], + attributeOldValue: true, childList: true, subtree: true, }); @@ -57,29 +58,11 @@ export default class IFrameResizer { } onMutation(mutation) { - // Check if the mutation is related to visibility - if (mutation.type === "attributes") { - const target = mutation.target; - const computedStyle = window.getComputedStyle(target); - - if ( - mutation.attributeName === "style" || - mutation.attributeName === "class" - ) { - // Check if visibility-related properties changed - if ( - computedStyle.display !== "none" || - computedStyle.visibility !== "hidden" || - computedStyle.opacity !== "0" - ) { - this.adjustSize(); - } - } - } - // Check for added/removed nodes - else if (mutation.type === "childList") { - this.adjustSize(); - } + // Ideally we might want to restrict this slightly to check if we + // need to adjust size, but this is tricky. Most of our components are + // relatively static, so if something changes its likely to be + // visibility-related + this.adjustSize(); } onResize(entry) { @@ -95,7 +78,7 @@ export default class IFrameResizer { const elements = body.getElementsByTagName("*"); let maxHeight = html.offsetHeight; - let padding = 30 + let padding = 30; // Check each element's bottom edge position for (const element of elements) { @@ -103,7 +86,7 @@ export default class IFrameResizer { const bottomPos = rect.top + rect.height; // If maxHeight is bigger, that includes the body padding, if bottomPos // is higher, that is the exact bottom of the element, so we add some padding - maxHeight = ( maxHeight > bottomPos ? maxHeight : bottomPos + padding ) + maxHeight = maxHeight > bottomPos ? maxHeight : bottomPos + padding; } // Update iframe height From d1090e6b87f893f4b70c8d8cd31191fab0f7acb0 Mon Sep 17 00:00:00 2001 From: Murray Lippiatt Date: Thu, 12 Dec 2024 15:08:06 +0000 Subject: [PATCH 5/5] docs(example template): remove commented out line --- docs/assets/stylesheets/components/_example.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/assets/stylesheets/components/_example.scss b/docs/assets/stylesheets/components/_example.scss index 57389948..18397422 100644 --- a/docs/assets/stylesheets/components/_example.scss +++ b/docs/assets/stylesheets/components/_example.scss @@ -33,7 +33,6 @@ a:link, a:visited { color: govuk-colour("blue"); - // display: block; text-decoration: none; }