diff --git a/prettier.json b/prettier.json
index 232464b5..11ac27e4 100644
--- a/prettier.json
+++ b/prettier.json
@@ -7,5 +7,6 @@
"bracketSpacing": true,
"bracketSameLine": true,
"arrowParens": "always",
- "endOfLine": "lf"
+ "endOfLine": "lf",
+ "quoteProps": "as-needed"
}
diff --git a/src/components/back_to_top/index.js b/src/components/back_to_top/index.js
index c430590a..28cb244f 100644
--- a/src/components/back_to_top/index.js
+++ b/src/components/back_to_top/index.js
@@ -1,20 +1,20 @@
/**
* @file index.js
- * @description Example Component entry file (index.js).
- * @module example
+ * @description BackToTop Component entry file (index.js).
+ * @module BackToTop
*/
// Imports QGDS Component utility:
import QGDSComponent from "./../../js/QGDSComponent.js";
-// Imports resources needed to nake our "Example" component:
+// Imports resources needed to make our "BackToTop" component:
import hbstemplate from "./back-to-top.hbs?raw";
import logic from "./back-to-top.js";
import meta from "./version.json";
/**
* @function BackToTop
- * @description The Example component.
+ * @description The BackToTop component.
* @param {object} data - The data to be used in the template.
* @param {string} template - The template to render.
* @returns {object} - A new instance of the QGDSComponent class, contained properties: template, meta, htmlstring, node.
diff --git a/src/components/in_page_navigation/stories/InPageNavigation.mdx b/src/components/in_page_navigation/_other/_InPageNAvigation.mdx
similarity index 100%
rename from src/components/in_page_navigation/stories/InPageNavigation.mdx
rename to src/components/in_page_navigation/_other/_InPageNAvigation.mdx
diff --git a/src/components/in_page_navigation/html/example1.test.hbs b/src/components/in_page_navigation/_other/example1.test.hbs
similarity index 100%
rename from src/components/in_page_navigation/html/example1.test.hbs
rename to src/components/in_page_navigation/_other/example1.test.hbs
diff --git a/src/components/in_page_navigation/html/component.hbs b/src/components/in_page_navigation/html/component.hbs
deleted file mode 100644
index b216a3bc..00000000
--- a/src/components/in_page_navigation/html/component.hbs
+++ /dev/null
@@ -1,17 +0,0 @@
-{{!-- {{#ifCond current.data.metadata.pageType.value '==' 'landing'}} --}}
-
-
-{{!-- {{/ifCond}} --}}
-
-
-
- {{{component.data.metadata.heading.value}}}
-
-
-
-
-{{!-- {{#ifCond current.data.metadata.pageType.value '==' 'landing'}} --}}
-
-
-{{!-- {{/ifCond}} --}}
-
diff --git a/src/components/in_page_navigation/html/example1.json b/src/components/in_page_navigation/html/example1.json
deleted file mode 100644
index 1f7da8f0..00000000
--- a/src/components/in_page_navigation/html/example1.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "component": {
- "name": "In Page Navigation",
- "description": "",
- "version": "1.0",
- "status": "Released",
- "data": {
- "metadata": {
- "heading": {
- "type": "metadata_field_text",
- "description": "",
- "friendly_name": "Navigation Heading",
- "value": "On This Page",
- "required": false,
- "editable": true
- },
- "headingType": {
- "type": "metadata_field_select",
- "description": "",
- "friendly_name": "Heading Type",
- "value": "h2",
- "options": {
- "h2": "H2",
- "h3": "H3",
- "h4": "H4",
- "h5": "H5",
- "h6": "H6"
- },
- "required": false,
- "editable": true
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/components/in_page_navigation/index.js b/src/components/in_page_navigation/index.js
new file mode 100644
index 00000000..486e113b
--- /dev/null
+++ b/src/components/in_page_navigation/index.js
@@ -0,0 +1,49 @@
+/**
+ * @file index.js
+ * @module InpageNavigation
+ * @description Inpage Navigation Component file.
+ *
+ * @function InpageNavigation
+ * @description The Inpage Navigation component.
+ * @param {object} data - The data to be used by the template.
+ * @param {string} template - The handlebars template to render.
+ * @returns {object} - A new instance of the QGDSComponent class, containing properties: template, meta, htmlstring, node.
+ */
+
+// Imports QGDS Component utility:
+import QGDSComponent from "./../../js/QGDSComponent.js";
+
+// Imports resources needed to make our "Inpage Navigation" component:
+import hbstemplate from "./inpage-navigation.hbs?raw";
+import logic from "./inpage-navigation.js";
+import meta from "./version.json";
+
+export default function InpageNavigation({ data, template = hbstemplate }) {
+ // Initialise Inpage Navigation JS on page load (if data.dynamiclinks is true)
+ document.addEventListener("DOMContentLoaded", () => {
+ try {
+ if (data.source === "dynamic") {
+ /**
+ * Initialize the logic for Inpage Navigation.
+ * @function init
+ * @memberof logic
+ */
+ logic.init();
+ }
+ } catch (error) {
+ console.error(`InpageNavigation error in function 'init': ${error.message}`, {
+ functionName: "init",
+ errorDetails: error,
+ });
+ }
+ });
+
+ //Minimum required fields for the component to function
+ const props = {
+ data: data,
+ template: template,
+ meta: meta || {},
+ };
+
+ return new QGDSComponent("InpageNavigation", props);
+}
diff --git a/src/components/in_page_navigation/inpage-navigation.data.json b/src/components/in_page_navigation/inpage-navigation.data.json
new file mode 100644
index 00000000..67cfcf70
--- /dev/null
+++ b/src/components/in_page_navigation/inpage-navigation.data.json
@@ -0,0 +1,25 @@
+{
+ "id": "in-page-navigation",
+ "component": "inpage-navigation",
+ "title": "On this page",
+ "headingType": "h2",
+ "source": "static",
+ "links": [
+ {
+ "href": "#section-1",
+ "title": "Static Link Section 1"
+ },
+ {
+ "href": "#section-2",
+ "title": "Static Link Section 2"
+ },
+ {
+ "href": "#section-3",
+ "title": "Static Link Section 3"
+ },
+ {
+ "href": "#section-4",
+ "title": "Static Link Section 4"
+ }
+ ]
+}
diff --git a/src/components/in_page_navigation/inpage-navigation.hbs b/src/components/in_page_navigation/inpage-navigation.hbs
new file mode 100644
index 00000000..28243d3b
--- /dev/null
+++ b/src/components/in_page_navigation/inpage-navigation.hbs
@@ -0,0 +1,22 @@
+
+
+
+ {{{title}}}
+
+
+
+ {{#ifCond source '==' 'static'}}
+ {{#each links}}
+
+ {{{title}}}
+
+ {{/each}}
+ {{/ifCond}}
+
+ {{#ifCond source '==' 'dynamic'}}
+ {{!-- LI > A links will be injected by the component's JS --}}
+ {{/ifCond}}
+
+
+
\ No newline at end of file
diff --git a/src/components/in_page_navigation/inpage-navigation.js b/src/components/in_page_navigation/inpage-navigation.js
new file mode 100644
index 00000000..5c19e503
--- /dev/null
+++ b/src/components/in_page_navigation/inpage-navigation.js
@@ -0,0 +1,36 @@
+export default {
+ init() {
+ var navs = document.querySelectorAll(".qld__inpage-nav-links");
+ var mainEl = document.querySelector("main.main");
+ var isLandingPage = mainEl && mainEl.classList.contains("landing");
+
+ // For all In-Page Nav components
+ navs.forEach(function (nav) {
+ var headingSelector = nav.getAttribute("data-headingType") ? nav.getAttribute("data-headingType") : "h2";
+ var pageContent = isLandingPage ? mainEl : document.getElementById("content");
+ var headings = pageContent.querySelectorAll(
+ headingSelector + ":not(.qld__inpage-nav-links__heading):not(.banner__heading)",
+ );
+ var list = nav.querySelector(".qld__link-list");
+ list.innerHTML = "";
+
+ if (headings.length === 0) {
+ nav.style.display = "none";
+ }
+
+ // For all headings (with matching data-headingType) in page content
+ headings.forEach(function (heading) {
+ var title = heading.innerText;
+ var id = "section__" + title.toLowerCase().replace(/\s+/g, "-");
+ heading.setAttribute("id", id);
+ heading.setAttribute("tabindex", -1);
+ var link = '
' + title + " ";
+
+ // Append link item if it doesn't already exist in list
+ if (list.querySelector('a[href="#' + id + '"') === null) {
+ list.insertAdjacentHTML("beforeend", link);
+ }
+ });
+ });
+ },
+};
diff --git a/src/components/in_page_navigation/css/component.scss b/src/components/in_page_navigation/inpage-navigation.scss
similarity index 87%
rename from src/components/in_page_navigation/css/component.scss
rename to src/components/in_page_navigation/inpage-navigation.scss
index bf14df9a..d3047725 100644
--- a/src/components/in_page_navigation/css/component.scss
+++ b/src/components/in_page_navigation/inpage-navigation.scss
@@ -18,16 +18,17 @@
line-height: $typographyDesktopH6LineHeight;
}
- * + & {
+ *+& {
@include QLD-space(margin-top, 2.3125unit);
}
+
a {
@include QLD-underline("light", "underline", "default", "noVisited");
}
- .qld__body & > ul,
- .qld__body & > ol {
- @include QLD-space(margin-top, 0.75unit);
+ .qld__body &>ul,
+ .qld__body &>ol {
+ @include QLD-space(margin-top, 0.75unit); // Consider using a variable for 0.75unit
list-style-type: none;
padding: 0;
@@ -35,17 +36,19 @@
margin: 0;
}
- * + li {
+ *+li {
margin-left: 0;
}
- li + li {
- @include QLD-space(margin-top, 0.5unit);
+
+ li+li {
+ @include QLD-space(margin-top, 0.5unit); // Consider using a variable for 0.5unit
}
}
.qld__body--dark &,
.qld__body--dark-alt & {
border-color: var(--QLD-color-dark__action--primary);
+
a {
@include QLD-underline("dark", "underline", "default", "noVisited");
}
@@ -74,4 +77,4 @@
.qld__inpage-nav-links {
display: none !important;
}
-}
+}
\ No newline at end of file
diff --git a/src/components/in_page_navigation/inpage-navigation.stories.js b/src/components/in_page_navigation/inpage-navigation.stories.js
new file mode 100644
index 00000000..ab43346e
--- /dev/null
+++ b/src/components/in_page_navigation/inpage-navigation.stories.js
@@ -0,0 +1,120 @@
+/**
+ * @file inpage-navigation.stories.js
+ * @description Storybook configuration file for the Inpage Navigation component.
+ * @module inpage-navigation.stories
+ */
+
+// Imports:
+// - the QGDS object containing all components
+// - data you need to populate the component for rendering
+import { QGDS } from "../../js/index.js";
+import mockupData from "./inpage-navigation.data.json";
+
+/* ========= STORIES 👇 ===== */
+
+export default {
+ title: "Components/Navigation (In-page navigation)",
+ render: (args) => {
+ try {
+ return new QGDS.InpageNavigation({ data: args }).htmlstring;
+ } catch (e) {
+ return JSON.stringify(e) + JSON.stringify(args);
+ }
+ },
+ args: mockupData,
+
+ decorators: [
+ (Story) => {
+ return `
+ `;
+ },
+ ],
+
+ /**
+ * Additional parameters for the story.
+ *
+ * @type {Object}
+ * @property {Object} design - Configuration for the design parameter.
+ * @property {string} design.name - Name of the design parameter.
+ * @property {string} design.type - Type of the design parameter. figma | link
+ * @property {string} design.url - URL of the design parameter.
+ */
+ parameters: {
+ design: [
+ {
+ name: "Link",
+ type: "link",
+ url: "https://www.figma.com/design/qKsxl3ogIlBp7dafgxXuCA/QLD-GOV-DDS?node-id=7229-112138",
+ },
+ {
+ name: "QGDS Figma Reference",
+ type: "figma",
+ url: "https://www.figma.com/design/qKsxl3ogIlBp7dafgxXuCA/QLD-GOV-DDS?node-id=7229-112138",
+ },
+ {
+ name: "dark xl",
+ type: "figma",
+ url: "https://www.figma.com/design/qKsxl3ogIlBp7dafgxXuCA/QLD-GOV-DDS?node-id=10865-242473",
+ },
+ ],
+ },
+};
+
+/**
+ * In-page Navigation with Static links (Default)
+ */
+
+export const WithStaticLinks = {
+ args: {
+ ...mockupData,
+ source: "static",
+ },
+};
+
+/**
+ * In-page Navigation with dynamic links
+ */
+
+export const WithDynamicLinks = {
+ args: {
+ ...mockupData,
+ source: "dynamic",
+ },
+ decorators: [
+ (Story) => {
+ return `
+
+
+
Page title
+
+
+ ${Story()}
+
+
Section 1
+
+ This inpage navigation list was generated with Javascript that looped over all H2 tags within the #content container.
+
+
+
Section 2
+
This is a paragraph under section 2. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec purus nec nunc ultricies ultricies.
+
+
Sub-section
+
This is a sub-section under section 2.
+
+
Sub-section
+
This is a sub-section under section 2.
+
+
Section 3
+
+ This is a paragraph under section 3. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+ Nullam nec purus nec nunc ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies.
+
+
+
`;
+ },
+ ],
+};
diff --git a/src/components/in_page_navigation/inpage-navigation.test.js b/src/components/in_page_navigation/inpage-navigation.test.js
new file mode 100644
index 00000000..437ffa7f
--- /dev/null
+++ b/src/components/in_page_navigation/inpage-navigation.test.js
@@ -0,0 +1,21 @@
+/**
+ * @jest-environment jsdom
+ */
+
+import { expect, test } from "vitest";
+
+import InpageNavigation from "./index.js";
+import MockData from "./inpage-navigation.data.json";
+
+test("Inpage Navigation initiated", () => {
+ const InpageNavigationComponent = new InpageNavigation({ data: MockData });
+ expect(InpageNavigationComponent).toBeDefined();
+});
+
+//1. Test that the component renders the correct HTML structure:
+test("Inpage Navigation renders the correct structure", () => {
+ const InpageNavigationComponent = new InpageNavigation({ data: MockData });
+ expect(InpageNavigationComponent.htmlstring).toContain("");
+ expect(InpageNavigationComponent.htmlstring).toMatch(//);
+});
diff --git a/src/components/in_page_navigation/js/global.js b/src/components/in_page_navigation/js/global.js
deleted file mode 100644
index cf4bd6c0..00000000
--- a/src/components/in_page_navigation/js/global.js
+++ /dev/null
@@ -1,51 +0,0 @@
-export default function (QLD) {
-
- /**
- * @module inPageNav
- */
- var inPageNav = {};
-
- /**
- * Create in page nav from headings
- *
- * @memberof module:inPageNav
- */
- inPageNav.init = function () {
- var navs = document.querySelectorAll('.qld__inpage-nav-links');
- var mainEl = document.querySelector('main.main');
- var isLandingPage = mainEl && mainEl.classList.contains('landing');
-
- // For all In-Page Nav components
- navs.forEach(function (nav) {
-
- var headingSelector = nav.getAttribute('data-headingType') ? nav.getAttribute('data-headingType') : 'h2';
- var pageContent = isLandingPage ? mainEl : document.getElementById('content');
- var headings = pageContent.querySelectorAll(headingSelector + ':not(.qld__inpage-nav-links__heading):not(.banner__heading)');
- var list = nav.querySelector('.qld__link-list');
- list.innerHTML = '';
-
- if (headings.length === 0) {
- nav.style.display = 'none';
- }
-
- // For all headings (with matching data-headingType) in page content
- headings.forEach(function (heading) {
- var title = heading.innerText;
- var id = 'section__' + title.toLowerCase().replace(/\s+/g, '-');
- heading.setAttribute('id', id);
- heading.setAttribute('tabindex', -1);
- var link = '' + title + ' ';
-
- // Append link item if it doesn't already exist in list
- if (list.querySelector('a[href="#' + id + '"') === null) {
- list.insertAdjacentHTML('beforeend', link);
- }
- });
- });
- }
-
- QLD.inPageNav = inPageNav;
-
- // Init In Page Nav on document load
- document.addEventListener('DOMContentLoaded', QLD.inPageNav.init);
-}
diff --git a/src/components/in_page_navigation/stories/inPageNavigation.stories.js b/src/components/in_page_navigation/stories/inPageNavigation.stories.js
deleted file mode 100644
index b8a49774..00000000
--- a/src/components/in_page_navigation/stories/inPageNavigation.stories.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * @file inPageNavigation.stories.js
- * @description Storybook configuration file for the inPageNavigation component.
- * @module inPageNavigation.stories
- */
-
-import example1 from "../html/example1.json";
-import example1template from "./../html/example1.test.hbs?raw";
-
-
-// load helpers handlebars
-import Handlebars from "handlebars";
-import handlebarsInit from "../../../helpers/handlebars.init.js";
-
-
-export default {
- title: "Components/Navigation (In-page navigation)",
- render: ( args) => {
- handlebarsInit(Handlebars)
- try {
- return Handlebars.compile(example1template)(args)
- } catch (e) {
- console.log(e)
- return JSON.stringify(e) + JSON.stringify(args);
- }
- },
- args: example1,
-
-
- /**
- * Additional parameters for the story.
- *
- * @type {Object}
- * @property {Object} design - Configuration for the design parameter.
- * @property {string} design.name - Name of the design parameter.
- * @property {string} design.type - Type of the design parameter. figma | link
- * @property {string} design.url - URL of the design parameter.
- */
- parameters: {
- design: [
- {
- name: "Link",
- type: "link",
- url: "https://www.figma.com/design/qKsxl3ogIlBp7dafgxXuCA/QLD-GOV-DDS?node-id=7229-112138"
- },
- {
- name: "QGDS Figma Reference",
- type: "figma",
- url: "https://www.figma.com/design/qKsxl3ogIlBp7dafgxXuCA/QLD-GOV-DDS?node-id=7229-112138",
- },{
- name: "dark xl",
- type: "figma",
- url: "https://www.figma.com/design/qKsxl3ogIlBp7dafgxXuCA/QLD-GOV-DDS?node-id=10865-242473",
- }],
- },
-};
-
-/**
- * Default
-
- */
-export const Default = {};
diff --git a/src/js/index.js b/src/js/index.js
index 55f5219b..b511d35d 100644
--- a/src/js/index.js
+++ b/src/js/index.js
@@ -2,10 +2,12 @@ import Example from "../components/_example/index.js";
import Breadcrumbs from "../components/breadcrumbs/index.js";
// import Button from "../components/button/index.js";
import BackToTop from "../components/back_to_top/index.js";
+import InpageNavigation from "../components/in_page_navigation/index.js";
// import Dropdown from "../components/dcropdown/index.js";
export const QGDS = {
Example,
Breadcrumbs,
BackToTop,
+ InpageNavigation,
};