From f20b818c1c8b0ae81b5638f0488129e2a0d6c40b Mon Sep 17 00:00:00 2001 From: Andrew Hosgood Date: Fri, 6 Oct 2023 17:59:17 +0100 Subject: [PATCH] Allow no default tab option --- src/nationalarchives/components/tabs/tabs.mjs | 39 +++-- .../components/tabs/tabs.scss | 4 + .../components/tabs/tabs.stories.js | 152 +++++++++++++++++- .../components/tabs/template.njk | 3 + 4 files changed, 182 insertions(+), 16 deletions(-) diff --git a/src/nationalarchives/components/tabs/tabs.mjs b/src/nationalarchives/components/tabs/tabs.mjs index 6876e0eb..7923aed0 100644 --- a/src/nationalarchives/components/tabs/tabs.mjs +++ b/src/nationalarchives/components/tabs/tabs.mjs @@ -21,6 +21,7 @@ export class Tabs { } this.sticky = this.$module.classList.contains("tna-tabs--sticky"); + this.noDefault = this.$module.classList.contains("tna-tabs--no-default"); const startingTarget = window.location.hash.replace(/^#/, ""); const doesStartingTargetExist = [...this.$tabItems].some( @@ -38,12 +39,16 @@ export class Tabs { `${$tabItem.getAttribute("id")}-tab`, ); $tabItem.setAttribute("tabindex", "0"); - if ( - (doesStartingTargetExist && - $tabItem.getAttribute("id") !== startingTarget) || - (!doesStartingTargetExist && index > 0) - ) { + if (this.noDefault) { $tabItem.setAttribute("hidden", true); + } else { + if ( + (doesStartingTargetExist && + $tabItem.getAttribute("id") !== startingTarget) || + (!doesStartingTargetExist && index > 0) + ) { + $tabItem.setAttribute("hidden", true); + } } }); @@ -74,17 +79,21 @@ export class Tabs { ); this.$tabListItemLinks.forEach(($tabListItemLink, index) => { - if ( - (startingTarget && - $tabListItemLink.getAttribute("aria-controls") === - `${startingTarget}`) || - (!startingTarget && index === 0) - ) { - $tabListItemLink.classList.add("tna-tabs__list-item-link--selected"); - $tabListItemLink.setAttribute("aria-selected", true); - $tabListItemLink.setAttribute("tabindex", "0"); - } else { + if (this.noDefault) { $tabListItemLink.setAttribute("aria-selected", false); + } else { + if ( + (startingTarget && + $tabListItemLink.getAttribute("aria-controls") === + `${startingTarget}`) || + (!startingTarget && index === 0) + ) { + $tabListItemLink.classList.add("tna-tabs__list-item-link--selected"); + $tabListItemLink.setAttribute("aria-selected", true); + $tabListItemLink.setAttribute("tabindex", "0"); + } else { + $tabListItemLink.setAttribute("aria-selected", false); + } } $tabListItemLink.addEventListener( diff --git a/src/nationalarchives/components/tabs/tabs.scss b/src/nationalarchives/components/tabs/tabs.scss index 1973e5ec..f455e7e1 100644 --- a/src/nationalarchives/components/tabs/tabs.scss +++ b/src/nationalarchives/components/tabs/tabs.scss @@ -131,6 +131,10 @@ } } + .tna-template--js-enabled &--no-default &__item[hidden] { + display: none; + } + @include colour.on-high-contrast { &__list-item-link { padding-right: 1rem; diff --git a/src/nationalarchives/components/tabs/tabs.stories.js b/src/nationalarchives/components/tabs/tabs.stories.js index c97666e0..0bc0eed8 100644 --- a/src/nationalarchives/components/tabs/tabs.stories.js +++ b/src/nationalarchives/components/tabs/tabs.stories.js @@ -7,6 +7,7 @@ const argTypes = { title: { control: "text" }, items: { control: "object" }, sticky: { control: "boolean" }, + noDefault: { control: "boolean" }, classes: { control: "text" }, attributes: { control: "object" }, }; @@ -22,12 +23,13 @@ export default { argTypes, }; -const Template = ({ title, items, sticky, classes, attributes }) => +const Template = ({ title, items, sticky, noDefault, classes, attributes }) => Tabs({ params: { title, items, sticky, + noDefault, classes, attributes, }, @@ -56,6 +58,154 @@ Standard.args = { classes: "tna-tabs--demo", }; +export const NoDefault = Template.bind({}); +NoDefault.args = { + title: "Example tabs", + items: [ + { + id: "unique-id-a", + title: "Alpha section", + body: '

Alpha title

Lorem ipsum

', + }, + { + id: "unique-id-b", + title: "Beta section", + body: '

Beta title

Lorem ipsum

', + }, + { + id: "unique-id-c", + title: "Gamma section", + body: '

Gamma title

Lorem ipsum

', + }, + ], + noDefault: true, + classes: "tna-tabs--demo", +}; +// NoDefault.play = async ({ args, canvasElement, step }) => { +// await new Promise((r) => setTimeout(r, 100)); + +// const canvas = within(canvasElement); + +// const tablist = canvas.getByRole("tablist"); +// const [buttonA, buttonB, buttonC] = args.items.map((item) => +// canvas.getByText(item.title), +// ); +// const [sectionA, sectionB, sectionC] = args.items.map((item) => +// canvasElement.querySelector(`#${item.id}`), +// ); + +// const expectButtonToBeCurrent = async (button) => { +// await expect(button).toHaveAttribute("tabindex", "0"); +// await expect(button).toHaveAttribute("aria-selected", "true"); +// await expect(button).toHaveFocus(); +// }; + +// const expectButtonNotToBeCurrent = async (button) => { +// await expect(button).toHaveAttribute("tabindex", "-1"); +// await expect(button).toHaveAttribute("aria-selected", "false"); +// }; + +// const expectSectionToBeCurrent = async (section) => { +// await expect(section).toBeVisible(); +// await expect(section).toHaveAttribute("tabindex", "0"); +// }; + +// const expectSectionNotToBeCurrent = async (section) => { +// await expect(section).not.toBeVisible(); +// await expect(section).toHaveAttribute("tabindex", "-1"); +// }; + +// const expectButtonAndSectionAToBeCurrent = async (section) => { +// await step("Test tab buttons", async () => { +// await expectButtonToBeCurrent(buttonA); +// await expectButtonNotToBeCurrent(buttonB); +// await expectButtonNotToBeCurrent(buttonC); +// }); + +// await step("Test tab sections", async () => { +// await expectSectionToBeCurrent(sectionA); +// await expectSectionNotToBeCurrent(sectionB); +// await expectSectionNotToBeCurrent(sectionC); +// }); +// }; + +// const expectButtonAndSectionBToBeCurrent = async (section) => { +// await step("Test tab buttons", async () => { +// await expectButtonNotToBeCurrent(buttonA); +// await expectButtonToBeCurrent(buttonB); +// await expectButtonNotToBeCurrent(buttonC); +// }); + +// await step("Test tab sections", async () => { +// await expectSectionNotToBeCurrent(sectionA); +// await expectSectionToBeCurrent(sectionB); +// await expectSectionNotToBeCurrent(sectionC); +// }); +// }; + +// const expectButtonAndSectionCToBeCurrent = async (section) => { +// await step("Test tab buttons", async () => { +// await expectButtonNotToBeCurrent(buttonA); +// await expectButtonNotToBeCurrent(buttonB); +// await expectButtonToBeCurrent(buttonC); +// }); + +// await step("Test tab sections", async () => { +// await expectSectionNotToBeCurrent(sectionA); +// await expectSectionNotToBeCurrent(sectionB); +// await expectSectionToBeCurrent(sectionC); +// }); +// }; + +// await step("Initial load", async () => { +// await step("Test tab sections", async () => { +// await expect(sectionA).not.toBeVisible(); +// await expect(sectionA).toHaveAttribute("id", args.items[0].id); +// await expect(sectionA).toHaveAttribute("role", "tabpanel"); +// await expect(sectionA).toHaveAttribute( +// "aria-labelledby", +// `${args.items[0].id}-tab`, +// ); +// await expect(sectionA).toHaveAttribute("tabindex", "0"); + +// await expect(sectionB).not.toBeVisible(); +// await expect(sectionB).toHaveAttribute("id", args.items[1].id); +// await expect(sectionB).toHaveAttribute("role", "tabpanel"); +// await expect(sectionB).toHaveAttribute( +// "aria-labelledby", +// `${args.items[1].id}-tab`, +// ); +// await expect(sectionB).toHaveAttribute("tabindex", "0"); + +// await expect(sectionC).not.toBeVisible(); +// await expect(sectionC).toHaveAttribute("id", args.items[2].id); +// await expect(sectionC).toHaveAttribute("role", "tabpanel"); +// await expect(sectionC).toHaveAttribute( +// "aria-labelledby", +// `${args.items[2].id}-tab`, +// ); +// await expect(sectionC).toHaveAttribute("tabindex", "0"); +// }); +// }); + +// await userEvent.click(buttonA); + +// await step("First tab", async () => { +// await userEvent.click(buttonA); +// await expectButtonAndSectionAToBeCurrent(); +// }); + +// await step("Second tab", async () => { +// await userEvent.click(buttonB); +// await expectButtonAndSectionBToBeCurrent(); +// }); + +// await step("Third tab", async () => { +// await userEvent.click(buttonC); +// await expectButtonAndSectionCToBeCurrent(); +// }); +// }; + export const Test = Template.bind({}); Test.args = { title: "Example tabs", diff --git a/src/nationalarchives/components/tabs/template.njk b/src/nationalarchives/components/tabs/template.njk index a5271e3b..28823d95 100644 --- a/src/nationalarchives/components/tabs/template.njk +++ b/src/nationalarchives/components/tabs/template.njk @@ -1,4 +1,7 @@ {%- set containerClasses = [params.classes] if params.classes else [] -%} +{%- if params.noDefault -%} + {%- set containerClasses = containerClasses.concat('tna-tabs--no-default') -%} +{%- endif -%} {%- if params.sticky -%} {%- set containerClasses = containerClasses.concat('tna-tabs--sticky') -%} {%- endif -%}