@windstream/react-shared-components 0.2.72 → 0.2.74
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 +2 -2
- package/dist/contentful/index.esm.js.map +1 -1
- package/dist/contentful/index.js +1 -1
- package/dist/contentful/index.js.map +1 -1
- package/dist/core.d.ts +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.esm.js +6 -6
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/utils/index.esm.js +1 -1
- package/dist/utils/index.esm.js.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/rich-text-basic/index.tsx +2 -1
- package/src/contentful/blocks/anchored-bottom-banner/index.test.tsx +128 -1
- package/src/contentful/blocks/anchored-bottom-banner/index.tsx +39 -28
- package/src/contentful/blocks/anchored-bottom-banner/types.ts +4 -0
- package/src/hooks/contentful/use-contentful-rich-text.test.tsx +55 -0
- package/src/hooks/contentful/use-contentful-rich-text.tsx +15 -8
- package/src/styles/globals.css +3 -0
- package/tailwind.config.cjs +1 -0
package/package.json
CHANGED
|
@@ -87,7 +87,8 @@ export const RichTextBasic: FC<RichTextBasicProps> = props => {
|
|
|
87
87
|
}
|
|
88
88
|
className={cx(
|
|
89
89
|
"last:[&_p]:mb-0",
|
|
90
|
-
|
|
90
|
+
// mobile-first: body3/leading-6 below md, body1/leading-normal from md up
|
|
91
|
+
"text-body3 leading-6 md:text-body1 md:leading-normal",
|
|
91
92
|
"[&_b]:font-bold [&_strong]:font-bold [&_i]:italic [&_em]:italic",
|
|
92
93
|
className,
|
|
93
94
|
!enableMaxWidth && "max-w-none"
|
|
@@ -106,7 +106,7 @@ describe("AnchoredBottomBanner", () => {
|
|
|
106
106
|
<AnchoredBottomBanner backgroundColor="blue" />
|
|
107
107
|
);
|
|
108
108
|
expect(
|
|
109
|
-
container.querySelector(".bg-bg-fill-brand-
|
|
109
|
+
container.querySelector(".bg-bg-fill-brand-info")
|
|
110
110
|
).toBeInTheDocument();
|
|
111
111
|
});
|
|
112
112
|
|
|
@@ -306,6 +306,7 @@ describe("AnchoredBottomBanner scheduledMessages", () => {
|
|
|
306
306
|
scheduledStart: "2026-05-18",
|
|
307
307
|
scheduleEnd: "2026-05-20",
|
|
308
308
|
daysToShow: ["ALL"],
|
|
309
|
+
enableCountdownTimer: true,
|
|
309
310
|
countdownStartDateTime: "2026-05-19T11:00:00Z",
|
|
310
311
|
countdownEndDateTime: "2026-05-19T13:00:00Z",
|
|
311
312
|
ctaButtonLabel: "Flash sale",
|
|
@@ -327,6 +328,7 @@ describe("AnchoredBottomBanner scheduledMessages", () => {
|
|
|
327
328
|
{
|
|
328
329
|
scheduledStart: "2026-05-17",
|
|
329
330
|
scheduleEnd: "2026-05-19",
|
|
331
|
+
enableCountdownTimer: true,
|
|
330
332
|
countdownEndDateTime: "2026-05-19T13:00:00Z",
|
|
331
333
|
ctaButtonLabel: "Last day",
|
|
332
334
|
},
|
|
@@ -383,6 +385,7 @@ describe("AnchoredBottomBanner scheduledMessages", () => {
|
|
|
383
385
|
scheduledStart: "2026-05-18",
|
|
384
386
|
scheduleEnd: "2026-05-20",
|
|
385
387
|
daysToShow: ["TUE"],
|
|
388
|
+
enableCountdownTimer: true,
|
|
386
389
|
countdownEndDateTime: "2026-05-19T13:00:00Z",
|
|
387
390
|
ctaButtonLabel: "Tuesday deal",
|
|
388
391
|
},
|
|
@@ -403,6 +406,7 @@ describe("AnchoredBottomBanner scheduledMessages", () => {
|
|
|
403
406
|
{
|
|
404
407
|
scheduledStart: "2026-05-18",
|
|
405
408
|
scheduleEnd: "2026-05-20",
|
|
409
|
+
enableCountdownTimer: true,
|
|
406
410
|
countdownEndDateTime: "2026-05-19T12:00:10Z",
|
|
407
411
|
ctaButtonLabel: "Active message",
|
|
408
412
|
},
|
|
@@ -414,6 +418,87 @@ describe("AnchoredBottomBanner scheduledMessages", () => {
|
|
|
414
418
|
expect(screen.getByText(/00H : 00M : 10/)).toBeInTheDocument();
|
|
415
419
|
});
|
|
416
420
|
|
|
421
|
+
it("does not show the countdown when the active message disables the timer", () => {
|
|
422
|
+
render(
|
|
423
|
+
<AnchoredBottomBanner
|
|
424
|
+
ctaButtonLabel="Default label"
|
|
425
|
+
scheduledMessages={[
|
|
426
|
+
{
|
|
427
|
+
scheduledStart: "2026-05-18",
|
|
428
|
+
scheduleEnd: "2026-05-20",
|
|
429
|
+
enableCountdownTimer: false,
|
|
430
|
+
countdownEndDateTime: "2026-05-19T13:00:00Z",
|
|
431
|
+
ctaButtonLabel: "No timer message",
|
|
432
|
+
},
|
|
433
|
+
]}
|
|
434
|
+
/>
|
|
435
|
+
);
|
|
436
|
+
expect(screen.getByText("No timer message")).toBeInTheDocument();
|
|
437
|
+
expect(screen.queryByText(/H :/)).not.toBeInTheDocument();
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it("does not show the active message's countdown when the flag is unset", () => {
|
|
441
|
+
render(
|
|
442
|
+
<AnchoredBottomBanner
|
|
443
|
+
ctaButtonLabel="Default label"
|
|
444
|
+
scheduledMessages={[
|
|
445
|
+
{
|
|
446
|
+
scheduledStart: "2026-05-18",
|
|
447
|
+
scheduleEnd: "2026-05-20",
|
|
448
|
+
countdownEndDateTime: "2026-05-19T13:00:00Z",
|
|
449
|
+
ctaButtonLabel: "Unset timer message",
|
|
450
|
+
},
|
|
451
|
+
]}
|
|
452
|
+
/>
|
|
453
|
+
);
|
|
454
|
+
expect(screen.getByText("Unset timer message")).toBeInTheDocument();
|
|
455
|
+
expect(screen.queryByText(/H :/)).not.toBeInTheDocument();
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it("does not inherit the parent's enableCountdownTimer for an active message", () => {
|
|
459
|
+
render(
|
|
460
|
+
<AnchoredBottomBanner
|
|
461
|
+
ctaButtonLabel="Default label"
|
|
462
|
+
enableCountdownTimer={true}
|
|
463
|
+
countdownEndDateTime="2026-05-19T13:00:00Z"
|
|
464
|
+
scheduledMessages={[
|
|
465
|
+
{
|
|
466
|
+
scheduledStart: "2026-05-18",
|
|
467
|
+
scheduleEnd: "2026-05-20",
|
|
468
|
+
enableCountdownTimer: false,
|
|
469
|
+
countdownEndDateTime: "2026-05-19T13:00:00Z",
|
|
470
|
+
ctaButtonLabel: "Active message",
|
|
471
|
+
},
|
|
472
|
+
]}
|
|
473
|
+
/>
|
|
474
|
+
);
|
|
475
|
+
// The parent timer is enabled, but the active message disables it -> no countdown.
|
|
476
|
+
expect(screen.getByText("Active message")).toBeInTheDocument();
|
|
477
|
+
expect(screen.queryByText(/H :/)).not.toBeInTheDocument();
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it("keeps the message active after its countdown ends, hiding only the countdown", () => {
|
|
481
|
+
render(
|
|
482
|
+
<AnchoredBottomBanner
|
|
483
|
+
ctaButtonLabel="Default label"
|
|
484
|
+
scheduledMessages={[
|
|
485
|
+
{
|
|
486
|
+
scheduledStart: "2026-05-18",
|
|
487
|
+
scheduleEnd: "2026-05-20",
|
|
488
|
+
enableCountdownTimer: true,
|
|
489
|
+
// Countdown already ended (11:00Z) while now is 12:00Z, but the
|
|
490
|
+
// display window (05-18..05-20) is still open.
|
|
491
|
+
countdownEndDateTime: "2026-05-19T11:00:00Z",
|
|
492
|
+
ctaButtonLabel: "Still showing message",
|
|
493
|
+
},
|
|
494
|
+
]}
|
|
495
|
+
/>
|
|
496
|
+
);
|
|
497
|
+
expect(screen.getByText("Still showing message")).toBeInTheDocument();
|
|
498
|
+
expect(screen.queryByText("Default label")).not.toBeInTheDocument();
|
|
499
|
+
expect(screen.queryByText(/H :/)).not.toBeInTheDocument();
|
|
500
|
+
});
|
|
501
|
+
|
|
417
502
|
it("selects the first message whose window contains today", () => {
|
|
418
503
|
render(
|
|
419
504
|
<AnchoredBottomBanner
|
|
@@ -441,6 +526,48 @@ describe("AnchoredBottomBanner scheduledMessages", () => {
|
|
|
441
526
|
expect(screen.queryByText("Past message")).not.toBeInTheDocument();
|
|
442
527
|
expect(screen.queryByText("Future message")).not.toBeInTheDocument();
|
|
443
528
|
});
|
|
529
|
+
|
|
530
|
+
it("uses the active message's background, not the parent's", () => {
|
|
531
|
+
const { container } = render(
|
|
532
|
+
<AnchoredBottomBanner
|
|
533
|
+
backgroundColor="navy"
|
|
534
|
+
scheduledMessages={[
|
|
535
|
+
{
|
|
536
|
+
scheduledStart: "2026-05-18",
|
|
537
|
+
scheduleEnd: "2026-05-20",
|
|
538
|
+
backgroundColor: "green",
|
|
539
|
+
ctaButtonLabel: "Active message",
|
|
540
|
+
},
|
|
541
|
+
]}
|
|
542
|
+
/>
|
|
543
|
+
);
|
|
544
|
+
expect(container.querySelector(".bg-bg-fill-success")).toBeInTheDocument();
|
|
545
|
+
expect(
|
|
546
|
+
container.querySelector(".bg-bg-fill-inverse")
|
|
547
|
+
).not.toBeInTheDocument();
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it("uses the default background when the active message has none", () => {
|
|
551
|
+
const { container } = render(
|
|
552
|
+
<AnchoredBottomBanner
|
|
553
|
+
backgroundColor="navy"
|
|
554
|
+
scheduledMessages={[
|
|
555
|
+
{
|
|
556
|
+
scheduledStart: "2026-05-18",
|
|
557
|
+
scheduleEnd: "2026-05-20",
|
|
558
|
+
ctaButtonLabel: "Active message",
|
|
559
|
+
},
|
|
560
|
+
]}
|
|
561
|
+
/>
|
|
562
|
+
);
|
|
563
|
+
// No override on the message -> same default as a parent with no background.
|
|
564
|
+
expect(
|
|
565
|
+
container.querySelector(".bg-bg-fill-inverse")
|
|
566
|
+
).not.toBeInTheDocument();
|
|
567
|
+
expect(
|
|
568
|
+
container.querySelector(".bg-bg-fill-brand-accent")
|
|
569
|
+
).toBeInTheDocument();
|
|
570
|
+
});
|
|
444
571
|
});
|
|
445
572
|
|
|
446
573
|
describe("AnchoredBottomBanner scheduling timezone (America/New_York)", () => {
|
|
@@ -113,23 +113,16 @@ function dayMatches(daysToShow: string[] | undefined, nowMs: number): boolean {
|
|
|
113
113
|
return normalized.includes(full) || normalized.includes(full.slice(0, 3));
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
// Active = today is within [scheduledStart, scheduleEnd]
|
|
117
|
-
//
|
|
118
|
-
// countdown
|
|
116
|
+
// Active = today is within [scheduledStart, scheduleEnd] and allowed by
|
|
117
|
+
// daysToShow. The countdown is independent: it hides once its own end passes
|
|
118
|
+
// (see the `countdown` memo), but the message itself stays active across its
|
|
119
|
+
// whole display window.
|
|
119
120
|
function isMessageActive(message: ScheduledMessage, nowMs: number): boolean {
|
|
120
121
|
const startMs = parseDisplayStart(message.scheduledStart);
|
|
121
122
|
const endMs = parseDisplayEnd(message.scheduleEnd);
|
|
122
123
|
const afterStart = startMs === undefined || nowMs >= startMs;
|
|
123
124
|
const beforeEnd = endMs === undefined || nowMs < endMs;
|
|
124
|
-
|
|
125
|
-
const countdownNotExpired =
|
|
126
|
-
countdownEndMs === undefined || nowMs < countdownEndMs;
|
|
127
|
-
return (
|
|
128
|
-
afterStart &&
|
|
129
|
-
beforeEnd &&
|
|
130
|
-
countdownNotExpired &&
|
|
131
|
-
dayMatches(message.daysToShow, nowMs)
|
|
132
|
-
);
|
|
125
|
+
return afterStart && beforeEnd && dayMatches(message.daysToShow, nowMs);
|
|
133
126
|
}
|
|
134
127
|
|
|
135
128
|
function formatCountdown(totalSeconds: number) {
|
|
@@ -159,22 +152,12 @@ export const AnchoredBottomBanner: React.FC<AnchoredBottomBannerProps> = ({
|
|
|
159
152
|
const backGroundColorClasses = {
|
|
160
153
|
navy: "bg-bg-fill-inverse",
|
|
161
154
|
green: "bg-bg-fill-success",
|
|
162
|
-
blue: "bg-bg-fill-brand-
|
|
155
|
+
blue: "bg-bg-fill-brand-info",
|
|
163
156
|
purple: "bg-bg-fill-brand-tertiary",
|
|
164
157
|
yellow: "bg-bg-fill-brand-accent",
|
|
165
158
|
white: "bg-white",
|
|
166
159
|
};
|
|
167
160
|
|
|
168
|
-
const bgClass = backgroundColor
|
|
169
|
-
? backGroundColorClasses[backgroundColor]
|
|
170
|
-
: "bg-bg-fill-brand-accent";
|
|
171
|
-
|
|
172
|
-
const isLightBackground =
|
|
173
|
-
backgroundColor === "yellow" ||
|
|
174
|
-
backgroundColor === "white" ||
|
|
175
|
-
!backgroundColor;
|
|
176
|
-
const textColorClass = isLightBackground ? "text-text-primary" : "text-white";
|
|
177
|
-
|
|
178
161
|
const [nowMs, setNowMs] = useState(() => Date.now());
|
|
179
162
|
const intervalRef = useRef<number | null>(null);
|
|
180
163
|
|
|
@@ -198,7 +181,14 @@ export const AnchoredBottomBanner: React.FC<AnchoredBottomBannerProps> = ({
|
|
|
198
181
|
// records the pre-mount hasActiveMessage:false fallback.
|
|
199
182
|
if (!hasMounted) return;
|
|
200
183
|
onResolved?.({ hasActiveMessage, activeMessage, applicableWeekday });
|
|
201
|
-
}, [
|
|
184
|
+
}, [
|
|
185
|
+
hasMounted,
|
|
186
|
+
hasActiveMessage,
|
|
187
|
+
activeMessage,
|
|
188
|
+
applicableWeekday,
|
|
189
|
+
scheduledMessages,
|
|
190
|
+
onResolved,
|
|
191
|
+
]);
|
|
202
192
|
|
|
203
193
|
// Latest display-window end, so we keep ticking to let messages activate/expire.
|
|
204
194
|
const scheduleMaxEndMs = useMemo(() => {
|
|
@@ -217,6 +207,13 @@ export const AnchoredBottomBanner: React.FC<AnchoredBottomBannerProps> = ({
|
|
|
217
207
|
? activeMessage?.countdownEndDateTime
|
|
218
208
|
: countdownEndDateTime;
|
|
219
209
|
|
|
210
|
+
// The timer flag is never inherited: an active message uses its own
|
|
211
|
+
// enableCountdownTimer (undefined = off, same as the banner level); otherwise
|
|
212
|
+
// the banner's top-level flag applies.
|
|
213
|
+
const effectiveEnableCountdownTimer = hasActiveMessage
|
|
214
|
+
? activeMessage?.enableCountdownTimer
|
|
215
|
+
: enableCountdownTimer;
|
|
216
|
+
|
|
220
217
|
const endMs = useMemo(
|
|
221
218
|
() => parseCountdownDateTime(effectiveCountdownEndDateTime),
|
|
222
219
|
[effectiveCountdownEndDateTime]
|
|
@@ -227,8 +224,9 @@ export const AnchoredBottomBanner: React.FC<AnchoredBottomBannerProps> = ({
|
|
|
227
224
|
);
|
|
228
225
|
|
|
229
226
|
const isTimerValid = useMemo(() => {
|
|
230
|
-
//
|
|
231
|
-
|
|
227
|
+
// Timer shows only when explicitly enabled for the resolved source
|
|
228
|
+
// (active message's own flag, or the banner's flag when none is active).
|
|
229
|
+
if (!effectiveEnableCountdownTimer) return false;
|
|
232
230
|
if (endMs === undefined) return false;
|
|
233
231
|
if (effectiveCountdownStartDateTime && startMs === undefined) return false;
|
|
234
232
|
if (startMs !== undefined && startMs >= endMs) {
|
|
@@ -240,8 +238,7 @@ export const AnchoredBottomBanner: React.FC<AnchoredBottomBannerProps> = ({
|
|
|
240
238
|
}
|
|
241
239
|
return true;
|
|
242
240
|
}, [
|
|
243
|
-
|
|
244
|
-
enableCountdownTimer,
|
|
241
|
+
effectiveEnableCountdownTimer,
|
|
245
242
|
endMs,
|
|
246
243
|
startMs,
|
|
247
244
|
effectiveCountdownStartDateTime,
|
|
@@ -309,6 +306,20 @@ export const AnchoredBottomBanner: React.FC<AnchoredBottomBannerProps> = ({
|
|
|
309
306
|
? activeMessage?.ctaButtonTarget
|
|
310
307
|
: ctaButtonTarget;
|
|
311
308
|
|
|
309
|
+
// Active message owns its background; when unset it uses the same default as
|
|
310
|
+
// the parent-with-no-background (bg-fill-brand-accent), not the parent's color.
|
|
311
|
+
const effectiveBackgroundColor = hasActiveMessage
|
|
312
|
+
? activeMessage?.backgroundColor
|
|
313
|
+
: backgroundColor;
|
|
314
|
+
const bgClass = effectiveBackgroundColor
|
|
315
|
+
? backGroundColorClasses[effectiveBackgroundColor]
|
|
316
|
+
: "bg-bg-fill-brand-accent";
|
|
317
|
+
const isLightBackground =
|
|
318
|
+
effectiveBackgroundColor === "yellow" ||
|
|
319
|
+
effectiveBackgroundColor === "white" ||
|
|
320
|
+
!effectiveBackgroundColor;
|
|
321
|
+
const textColorClass = isLightBackground ? "text-text-primary" : "text-white";
|
|
322
|
+
|
|
312
323
|
return (
|
|
313
324
|
<section id={anchorId}>
|
|
314
325
|
<div
|
|
@@ -25,10 +25,14 @@ export interface ScheduledMessage {
|
|
|
25
25
|
scheduleEnd?: string;
|
|
26
26
|
// Weekdays the message may show on within the window. "ALL" (or empty) = every day.
|
|
27
27
|
daysToShow?: ScheduledMessageDay[];
|
|
28
|
+
// Per-message timer toggle. The message never inherits the banner's
|
|
29
|
+
// enableCountdownTimer; the countdown shows only when this is explicitly true.
|
|
30
|
+
enableCountdownTimer?: boolean;
|
|
28
31
|
// Countdown timer window (full timestamps).
|
|
29
32
|
countdownStartDateTime?: string;
|
|
30
33
|
countdownEndDateTime?: string;
|
|
31
34
|
iconName?: string;
|
|
35
|
+
backgroundColor?: AnchoredBottomBannerProps["backgroundColor"];
|
|
32
36
|
ctaButtonLabel?: string;
|
|
33
37
|
ctaSuffixText?: string;
|
|
34
38
|
ctaButtonLink?: string;
|
|
@@ -1120,6 +1120,61 @@ describe("renderContentfulRichTextTable", () => {
|
|
|
1120
1120
|
expect(container).toHaveTextContent("Other Title");
|
|
1121
1121
|
});
|
|
1122
1122
|
|
|
1123
|
+
it("renders componentVideoLink inline entry (REST shape, no links prop)", () => {
|
|
1124
|
+
// Regression test: a doc containing both a table and an inline
|
|
1125
|
+
// componentVideoLink entry previously rendered nothing for the video,
|
|
1126
|
+
// since this function's INLINES.EMBEDDED_ENTRY override only resolved
|
|
1127
|
+
// entries via the GraphQL `links` lookup.
|
|
1128
|
+
const doc = makeDoc({
|
|
1129
|
+
nodeType: BLOCKS.PARAGRAPH,
|
|
1130
|
+
data: {},
|
|
1131
|
+
content: [
|
|
1132
|
+
makeInlineEntry("componentVideoLink", {
|
|
1133
|
+
link: "https://youtube.com/watch?v=123",
|
|
1134
|
+
}),
|
|
1135
|
+
],
|
|
1136
|
+
});
|
|
1137
|
+
const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
|
|
1138
|
+
const el = container.querySelector('[data-testid="video-link"]')!;
|
|
1139
|
+
expect(el).toBeInTheDocument();
|
|
1140
|
+
const videoLink = JSON.parse(el.getAttribute("data-video-link")!);
|
|
1141
|
+
expect(videoLink).toMatchObject({
|
|
1142
|
+
link: "https://youtube.com/watch?v=123",
|
|
1143
|
+
});
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
it("renders ComponentVideoLink (GraphQL __typename) inline entry resolved from links in table path", () => {
|
|
1147
|
+
const entryId = "video-inline-1";
|
|
1148
|
+
const doc = makeDoc({
|
|
1149
|
+
nodeType: BLOCKS.PARAGRAPH,
|
|
1150
|
+
data: {},
|
|
1151
|
+
content: [
|
|
1152
|
+
{
|
|
1153
|
+
nodeType: INLINES.EMBEDDED_ENTRY,
|
|
1154
|
+
data: { target: { sys: { id: entryId } } },
|
|
1155
|
+
content: [],
|
|
1156
|
+
},
|
|
1157
|
+
],
|
|
1158
|
+
});
|
|
1159
|
+
const links = {
|
|
1160
|
+
entries: {
|
|
1161
|
+
inline: [
|
|
1162
|
+
{
|
|
1163
|
+
sys: { id: entryId },
|
|
1164
|
+
__typename: "ComponentVideoLink",
|
|
1165
|
+
link: "https://youtube.com/watch?v=456",
|
|
1166
|
+
},
|
|
1167
|
+
],
|
|
1168
|
+
},
|
|
1169
|
+
};
|
|
1170
|
+
const { container } = render(
|
|
1171
|
+
<>{renderContentfulRichTextTable(doc, links)}</>
|
|
1172
|
+
);
|
|
1173
|
+
expect(
|
|
1174
|
+
container.querySelector('[data-testid="video-link"]')
|
|
1175
|
+
).toBeInTheDocument();
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1123
1178
|
it("returns null for inline entry not found in links", () => {
|
|
1124
1179
|
const doc = makeDoc({
|
|
1125
1180
|
nodeType: BLOCKS.PARAGRAPH,
|
|
@@ -471,18 +471,21 @@ export function renderContentfulRichTextTable(
|
|
|
471
471
|
);
|
|
472
472
|
},
|
|
473
473
|
[INLINES.EMBEDDED_ENTRY]: node => {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
)
|
|
474
|
+
// resolveEmbeddedEntry/getEntryTypeId/getEntryFields handle both REST
|
|
475
|
+
// (already-resolved target) and GraphQL (`links` lookup) shapes - a
|
|
476
|
+
// plain `links?.entries?.inline` lookup here always returns undefined
|
|
477
|
+
// for REST responses (no `links` prop passed), silently dropping any
|
|
478
|
+
// inline embedded entry (e.g. componentVideoLink) in a doc with a table.
|
|
479
|
+
const entry = resolveEmbeddedEntry(node, links);
|
|
479
480
|
if (!entry) return null;
|
|
481
|
+
const ctid = getEntryTypeId(entry);
|
|
482
|
+
const fields = getEntryFields(entry);
|
|
480
483
|
|
|
481
|
-
if (
|
|
484
|
+
if (ctid === "componentCheckList" || ctid === "ComponentCheckList") {
|
|
482
485
|
// FIX: Don't use a Hook here.
|
|
483
486
|
// Map the items manually or use a non-hook helper function.
|
|
484
487
|
const items =
|
|
485
|
-
entry.list?.items?.map((item: any) =>
|
|
488
|
+
(fields.list?.items ?? entry.list?.items)?.map((item: any) =>
|
|
486
489
|
renderContentfulRichText(
|
|
487
490
|
toDocument(item?.checkListTitle ?? ""),
|
|
488
491
|
true,
|
|
@@ -498,7 +501,11 @@ export function renderContentfulRichTextTable(
|
|
|
498
501
|
);
|
|
499
502
|
}
|
|
500
503
|
|
|
501
|
-
|
|
504
|
+
if (ctid === "componentVideoLink" || ctid === "ComponentVideoLink") {
|
|
505
|
+
return <VideoLink videoLink={toVideoLinkProps(fields)} />;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
return <span>{fields?.title ?? entry.title ?? ""}</span>;
|
|
502
509
|
},
|
|
503
510
|
},
|
|
504
511
|
};
|
package/src/styles/globals.css
CHANGED
|
@@ -104,6 +104,7 @@
|
|
|
104
104
|
--color-bg-fill-brand-hover: #45c187;
|
|
105
105
|
--color-bg-fill-brand-selected: #24a76a;
|
|
106
106
|
--color-bg-fill-brand-supporting: #07b2e2;
|
|
107
|
+
--color-bg-fill-brand-info: #07b2e2;
|
|
107
108
|
--color-bg-fill-brand-tertiary: #931d69;
|
|
108
109
|
--color-bg-fill-translucent: rgba(255, 255, 255, 0.5);
|
|
109
110
|
--color-bg-fill-critical: #d40000;
|
|
@@ -243,6 +244,7 @@
|
|
|
243
244
|
--color-bg-fill-brand-hover: #30aec5;
|
|
244
245
|
--color-bg-fill-brand-selected: #007c93;
|
|
245
246
|
--color-bg-fill-brand-supporting: #40afc9;
|
|
247
|
+
--color-bg-fill-brand-info: #40afc9;
|
|
246
248
|
--color-bg-fill-brand-tertiary: #a33895;
|
|
247
249
|
--color-bg-fill-inverse: #231f20;
|
|
248
250
|
--color-bg-fill-inverse-active: #231f20;
|
|
@@ -300,6 +302,7 @@
|
|
|
300
302
|
--color-bg-fill-brand-selected: #0393ba;
|
|
301
303
|
|
|
302
304
|
--color-bg-fill-brand-supporting: #26b170;
|
|
305
|
+
--color-bg-fill-brand-info: #0393ba;
|
|
303
306
|
|
|
304
307
|
/* Text colors */
|
|
305
308
|
--color-text-brand: #0393ba;
|
package/tailwind.config.cjs
CHANGED
|
@@ -158,6 +158,7 @@ module.exports = {
|
|
|
158
158
|
hover: "var(--color-bg-fill-brand-hover)",
|
|
159
159
|
selected: "var(--color-bg-fill-brand-selected)",
|
|
160
160
|
supporting: "var(--color-bg-fill-brand-supporting)",
|
|
161
|
+
info: "var(--color-bg-fill-brand-info)",
|
|
161
162
|
tertiary: "var(--color-bg-fill-brand-tertiary)",
|
|
162
163
|
},
|
|
163
164
|
critical: {
|