@windstream/react-shared-components 0.2.21 → 0.2.22

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windstream/react-shared-components",
3
- "version": "0.2.21",
3
+ "version": "0.2.22",
4
4
  "type": "module",
5
5
  "description": "Shared React components for Kinetic applications",
6
6
  "main": "dist/index.js",
@@ -1,7 +1,11 @@
1
1
  "use client";
2
2
 
3
3
  import React from "react";
4
- import { AnimationType, AnimationWrapperProps } from "./types";
4
+ import {
5
+ AnimationType,
6
+ AnimationWrapperProps,
7
+ DEFAULT_BUTTON_ANIMATION_TYPE,
8
+ } from "./types";
5
9
  import { motion, useReducedMotion } from "framer-motion";
6
10
 
7
11
  interface AnimationPreset {
@@ -66,7 +70,7 @@ function mergePresets(animationType?: AnimationType | AnimationType[]) {
66
70
 
67
71
  export function AnimationWrapper({
68
72
  children,
69
- animationType,
73
+ animationType = DEFAULT_BUTTON_ANIMATION_TYPE,
70
74
  // default to true for backward compatibility
71
75
  disableAnimation = true,
72
76
  whileHover,
@@ -84,6 +88,7 @@ export function AnimationWrapper({
84
88
  return child;
85
89
  }
86
90
 
91
+ // Use the provided animationType, falling back to the centralized default.
87
92
  const preset = mergePresets(animationType);
88
93
 
89
94
  const mergedWhileHover = preset
@@ -132,4 +137,5 @@ export function AnimationWrapper({
132
137
 
133
138
  AnimationWrapper.displayName = "AnimationWrapper";
134
139
 
140
+ export { DEFAULT_BUTTON_ANIMATION_TYPE };
135
141
  export type { AnimationWrapperProps, AnimationType };
@@ -9,6 +9,12 @@ export type AnimationType =
9
9
  | "grow"
10
10
  | "pop";
11
11
 
12
+ /**
13
+ * Single source of truth for the default button animation type.
14
+ * Change this value to update the default button animation across the app.
15
+ */
16
+ export const DEFAULT_BUTTON_ANIMATION_TYPE: AnimationType = "pop";
17
+
12
18
  export interface AnimationWrapperProps
13
19
  extends Omit<HTMLMotionProps<"div">, "children"> {
14
20
  children: ReactElement;
@@ -3,7 +3,10 @@
3
3
  import React, { forwardRef } from "react";
4
4
  import { getLabelSizeBasedOnButtonSize, sizeToClassNames } from "./helpers";
5
5
 
6
- import { AnimationWrapper } from "@shared/components/animation-wrapper";
6
+ import {
7
+ AnimationWrapper,
8
+ DEFAULT_BUTTON_ANIMATION_TYPE,
9
+ } from "@shared/components/animation-wrapper";
7
10
  import {
8
11
  ButtonProps,
9
12
  ButtonVariantsT,
@@ -29,9 +32,15 @@ export const BrandButton = forwardRef<
29
32
  iconName,
30
33
  iconSize = 24,
31
34
  iconFill = 0,
35
+ // `disableAnimation` is intentionally NOT defaulted to `false` here.
36
+ // Animations are opt-in by design: when this prop is left undefined,
37
+ // AnimationWrapper falls back to its own default of `true` (animation
38
+ // disabled). This keeps behavior consistent with the CMS contract where
39
+ // an unset value means "no animation". Animation runs only when a caller
40
+ // (or Contentful) explicitly passes `disableAnimation={false}`.
41
+ disableAnimation,
42
+ animationType = DEFAULT_BUTTON_ANIMATION_TYPE,
32
43
  as = "button",
33
- disableAnimation = true,
34
- animationType,
35
44
  ...props
36
45
  },
37
46
  ref
@@ -1,7 +1,10 @@
1
1
  import React from "react";
2
2
  import { ButtonProps } from "./types";
3
3
 
4
- import { AnimationWrapper } from "@shared/components/animation-wrapper";
4
+ import {
5
+ AnimationWrapper,
6
+ DEFAULT_BUTTON_ANIMATION_TYPE,
7
+ } from "@shared/components/animation-wrapper";
5
8
  import { BrandButton, ButtonVariantsT } from "@shared/components/brand-button";
6
9
  import { Button as CoreButton } from "@shared/components/button";
7
10
  import { Link } from "@shared/components/link";
@@ -35,8 +38,8 @@ export const Button: React.FC<ButtonProps> = props => {
35
38
  size,
36
39
  iconSize = 24,
37
40
  preserveQueryParameters,
38
- disableAnimation = true,
39
- animationType,
41
+ disableAnimation,
42
+ animationType = DEFAULT_BUTTON_ANIMATION_TYPE,
40
43
  ...rest
41
44
  } = props;
42
45
 
@@ -51,6 +54,12 @@ export const Button: React.FC<ButtonProps> = props => {
51
54
  cta: { ...props },
52
55
  });
53
56
  return React.isValidElement(checkPlansNode) ? (
57
+ // `disableAnimation` is passed through as-is (not defaulted to `false`).
58
+ // Animations are opt-in: when unset, AnimationWrapper falls back to its
59
+ // default of `true` (disabled), matching the CMS contract where an unset
60
+ // value means "no animation". This stays consistent with the other
61
+ // branches in this block. Animation runs only when a caller explicitly
62
+ // passes `disableAnimation={false}`.
54
63
  <AnimationWrapper
55
64
  disableAnimation={disableAnimation}
56
65
  animationType={animationType}
@@ -7,11 +7,21 @@ import { fireEvent, render, screen } from "@testing-library/react";
7
7
  jest.mock("../button", () => ({
8
8
  __esModule: true,
9
9
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
10
- Button: ({ buttonLabel, ...rest }: any) => (
11
- <button data-testid="cta-button">{buttonLabel || "CTA"}</button>
10
+ Button: ({ buttonLabel, disableAnimation, ...rest }: any) => (
11
+ <button
12
+ data-testid="cta-button"
13
+ data-disable-animation={String(disableAnimation)}
14
+ >
15
+ {buttonLabel || "CTA"}
16
+ </button>
12
17
  ),
13
- default: ({ buttonLabel }: any) => (
14
- <button data-testid="cta-button">{buttonLabel || "CTA"}</button>
18
+ default: ({ buttonLabel, disableAnimation }: any) => (
19
+ <button
20
+ data-testid="cta-button"
21
+ data-disable-animation={String(disableAnimation)}
22
+ >
23
+ {buttonLabel || "CTA"}
24
+ </button>
15
25
  ),
16
26
  }));
17
27
 
@@ -245,6 +255,37 @@ describe("ImagePromoBar", () => {
245
255
  const { container } = render(<ImagePromoBar {...defaultProps} />);
246
256
  expect(container.querySelector(".primary-cta")).not.toBeInTheDocument();
247
257
  });
258
+
259
+ it("keeps disableAnimation true on both CTAs by default", () => {
260
+ render(
261
+ <ImagePromoBar
262
+ {...defaultProps}
263
+ cta={{ buttonLabel: "Primary" }}
264
+ secondaryCta={{ buttonLabel: "Secondary" }}
265
+ />
266
+ );
267
+ const buttons = screen.getAllByTestId("cta-button");
268
+ expect(buttons).toHaveLength(2);
269
+ buttons.forEach(button => {
270
+ expect(button).toHaveAttribute("data-disable-animation", "true");
271
+ });
272
+ });
273
+
274
+ it("passes disableAnimation false to both CTAs when enableAnimation is true", () => {
275
+ render(
276
+ <ImagePromoBar
277
+ {...defaultProps}
278
+ cta={{ buttonLabel: "Primary" }}
279
+ secondaryCta={{ buttonLabel: "Secondary" }}
280
+ enableAnimation={true}
281
+ />
282
+ );
283
+ const buttons = screen.getAllByTestId("cta-button");
284
+ expect(buttons).toHaveLength(2);
285
+ buttons.forEach(button => {
286
+ expect(button).toHaveAttribute("data-disable-animation", "false");
287
+ });
288
+ });
248
289
  });
249
290
 
250
291
  describe("Image", () => {
@@ -36,6 +36,7 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
36
36
  onModalButtonClick,
37
37
  onClick,
38
38
  renderCheckPlans,
39
+ enableAnimation = false,
39
40
  }) => {
40
41
  const [activeVideo, setActiveVideo] = useState("");
41
42
  const [isHovered, setIsHovered] = useState(false);
@@ -138,11 +139,12 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
138
139
  )}
139
140
  {/* CTAs and Disclaimers */}
140
141
  {(cta || secondaryCta) && (
141
- <div className="flex w-full flex-col gap-3 xl:flex-row">
142
+ <div className="flex w-full flex-col gap-4 xl:flex-row">
142
143
  {cta && (
143
144
  <div className="primary-cta w-full xl:w-auto xl:shrink-0">
144
145
  <Button
145
146
  {...cta}
147
+ disableAnimation={!enableAnimation}
146
148
  fullWidth={true}
147
149
  size={{ base: "large" }}
148
150
  renderCheckPlans={renderCheckPlans}
@@ -155,6 +157,7 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
155
157
  <div className="secondary-cta w-full xl:w-auto xl:shrink-0">
156
158
  <Button
157
159
  {...secondaryCta}
160
+ disableAnimation={!enableAnimation}
158
161
  fullWidth={true}
159
162
  size={{ base: "large" }}
160
163
  renderCheckPlans={renderCheckPlans}
@@ -36,6 +36,7 @@ export type ImagePromoBarProps = {
36
36
  videoLink: VideoLinkProps;
37
37
  maxWidth?: boolean;
38
38
  color: "light" | "dark";
39
+ enableAnimation?: boolean;
39
40
  onModalButtonClick?: (id?: string) => void;
40
41
  onClick?: (event: React.MouseEvent, source: "primary" | "secondary") => void;
41
42
  renderCheckPlans?: (overrides?: CheckPlansProps) => React.ReactNode;
package/src/index.ts CHANGED
@@ -84,6 +84,7 @@ export { Tooltip } from "./components/tooltip";
84
84
  export type { ToolTipProps } from "./components/tooltip";
85
85
 
86
86
  export { AnimationWrapper } from "./components/animation-wrapper";
87
+ export { DEFAULT_BUTTON_ANIMATION_TYPE } from "./components/animation-wrapper";
87
88
  export type {
88
89
  AnimationWrapperProps,
89
90
  AnimationType,