@windstream/react-shared-components 0.2.73 → 0.2.75
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 +1 -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 +2 -2
- package/dist/index.d.ts +2 -2
- 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/cards/product-card/index.test.tsx +31 -0
- package/src/contentful/blocks/cards/product-card/index.tsx +9 -8
- package/src/contentful/blocks/cards/product-card/types.ts +1 -0
- package/src/contentful/blocks/carousel/Carousel.stories.tsx +303 -3
- package/src/contentful/blocks/carousel/helper.test.tsx +265 -3
- package/src/contentful/blocks/carousel/helper.tsx +165 -98
- package/src/contentful/blocks/ordered-list/index.test.tsx +163 -0
- package/src/contentful/blocks/primary-hero/index.tsx +1 -0
- package/src/hooks/contentful/use-contentful-rich-text.test.tsx +55 -0
- package/src/hooks/contentful/use-contentful-rich-text.tsx +15 -8
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { OrderedList } from "./index";
|
|
5
|
+
|
|
6
|
+
import { render, screen, within } from "@testing-library/react";
|
|
7
|
+
|
|
8
|
+
jest.mock("@shared/components/link", () => ({
|
|
9
|
+
Link: ({ children, href, title, className }: any) => (
|
|
10
|
+
<a href={href} title={title} className={className} data-testid="ol-link">
|
|
11
|
+
{children}
|
|
12
|
+
</a>
|
|
13
|
+
),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock("@shared/components/text", () => ({
|
|
17
|
+
Text: ({ children, as: Tag = "span", className }: any) => (
|
|
18
|
+
<Tag className={className}>{children}</Tag>
|
|
19
|
+
),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
jest.mock("@shared/contentful/blocks/dynamic-tabs", () => ({
|
|
23
|
+
DynamicTabs: ({
|
|
24
|
+
tabs,
|
|
25
|
+
dynamic,
|
|
26
|
+
isOrderedList,
|
|
27
|
+
navigationType,
|
|
28
|
+
mobileSidebarTabType,
|
|
29
|
+
}: any) => (
|
|
30
|
+
<div
|
|
31
|
+
data-testid="dynamic-tabs"
|
|
32
|
+
data-dynamic={String(dynamic)}
|
|
33
|
+
data-ordered={String(isOrderedList)}
|
|
34
|
+
data-nav={String(navigationType)}
|
|
35
|
+
data-sidebar={String(mobileSidebarTabType)}
|
|
36
|
+
>
|
|
37
|
+
{tabs.items.map((tab: any) => (
|
|
38
|
+
<div
|
|
39
|
+
key={tab.anchorId}
|
|
40
|
+
data-testid="dt-tab"
|
|
41
|
+
data-tabname={tab.tabName}
|
|
42
|
+
data-anchor={tab.anchorId}
|
|
43
|
+
>
|
|
44
|
+
{tab.tabContent}
|
|
45
|
+
</div>
|
|
46
|
+
))}
|
|
47
|
+
</div>
|
|
48
|
+
),
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
describe("OrderedList", () => {
|
|
52
|
+
const tabs = [
|
|
53
|
+
{
|
|
54
|
+
items: {
|
|
55
|
+
tabName: "States",
|
|
56
|
+
tabContent: [
|
|
57
|
+
{ itemName: "new york", itemSlug: "/new-york" },
|
|
58
|
+
{ itemName: "los angeles" },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
items: {
|
|
64
|
+
tabName: "Cities",
|
|
65
|
+
tabContent: [{ itemName: "san FRANCISCO", itemSlug: "/sf" }],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
const renderComponent = (overrides: Partial<any> = {}) =>
|
|
71
|
+
render(
|
|
72
|
+
<OrderedList
|
|
73
|
+
title="Browse locations"
|
|
74
|
+
description="Find availability near you"
|
|
75
|
+
tabs={tabs}
|
|
76
|
+
{...overrides}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
it("renders the title and description", () => {
|
|
81
|
+
renderComponent();
|
|
82
|
+
expect(screen.getByText("Browse locations")).toBeInTheDocument();
|
|
83
|
+
expect(screen.getByText("Find availability near you")).toBeInTheDocument();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("renders the section with the provided anchor id and testid", () => {
|
|
87
|
+
const { container } = renderComponent({ anchorId: "locations" });
|
|
88
|
+
const section = container.querySelector(
|
|
89
|
+
'[data-testid="non-fiber-city-list"]'
|
|
90
|
+
);
|
|
91
|
+
expect(section).toBeInTheDocument();
|
|
92
|
+
expect(section).toHaveAttribute("id", "locations");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("defaults the background to white", () => {
|
|
96
|
+
const { container } = renderComponent();
|
|
97
|
+
expect(container.querySelector(".bg-secondary-white")).toBeInTheDocument();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("applies the cream background when requested", () => {
|
|
101
|
+
const { container } = renderComponent({ backgroundColor: "cream" });
|
|
102
|
+
expect(container.querySelector(".bg-secondary-cream")).toBeInTheDocument();
|
|
103
|
+
expect(
|
|
104
|
+
container.querySelector(".bg-secondary-white")
|
|
105
|
+
).not.toBeInTheDocument();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("forwards the expected flags to DynamicTabs", () => {
|
|
109
|
+
renderComponent();
|
|
110
|
+
const dynamicTabs = screen.getByTestId("dynamic-tabs");
|
|
111
|
+
expect(dynamicTabs).toHaveAttribute("data-dynamic", "true");
|
|
112
|
+
expect(dynamicTabs).toHaveAttribute("data-ordered", "true");
|
|
113
|
+
expect(dynamicTabs).toHaveAttribute("data-nav", "false");
|
|
114
|
+
expect(dynamicTabs).toHaveAttribute("data-sidebar", "false");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("maps each tab with its name and a generated anchor id", () => {
|
|
118
|
+
renderComponent();
|
|
119
|
+
const dtTabs = screen.getAllByTestId("dt-tab");
|
|
120
|
+
expect(dtTabs).toHaveLength(2);
|
|
121
|
+
expect(dtTabs[0]).toHaveAttribute("data-tabname", "States");
|
|
122
|
+
expect(dtTabs[0]).toHaveAttribute("data-anchor", "tab-0");
|
|
123
|
+
expect(dtTabs[1]).toHaveAttribute("data-tabname", "Cities");
|
|
124
|
+
expect(dtTabs[1]).toHaveAttribute("data-anchor", "tab-1");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("title-cases item names regardless of input casing", () => {
|
|
128
|
+
renderComponent();
|
|
129
|
+
// "new york" -> "New York", "los angeles" -> "Los Angeles",
|
|
130
|
+
// "san FRANCISCO" -> "San Francisco"
|
|
131
|
+
expect(screen.getByText("New York")).toBeInTheDocument();
|
|
132
|
+
expect(screen.getByText("Los Angeles")).toBeInTheDocument();
|
|
133
|
+
expect(screen.getByText("San Francisco")).toBeInTheDocument();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("renders a linked item with an uppercased title and href", () => {
|
|
137
|
+
renderComponent();
|
|
138
|
+
const link = screen.getByText("New York").closest("a");
|
|
139
|
+
expect(link).not.toBeNull();
|
|
140
|
+
expect(link).toHaveAttribute("href", "/new-york");
|
|
141
|
+
expect(link).toHaveAttribute("title", "NEW YORK");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("renders an item without a slug as plain text (no link)", () => {
|
|
145
|
+
renderComponent();
|
|
146
|
+
const losAngeles = screen.getByText("Los Angeles");
|
|
147
|
+
expect(losAngeles.closest("a")).toBeNull();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("keeps each tab's items within that tab", () => {
|
|
151
|
+
renderComponent();
|
|
152
|
+
const dtTabs = screen.getAllByTestId("dt-tab");
|
|
153
|
+
expect(within(dtTabs[0]).getByText("New York")).toBeInTheDocument();
|
|
154
|
+
expect(within(dtTabs[0]).getByText("Los Angeles")).toBeInTheDocument();
|
|
155
|
+
expect(within(dtTabs[1]).getByText("San Francisco")).toBeInTheDocument();
|
|
156
|
+
expect(within(dtTabs[1]).queryByText("New York")).toBeNull();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("does not render DynamicTabs when there are no tabs", () => {
|
|
160
|
+
renderComponent({ tabs: [] });
|
|
161
|
+
expect(screen.queryByTestId("dynamic-tabs")).toBeNull();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -184,6 +184,7 @@ export const PrimaryHero: React.FC<PrimaryHeroProps> = props => {
|
|
|
184
184
|
const checklistEl = (
|
|
185
185
|
<Checklist
|
|
186
186
|
iconSize={24}
|
|
187
|
+
iconPosition="top"
|
|
187
188
|
listItemClassName={`body2 text-left ${textColorClass}`}
|
|
188
189
|
items={checklist.map(item =>
|
|
189
190
|
item.iconUrl
|
|
@@ -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
|
};
|