@windstream/react-shared-components 0.2.62 → 0.2.63

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.62",
3
+ "version": "0.2.63",
4
4
  "type": "module",
5
5
  "description": "Shared React components for Kinetic applications",
6
6
  "main": "dist/index.js",
@@ -5,18 +5,27 @@ import Player from "@vimeo/player";
5
5
  import { cx } from "@shared/utils/cx";
6
6
 
7
7
  const extractVimeoIdandHash = (href: string) => {
8
- // Strip query parameters and fragments
9
- const cleanHref = href.split(/[?#]/)[0].replace(/\/+$/, "");
10
- const match =
11
- cleanHref.match(/vimeo\.com\/(?:.*\/)?(\d{6,})/) ||
12
- cleanHref.match(/player\.vimeo\.com\/video\/(\d{6,})/);
8
+ // Strip query parameters and fragments without a backtracking-prone regex
9
+ let cleanHref = href.split(/[?#]/)[0];
10
+ while (cleanHref.endsWith("/")) cleanHref = cleanHref.slice(0, -1);
13
11
 
14
- const parts = cleanHref.split("/");
15
- const hash = parts[parts.length - 1];
12
+ const parts = cleanHref.split("/").filter(Boolean);
13
+
14
+ // Locate the vimeo domain segment (vimeo.com or player.vimeo.com) manually
15
+ // instead of a backtracking regex like `(?:.*\/)?(\d{6,})`
16
+ const domainIndex = parts.findIndex(
17
+ part => part === "vimeo.com" || part === "player.vimeo.com"
18
+ );
19
+ if (domainIndex === -1) return "";
16
20
 
17
- if (!match?.[1]) return "";
18
- if (match[1] === hash) return match[1];
19
- return `${match[1]}/${hash}`;
21
+ const id = parts.find(
22
+ (part, i) => i > domainIndex && /^\d{6,}$/.test(part)
23
+ );
24
+ if (!id) return "";
25
+
26
+ const hash = parts[parts.length - 1];
27
+ if (id === hash) return id;
28
+ return `${id}/${hash}`;
20
29
  };
21
30
 
22
31
  export const getVimeoEmbedId = (link?: string): string => {
@@ -164,7 +164,7 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
164
164
  </div>
165
165
  )}
166
166
  </div>
167
- <aside className="flex w-full flex-1 shrink-0 items-center justify-center lg:w-auto">
167
+ <div className="flex w-full flex-1 shrink-0 items-center justify-center lg:w-auto">
168
168
  {/* Media Section: only when no Video Link is attached at all */}
169
169
  {image && !hasVideoLink && (
170
170
  <div
@@ -203,7 +203,7 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
203
203
  isAboveTheFold={isAboveTheFold}
204
204
  onVideoEvent={onVideoEvent}
205
205
  />
206
- </aside>
206
+ </div>
207
207
  </div>
208
208
  </div>
209
209
 
@@ -276,6 +276,18 @@ describe("renderContentfulRichText", () => {
276
276
  const a = container.querySelector("a")!;
277
277
  expect(a).toHaveClass("custom-link");
278
278
  });
279
+
280
+ it("uses a separate class for links inside list items", () => {
281
+ const list = makeList(false, ["List link"]);
282
+ list.content[0].content = [makeHyperlink("/list-link", "List link")];
283
+ const doc = makeDoc(makeHyperlink("/paragraph-link", "Paragraph link"), list);
284
+
285
+ const { container } = render(<>{renderContentfulRichText(doc)}</>);
286
+ const links = container.querySelectorAll("a");
287
+
288
+ expect(links[0]).toHaveClass("body1", "font-bold");
289
+ expect(links[1]).toHaveClass("body2");
290
+ });
279
291
  });
280
292
 
281
293
  describe("Custom options merge", () => {
@@ -267,10 +267,23 @@ export function renderContentfulRichText(
267
267
  className: string = "body1",
268
268
  linkClassName: string = "body1 font-bold",
269
269
  options?: Options,
270
- links?: any
270
+ links?: any,
271
+ listLinkClassName: string = "body2"
271
272
  ): ReactNode | null {
272
273
  if (!doc || !Array.isArray(doc.content)) return null;
273
274
 
275
+ const listItemHyperlinks = new WeakSet<object>();
276
+ const collectListItemHyperlinks = (node: any, isInListItem = false) => {
277
+ const isListItem = isInListItem || node?.nodeType === BLOCKS.LIST_ITEM;
278
+ if (isListItem && node?.nodeType === INLINES.HYPERLINK) {
279
+ listItemHyperlinks.add(node);
280
+ }
281
+ node?.content?.forEach((child: any) =>
282
+ collectListItemHyperlinks(child, isListItem)
283
+ );
284
+ };
285
+ collectListItemHyperlinks(doc);
286
+
274
287
  const defaultOptions = getDefaultOptions(links);
275
288
  const merged: Options = {
276
289
  ...defaultOptions,
@@ -301,7 +314,9 @@ export function renderContentfulRichText(
301
314
  target={shouldOpenBlank ? "_blank" : undefined}
302
315
  rel={shouldOpenBlank ? "noopener" : undefined}
303
316
  variant="default"
304
- className={linkClassName}
317
+ className={
318
+ listItemHyperlinks.has(node) ? listLinkClassName : linkClassName
319
+ }
305
320
  >
306
321
  {children}
307
322
  </Link>