@windstream/react-shared-components 0.2.58 → 0.2.60

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.58",
3
+ "version": "0.2.60",
4
4
  "type": "module",
5
5
  "description": "Shared React components for Kinetic applications",
6
6
  "main": "dist/index.js",
@@ -28,6 +28,26 @@ describe("AwardMarquee", () => {
28
28
  expect(icon.src).toContain("icon-0.png");
29
29
  });
30
30
 
31
+ it("renders intrinsic icon dimensions when provided", () => {
32
+ const [item] = buildItems(1);
33
+ render(
34
+ <AwardMarquee
35
+ items={[{ ...item, iconWidth: 320, iconHeight: 180 }]}
36
+ />
37
+ );
38
+ const icon = screen.getByAltText("Award 0");
39
+ expect(icon).toHaveAttribute("width", "320");
40
+ expect(icon).toHaveAttribute("height", "180");
41
+ expect(icon).toHaveClass("w-[200px]");
42
+ });
43
+
44
+ it("omits intrinsic icon dimensions when unavailable", () => {
45
+ render(<AwardMarquee items={buildItems(1)} />);
46
+ const icon = screen.getByAltText("Award 0");
47
+ expect(icon).not.toHaveAttribute("width");
48
+ expect(icon).not.toHaveAttribute("height");
49
+ });
50
+
31
51
  it("renders a static (non-animated) list when scroll is disabled", () => {
32
52
  const { container } = render(
33
53
  <AwardMarquee items={buildItems(5)} scroll={false} />
@@ -21,14 +21,27 @@ const BACKGROUND_CLASS: Record<AwardMarqueeBackground, string> = {
21
21
  };
22
22
 
23
23
  const renderAwardItem = (
24
- { iconUrl, iconAlt, title, description }: AwardMarqueeItem,
24
+ {
25
+ iconUrl,
26
+ iconAlt,
27
+ iconWidth,
28
+ iconHeight,
29
+ title,
30
+ description,
31
+ }: AwardMarqueeItem,
25
32
  key: React.Key
26
33
  ) => (
27
34
  <div
28
35
  key={key}
29
36
  className="ml-4 flex w-auto flex-shrink-0 items-center whitespace-nowrap"
30
37
  >
31
- <img src={iconUrl} alt={iconAlt ?? ""} width={200} />
38
+ <img
39
+ src={iconUrl}
40
+ alt={iconAlt ?? ""}
41
+ width={iconWidth}
42
+ height={iconHeight}
43
+ className="w-[200px]"
44
+ />
32
45
  <div className="w-[250px] text-center">
33
46
  <p className="flex justify-center text-center text-body3 font-bold">
34
47
  {title}
@@ -5,6 +5,8 @@ export type AwardMarqueeBackground = "cream" | "gray90" | "white";
5
5
  export type AwardMarqueeItem = {
6
6
  iconUrl: string;
7
7
  iconAlt?: string;
8
+ iconWidth?: number;
9
+ iconHeight?: number;
8
10
  title?: string;
9
11
  description?: string;
10
12
  };
@@ -13,6 +13,7 @@ import type { IconName } from "@shared/components/material-icon/types";
13
13
  import { Text } from "@shared/components/text";
14
14
  import { cx } from "@shared/utils";
15
15
  import { buildPreservedQueryHref } from "@shared/utils/utm";
16
+ import { redirectToWifiGrantUrl } from "@shared/utils/wifi-grant-redirect";
16
17
 
17
18
  const textVariantClasses: Record<string, string> = {
18
19
  primary_brand: "text-text-brand",
@@ -114,13 +115,22 @@ export const Button: React.FC<ButtonProps> = props => {
114
115
  }
115
116
  }
116
117
 
118
+ // Self-contained: reads base_grant_url/user_continue_url and redirects, no host wiring needed.
119
+ function handleWifiGrantRedirect() {
120
+ if (preDefinedFunctionExecution === "wifi grant redirect") {
121
+ redirectToWifiGrantUrl();
122
+ }
123
+ }
124
+
117
125
  function linkClick(event: React.MouseEvent<HTMLAnchorElement>) {
118
126
  handleModalClick(event);
127
+ handleWifiGrantRedirect();
119
128
  onClick?.(event);
120
129
  }
121
130
 
122
131
  function buttonClick(event: React.MouseEvent<HTMLButtonElement>) {
123
132
  handleModalClick(event);
133
+ handleWifiGrantRedirect();
124
134
  onClick?.(event);
125
135
  }
126
136
 
@@ -116,7 +116,7 @@ export const FormFieldElement: React.FC<FormFieldElementProps> = props => {
116
116
  } else if (fieldType === "Imageupload") {
117
117
  const nextFiles = Array.isArray(event) ? event : [];
118
118
  context?.setFiles(nextFiles);
119
- updateFormData(fieldId!, nextFiles);
119
+ updateFormData("files", nextFiles);
120
120
  return;
121
121
  } else {
122
122
  const { name, value } = event.target;
@@ -27,6 +27,11 @@ export type {
27
27
  } from "../types/utm";
28
28
  export { UTM_PARAM_NAMES } from "../types/utm";
29
29
 
30
+ export {
31
+ buildWifiGrantRedirectUrl,
32
+ redirectToWifiGrantUrl,
33
+ } from "./wifi-grant-redirect";
34
+
30
35
  // Contentful utilities (server-safe, no "use client")
31
36
  export { toDocument } from "./contentful/to-document";
32
37
  export {
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Wifi Grant Redirect Helpers
3
+ *
4
+ * Builds the captive-portal redirect URL for Wi-Fi splash pages (e.g. /free-public-wifi).
5
+ * The portal controller appends base_grant_url to the page URL; continue_url always
6
+ * points at the fixed success page below (not read from the portal's query params).
7
+ */
8
+
9
+ const WIFI_GRANT_CONTINUE_URL = "https://www.gokinetic.com/free-public-wifi/success";
10
+ const ALLOWED_BASE_GRANT_PROTOCOL = "https:";
11
+ // Cisco Meraki captive-portal domain; grant URLs use a per-node subdomain (e.g. n143.network-auth.com).
12
+ const ALLOWED_BASE_GRANT_DOMAIN = "network-auth.com";
13
+
14
+ // Restricts redirects to the known captive-portal domain to prevent open redirect via base_grant_url.
15
+ function getSafeBaseGrantUrl(rawBaseGrantUrl: string): URL | null {
16
+ try {
17
+ const parsed = new URL(rawBaseGrantUrl);
18
+ if (parsed.protocol !== ALLOWED_BASE_GRANT_PROTOCOL) return null;
19
+ const { hostname } = parsed;
20
+ const isAllowedHost =
21
+ hostname === ALLOWED_BASE_GRANT_DOMAIN ||
22
+ hostname.endsWith(`.${ALLOWED_BASE_GRANT_DOMAIN}`);
23
+ if (!isAllowedHost) return null;
24
+ return parsed;
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+
30
+ export function buildWifiGrantRedirectUrl(): string | null {
31
+ if (typeof window === "undefined") return null;
32
+ const params = new URLSearchParams(window.location.search);
33
+ const baseGrantUrl = params.get("base_grant_url");
34
+ if (!baseGrantUrl) return null;
35
+
36
+ const safeBaseGrantUrl = getSafeBaseGrantUrl(baseGrantUrl);
37
+ if (!safeBaseGrantUrl) return null;
38
+
39
+ safeBaseGrantUrl.searchParams.set("continue_url", WIFI_GRANT_CONTINUE_URL);
40
+ safeBaseGrantUrl.searchParams.set("duration", "86400");
41
+ return safeBaseGrantUrl.toString();
42
+ }
43
+
44
+ export function redirectToWifiGrantUrl(): boolean {
45
+ const redirectUrl = buildWifiGrantRedirectUrl();
46
+ if (!redirectUrl) return false;
47
+ window.location.href = redirectUrl;
48
+ return true;
49
+ }