@windstream/react-shared-components 0.2.75 → 0.2.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/contentful/index.d.ts +2 -0
- package/dist/contentful/index.esm.js +1 -1
- package/dist/contentful/index.esm.js.map +1 -1
- package/dist/contentful/index.js +2 -2
- package/dist/contentful/index.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/animation-wrapper/index.tsx +11 -1
- package/src/contentful/blocks/award-marquee/AwardMarquee.stories.tsx +9 -1
- package/src/contentful/blocks/award-marquee/index.test.tsx +70 -4
- package/src/contentful/blocks/award-marquee/index.tsx +17 -18
- package/src/contentful/blocks/award-marquee/types.ts +2 -0
- package/src/contentful/blocks/button/index.tsx +4 -3
- package/src/contentful/blocks/floating-banner/index.tsx +1 -1
package/package.json
CHANGED
|
@@ -78,7 +78,17 @@ export function AnimationWrapper({
|
|
|
78
78
|
transition,
|
|
79
79
|
...motionProps
|
|
80
80
|
}: AnimationWrapperProps) {
|
|
81
|
-
|
|
81
|
+
// useReducedMotion() reads window.matchMedia, which is unavailable during
|
|
82
|
+
// SSR (always resolves falsy server-side). Gating on it immediately would
|
|
83
|
+
// make the client's first render diverge from the server-rendered markup
|
|
84
|
+
// on any machine with OS-level "Reduce motion" enabled, causing a
|
|
85
|
+
// hydration mismatch (extra framer-motion attributes like tabIndex) that
|
|
86
|
+
// can force React to remount the whole tree mid-click. Only honor it once
|
|
87
|
+
// mounted, so the first client render always matches SSR.
|
|
88
|
+
const [isMounted, setIsMounted] = React.useState(false);
|
|
89
|
+
React.useEffect(() => setIsMounted(true), []);
|
|
90
|
+
const reducedMotionPreferred = useReducedMotion();
|
|
91
|
+
const prefersReducedMotion = isMounted && reducedMotionPreferred;
|
|
82
92
|
|
|
83
93
|
const child = React.Children.only(children) as React.ReactElement<
|
|
84
94
|
Record<string, unknown>
|
|
@@ -13,7 +13,7 @@ const meta: Meta<typeof AwardMarquee> = {
|
|
|
13
13
|
page: DocsPage,
|
|
14
14
|
description: {
|
|
15
15
|
component:
|
|
16
|
-
"Server-rendered marquee of award badges. Animates when `scroll` is set; with 3 or fewer items it animates on mobile and falls back to a static row at the `sm` breakpoint (CSS-driven, no client-side JS).",
|
|
16
|
+
"Server-rendered marquee of award badges. Optional `heading` and `footerText` display gold text above and below the awards, with divider lines around the heading. Animates when `scroll` is set; with 3 or fewer items it animates on mobile and falls back to a static row at the `sm` breakpoint (CSS-driven, no client-side JS).",
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
19
|
},
|
|
@@ -48,3 +48,11 @@ export const CreamBackground: Story = {
|
|
|
48
48
|
backgroundColor: "cream",
|
|
49
49
|
},
|
|
50
50
|
};
|
|
51
|
+
|
|
52
|
+
export const WithHeadingAndFooter: Story = {
|
|
53
|
+
args: {
|
|
54
|
+
heading: "As seen on",
|
|
55
|
+
footerText: "And over 450+ news sites",
|
|
56
|
+
scroll: false,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
@@ -15,6 +15,70 @@ const buildItems = (count: number): AwardMarqueeItem[] =>
|
|
|
15
15
|
}));
|
|
16
16
|
|
|
17
17
|
describe("AwardMarquee", () => {
|
|
18
|
+
it.each([
|
|
19
|
+
{ scroll: false, count: 2 },
|
|
20
|
+
{ scroll: true, count: 5 },
|
|
21
|
+
{ scroll: true, count: 2 },
|
|
22
|
+
])(
|
|
23
|
+
"renders heading and footer once with $count items and scroll=$scroll",
|
|
24
|
+
({ scroll, count }) => {
|
|
25
|
+
const { container } = render(
|
|
26
|
+
<AwardMarquee
|
|
27
|
+
items={buildItems(count)}
|
|
28
|
+
scroll={scroll}
|
|
29
|
+
heading="As seen on"
|
|
30
|
+
footerText="And over 450+ news sites"
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const heading = screen.getByText("As seen on");
|
|
35
|
+
const footer = screen.getByText("And over 450+ news sites");
|
|
36
|
+
expect(screen.getAllByText("As seen on")).toHaveLength(1);
|
|
37
|
+
expect(screen.getAllByText("And over 450+ news sites")).toHaveLength(1);
|
|
38
|
+
expect(heading).toHaveClass("heading2");
|
|
39
|
+
expect(footer).toHaveClass("subheading3");
|
|
40
|
+
expect(heading).not.toHaveClass("text-xs");
|
|
41
|
+
expect(footer).not.toHaveClass("text-xs");
|
|
42
|
+
expect(container.querySelector("#Award-Marquee")?.firstElementChild).toBe(
|
|
43
|
+
heading
|
|
44
|
+
);
|
|
45
|
+
expect(container.querySelector("#Award-Marquee")?.lastElementChild).toBe(
|
|
46
|
+
footer
|
|
47
|
+
);
|
|
48
|
+
expect(screen.getAllByText("Award 0").length).toBeGreaterThan(0);
|
|
49
|
+
expect(screen.getAllByText("Description 0").length).toBeGreaterThan(0);
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
it("allows heading and footer to be set independently", () => {
|
|
54
|
+
const { rerender } = render(
|
|
55
|
+
<AwardMarquee items={buildItems(1)} heading="As seen on" />
|
|
56
|
+
);
|
|
57
|
+
expect(screen.getByText("As seen on")).toBeInTheDocument();
|
|
58
|
+
expect(
|
|
59
|
+
screen.queryByText("And over 450+ news sites")
|
|
60
|
+
).not.toBeInTheDocument();
|
|
61
|
+
|
|
62
|
+
rerender(
|
|
63
|
+
<AwardMarquee
|
|
64
|
+
items={buildItems(1)}
|
|
65
|
+
footerText="And over 450+ news sites"
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
expect(screen.queryByText("As seen on")).not.toBeInTheDocument();
|
|
69
|
+
expect(screen.getByText("And over 450+ news sites")).toBeInTheDocument();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("does not add text or divider lines when fields are empty or omitted", () => {
|
|
73
|
+
const { container, rerender } = render(
|
|
74
|
+
<AwardMarquee items={buildItems(1)} />
|
|
75
|
+
);
|
|
76
|
+
const originalMarkup = container.innerHTML;
|
|
77
|
+
rerender(<AwardMarquee items={buildItems(1)} heading="" footerText="" />);
|
|
78
|
+
expect(container.innerHTML).toBe(originalMarkup);
|
|
79
|
+
expect(container.querySelector('[aria-hidden="true"]')).toBeNull();
|
|
80
|
+
});
|
|
81
|
+
|
|
18
82
|
it("renders award items synchronously (SSR, no hydration gate)", () => {
|
|
19
83
|
render(<AwardMarquee items={buildItems(2)} />);
|
|
20
84
|
expect(screen.getByText("Award 0")).toBeInTheDocument();
|
|
@@ -31,9 +95,7 @@ describe("AwardMarquee", () => {
|
|
|
31
95
|
it("renders intrinsic icon dimensions when provided", () => {
|
|
32
96
|
const [item] = buildItems(1);
|
|
33
97
|
render(
|
|
34
|
-
<AwardMarquee
|
|
35
|
-
items={[{ ...item, iconWidth: 320, iconHeight: 180 }]}
|
|
36
|
-
/>
|
|
98
|
+
<AwardMarquee items={[{ ...item, iconWidth: 320, iconHeight: 180 }]} />
|
|
37
99
|
);
|
|
38
100
|
const icon = screen.getByAltText("Award 0");
|
|
39
101
|
expect(icon).toHaveAttribute("width", "320");
|
|
@@ -99,7 +161,11 @@ describe("AwardMarquee", () => {
|
|
|
99
161
|
).not.toBeNull();
|
|
100
162
|
|
|
101
163
|
const splitView = render(
|
|
102
|
-
<AwardMarquee
|
|
164
|
+
<AwardMarquee
|
|
165
|
+
items={buildItems(2)}
|
|
166
|
+
scroll={true}
|
|
167
|
+
anchorId="split-anchor"
|
|
168
|
+
/>
|
|
103
169
|
);
|
|
104
170
|
expect(splitView.container.querySelector("#split-anchor")).not.toBeNull();
|
|
105
171
|
});
|
|
@@ -55,6 +55,8 @@ const renderAwardItem = (
|
|
|
55
55
|
|
|
56
56
|
export const AwardMarquee: React.FC<AwardMarqueeProps> = ({
|
|
57
57
|
anchorId = "Award-Marquee",
|
|
58
|
+
heading,
|
|
59
|
+
footerText,
|
|
58
60
|
items = [],
|
|
59
61
|
speed = "medium",
|
|
60
62
|
scroll = false,
|
|
@@ -95,27 +97,24 @@ export const AwardMarquee: React.FC<AwardMarqueeProps> = ({
|
|
|
95
97
|
</div>
|
|
96
98
|
);
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<div id={anchorId} className={className}>
|
|
110
|
-
{animatedMarquee()}
|
|
111
|
-
</div>
|
|
112
|
-
);
|
|
100
|
+
const marquee = !scroll ? (
|
|
101
|
+
staticMarquee
|
|
102
|
+
) : scrollAvailable ? (
|
|
103
|
+
animatedMarquee()
|
|
104
|
+
) : (
|
|
105
|
+
<>
|
|
106
|
+
<div className="sm:hidden">{animatedMarquee()}</div>
|
|
107
|
+
<div className="hidden sm:block">{staticMarquee}</div>
|
|
108
|
+
</>
|
|
109
|
+
);
|
|
113
110
|
|
|
114
|
-
// Scroll with <=3 items: animate on mobile, static on >= sm (CSS-driven).
|
|
115
111
|
return (
|
|
116
112
|
<div id={anchorId} className={className}>
|
|
117
|
-
<
|
|
118
|
-
|
|
113
|
+
{heading && <p className="heading2 mb-4 px-4 text-center">{heading}</p>}
|
|
114
|
+
{marquee}
|
|
115
|
+
{footerText && (
|
|
116
|
+
<p className="subheading3 mt-4 px-4 text-center">{footerText}</p>
|
|
117
|
+
)}
|
|
119
118
|
</div>
|
|
120
119
|
);
|
|
121
120
|
};
|
|
@@ -52,7 +52,7 @@ export const Button: React.FC<ButtonProps> = props => {
|
|
|
52
52
|
animationType = DEFAULT_BUTTON_ANIMATION_TYPE,
|
|
53
53
|
...rest
|
|
54
54
|
} = props;
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
const isLeftIcon = buttonIconPosition === "left";
|
|
57
57
|
const matIconName = buttonIconName as IconName | undefined;
|
|
58
58
|
const brandButtonClassName = cx(
|
|
@@ -152,6 +152,7 @@ export const Button: React.FC<ButtonProps> = props => {
|
|
|
152
152
|
<BrandButton
|
|
153
153
|
as="a"
|
|
154
154
|
href={resolvedHref}
|
|
155
|
+
target={target}
|
|
155
156
|
id={anchorId}
|
|
156
157
|
variant={buttonVariant}
|
|
157
158
|
text={buttonLabel}
|
|
@@ -170,6 +171,7 @@ export const Button: React.FC<ButtonProps> = props => {
|
|
|
170
171
|
<BrandButton
|
|
171
172
|
id={anchorId}
|
|
172
173
|
variant={buttonVariant}
|
|
174
|
+
target={target}
|
|
173
175
|
text={buttonLabel}
|
|
174
176
|
label={buttonPrefix}
|
|
175
177
|
fullWidth={fullWidth}
|
|
@@ -192,8 +194,7 @@ export const Button: React.FC<ButtonProps> = props => {
|
|
|
192
194
|
target={target}
|
|
193
195
|
variant={linkVariant || "default"}
|
|
194
196
|
className={cx(
|
|
195
|
-
matIconName &&
|
|
196
|
-
"label1 inline-flex items-center gap-2 no-underline",
|
|
197
|
+
matIconName && "label1 inline-flex items-center gap-2 no-underline",
|
|
197
198
|
textVariantClasses[buttonVariant],
|
|
198
199
|
linkClassName
|
|
199
200
|
)}
|
|
@@ -75,8 +75,8 @@ export const FloatingBanner: React.FC<FloatingBannerProps> = ({
|
|
|
75
75
|
{cta && (
|
|
76
76
|
<div className="mt-5 flex flex-shrink-0 justify-center lg:mt-0 lg:items-center">
|
|
77
77
|
<Button
|
|
78
|
-
{...cta}
|
|
79
78
|
buttonVariant="secondary"
|
|
79
|
+
{...cta}
|
|
80
80
|
size={{ base: "medium", md: "large" }}
|
|
81
81
|
></Button>
|
|
82
82
|
</div>
|