From 46315feaf35882d8272c9a3288e2722b0c971ff6 Mon Sep 17 00:00:00 2001
From: Scott Williams <5209283+scott-williams-az@users.noreply.github.com>
Date: Wed, 2 Jul 2025 10:26:11 -0700
Subject: [PATCH 1/2] fix(unity-bootstrap-theme): fix long accordion scroll
issue
---
.../unity-bootstrap-theme/src/js/collapse.js | 23 +
.../src/js/unity-bootstrap.js | 2 +
.../src/scss/extends/_cards.scss | 7 +
.../accordion/accordion.examples.stories.js | 477 +-----------------
.../accordion/accordion.templates.stories.js | 91 ----
.../stories/atoms/accordion/accordion.test.js | 28 -
.../accordion/accordion-basics.stories.mdx | 131 -----
shared/utils/index.d.ts | 1 +
shared/utils/index.js | 1 +
shared/utils/scroll-helper.d.ts | 1 +
shared/utils/scroll-helper.js | 17 +
11 files changed, 54 insertions(+), 725 deletions(-)
create mode 100644 packages/unity-bootstrap-theme/src/js/collapse.js
delete mode 100644 packages/unity-bootstrap-theme/stories/atoms/accordion/accordion.templates.stories.js
delete mode 100644 packages/unity-bootstrap-theme/stories/atoms/accordion/accordion.test.js
delete mode 100644 packages/unity-bootstrap-theme/stories/docs/accordion/accordion-basics.stories.mdx
create mode 100644 shared/utils/scroll-helper.d.ts
create mode 100644 shared/utils/scroll-helper.js
diff --git a/packages/unity-bootstrap-theme/src/js/collapse.js b/packages/unity-bootstrap-theme/src/js/collapse.js
new file mode 100644
index 0000000000..f13aa19984
--- /dev/null
+++ b/packages/unity-bootstrap-theme/src/js/collapse.js
@@ -0,0 +1,23 @@
+import { adjustShrinkingElementIfAboveViewport } from "@shared";
+
+import { EventHandler } from "./bootstrap-helper";
+
+function initCollapse() {
+ function accordionClick({target}) {
+ const targetHref = target.getAttribute("href");
+ if (target.getAttribute("data-bs-toggle") !== "collapse" || !targetHref || !targetHref.includes("#")) {
+ return; // Exit the function - not a collapse link
+ }
+
+ /**
+ * Find the first accordion that is currently collapsing or expanding
+ * Function will determine if scroll adjustment is needed
+ */
+ adjustShrinkingElementIfAboveViewport(document.querySelector(".collapsing"));
+ }
+ EventHandler.on(document, "click.uds.collapse", accordionClick);
+}
+
+EventHandler.on(window, "load.uds.collapse", initCollapse);
+
+export { initCollapse };
diff --git a/packages/unity-bootstrap-theme/src/js/unity-bootstrap.js b/packages/unity-bootstrap-theme/src/js/unity-bootstrap.js
index b3783e3931..a74bdc8747 100644
--- a/packages/unity-bootstrap-theme/src/js/unity-bootstrap.js
+++ b/packages/unity-bootstrap-theme/src/js/unity-bootstrap.js
@@ -5,6 +5,7 @@ import { initCalendar } from "./calendar.js";
import { initCardBodies } from "./card-bodies.js";
import { initRankingCard } from "./card-ranking.js";
import { initChart } from "./charts-and-graphs.js";
+import { initCollapse } from "./collapse.js";
import { initDataLayer } from "./data-layer.js";
import { initGlobalHeader } from "./global-header.js";
import { initHeroesVideo } from "./heroes-video.js";
@@ -19,6 +20,7 @@ const unityBootstrap = {
initAnchorMenu,
initBlockquoteAnimation,
initCalendar,
+ initCollapse,
initChart,
initDataLayer,
initFixedTable,
diff --git a/packages/unity-bootstrap-theme/src/scss/extends/_cards.scss b/packages/unity-bootstrap-theme/src/scss/extends/_cards.scss
index d165381a1c..165321cce5 100644
--- a/packages/unity-bootstrap-theme/src/scss/extends/_cards.scss
+++ b/packages/unity-bootstrap-theme/src/scss/extends/_cards.scss
@@ -691,6 +691,13 @@ Cards - Table of Contents
7. Accordion
--------------------------------------------------------------------*/
.accordion {
+ &:focus {
+ outline: none!important;
+ box-shadow: 0 0 0 1px $uds-color-base-gray-7 !important;
+ border: 1px solid $uds-color-base-gray-7 !important;
+ margin: 4px;
+ width: -webkit-fill-available;
+ }
.accordion-item {
border-bottom: 1px solid $uds-color-base-gray-3;
border-top: 1px solid $uds-color-base-gray-3;
diff --git a/packages/unity-bootstrap-theme/stories/atoms/accordion/accordion.examples.stories.js b/packages/unity-bootstrap-theme/stories/atoms/accordion/accordion.examples.stories.js
index 78db067aba..c246c9b023 100644
--- a/packages/unity-bootstrap-theme/stories/atoms/accordion/accordion.examples.stories.js
+++ b/packages/unity-bootstrap-theme/stories/atoms/accordion/accordion.examples.stories.js
@@ -8,484 +8,11 @@ export default {
parameters: { controls: { disable: true } },
};
-export const FoldableCardDefaultOpen = () =>
+export const Default = () =>
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor
-
- This uses the
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
- Aenean commodo ligula eget dolor. Aenean massa. Cum sociis
- natoque penatibus et magnis dis parturient montes, nascetur
- ridiculus mus. Donec quam felis, ultricies nec, pellentesque
- eu, pretium quis, sem.
-
- This uses the
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
- Aenean commodo ligula eget dolor. Aenean massa. Cum sociis
- natoque penatibus et magnis dis parturient montes, nascetur
- ridiculus mus. Donec quam felis, ultricies nec, pellentesque
- eu, pretium quis, sem.
-
-
-
- This is a quaternary headline
-
- This is a level five headline. There's a fancy word for that too.
-
-
-
-
- Quatrenary Headline
-
- This is a level five headline. There's a fancy word for that too.
-
-
-
-
-
- This is a level five headline. There's a fancy word for that too.
-
-
-
-
-
- This is a level five headline. There's a fancy word for that too.
-
-
-
-
- Quatrenary Headline
-
- This is a level five headline. There's a fancy word for that too.
-
-
-
-
- Quatrenary Headline
-
- This is a level five headline. There's a fancy word for that too.
-
-
-
-
-
- This is a level five headline. There's a fancy word for that too.
-
-
-
-
- .desktop-disable-lg
class.
-
-
-
- .desktop-disable-xl
class.
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim - ad minim veniam, quis nostrud -
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim - ad minim veniam, quis nostrud -
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eiusmod tempor -
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod - tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim - veniam, quis nostrud -
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod - tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim - veniam, quis nostrud -
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod - tempor -
-{getLoremSentences(12, i++)}
+{getLoremSentences(22, i++)}
+{getLoremSentences(10, i++)}
+{getLoremSentences(35, i++)}
+{getLoremSentences(22, i++)}
+{getLoremSentences(12, i++)}
+{getLoremSentences(42, i++)}
+{getLoremSentences(19, i++)}
+{getLoremSentences(12, i++)}
+{getLoremSentences(10, i++)}
+{getLoremSentences(35, i++)}
+{getLoremSentences(22, i++)}
+{getLoremSentences(12, i++)}
+{getLoremSentences(42, i++)}
+{getLoremSentences(19, i++)}
+{getLoremSentences(12, i++)}
+{getLoremSentences(10, i++)}
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
", + }, + }, + { + content: { + header: "Accordion Card 2, Large Content", + body: `${getLoremSentences(35, i++)}
+${getLoremSentences(22, i++)}
+${getLoremSentences(12, i++)}
+${getLoremSentences(42, i++)}
+${getLoremSentences(19, i++)}
+${getLoremSentences(12, i++)}
+${getLoremSentences(10, i++)}
+${getLoremSentences(35, i++)}
+${getLoremSentences(22, i++)}
+${getLoremSentences(12, i++)}
+${getLoremSentences(42, i++)}
+${getLoremSentences(19, i++)}
+${getLoremSentences(12, i++)}
+${getLoremSentences(10, i++)}
+ `, + }, + }, + { + content: { + header: "Accordion Card 3", + body: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
+${getLoremSentences(35, i++)}
+${getLoremSentences(22, i++)}
`, + }, + }, + ], + openedCard: 2, + }, +}; export const ColorCombinations = Template.bind({}); ColorCombinations.args = { cards: [ diff --git a/packages/unity-react-core/src/components/Accordion/AccordionCard/AccordionCard.jsx b/packages/unity-react-core/src/components/Accordion/AccordionCard/AccordionCard.jsx index 7953aca0ce..118ab6118b 100644 --- a/packages/unity-react-core/src/components/Accordion/AccordionCard/AccordionCard.jsx +++ b/packages/unity-react-core/src/components/Accordion/AccordionCard/AccordionCard.jsx @@ -1,7 +1,7 @@ // @ts-check import classNames from "classnames"; import PropTypes from "prop-types"; -import React from "react"; +import React, { forwardRef } from "react"; import { sanitizeDangerousMarkup } from "../../../../../../shared"; import { accordionCardPropTypes } from "../../../core/models/shared-prop-types"; @@ -11,77 +11,104 @@ import { GaEventWrapper } from "../../GaEventWrapper/GaEventWrapper"; * @typedef {import('../../../core/types/shared-types').AccordionCardItemProps} AccordionCardItemProps */ +/** + * @typedef {import('../../../core/types/shared-types').AccordionCard} AccordionCard + */ + /** * @param {AccordionCardItemProps} props * @returns {JSX.Element} * @ignore */ -export const AccordionCard = ({ id, item, openCard, onClick, gaData }) => { - const isOpen = id === openCard; - /** - * event to open accordion is happening on the closed accordion - * so the action seem backwards but: - * open will send an event with action "close" - * close will send an event with action "open" - * */ +export const AccordionCard = forwardRef( + ( + /** @type {AccordionCardItemProps} */ { + id, + parentId, + /** @type {AccordionCard} */ item, + openCard, + onClick, + gaData, + }, + ref + ) => { + const isOpen = id === openCard; + /** + * event to open accordion is happening on the closed accordion + * so the action seem backwards but: + * open will send an event with action "close" + * close will send an event with action "open" + * */ - const gaAction = !isOpen ? "close" : "open"; - return ( -