@windstream/react-shared-components 0.2.68 → 0.2.70

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.
Files changed (35) hide show
  1. package/dist/contentful/index.d.ts +13 -2
  2. package/dist/contentful/index.esm.js +4 -4
  3. package/dist/contentful/index.esm.js.map +1 -1
  4. package/dist/contentful/index.js +2 -2
  5. package/dist/contentful/index.js.map +1 -1
  6. package/dist/core.d.ts +31 -2
  7. package/dist/index.d.ts +35 -6
  8. package/dist/index.esm.js +8 -8
  9. package/dist/index.esm.js.map +1 -1
  10. package/dist/index.js +8 -8
  11. package/dist/index.js.map +1 -1
  12. package/dist/styles.css +1 -1
  13. package/dist/utils/index.esm.js +1 -1
  14. package/dist/utils/index.esm.js.map +1 -1
  15. package/dist/utils/index.js +1 -1
  16. package/dist/utils/index.js.map +1 -1
  17. package/package.json +1 -1
  18. package/src/components/rich-text-basic/index.test.tsx +172 -0
  19. package/src/components/rich-text-basic/index.tsx +102 -0
  20. package/src/components/rich-text-basic/types.ts +34 -0
  21. package/src/components/select-plan-button/index.test.tsx +27 -0
  22. package/src/components/select-plan-button/index.tsx +6 -1
  23. package/src/components/video-link/index.tsx +2 -2
  24. package/src/contentful/blocks/cards/product-card/index.test.tsx +36 -0
  25. package/src/contentful/blocks/cards/product-card/index.tsx +23 -24
  26. package/src/contentful/blocks/carousel/index.tsx +1 -1
  27. package/src/contentful/blocks/dynamic-form/field-map.tsx +1 -1
  28. package/src/contentful/blocks/dynamic-form/index.test.tsx +96 -0
  29. package/src/contentful/blocks/dynamic-form/index.tsx +24 -1
  30. package/src/contentful/blocks/dynamic-form/types.ts +13 -2
  31. package/src/contentful/blocks/navigation/index.tsx +4 -1
  32. package/src/contentful/blocks/primary-hero/index.test.tsx +11 -2
  33. package/src/contentful/blocks/primary-hero/index.tsx +3 -2
  34. package/src/hooks/contentful/use-contentful-rich-text.tsx +39 -2
  35. package/src/index.ts +7 -0
@@ -180,6 +180,15 @@ export interface DynamicFormProps {
180
180
  emailSubject?: string;
181
181
  emailTemplateName?: string;
182
182
  toEmail?: string;
183
+ /**
184
+ * Test recipient(s) for the `email` action in non-production environments.
185
+ * When `isProduction === false` and the `email` action runs, the payload's
186
+ * `toEmail` is rerouted here so real recipients never receive non-prod
187
+ * submissions. Supports multiple comma-separated addresses (each is trimmed);
188
+ * when empty, the `email` action is skipped. Mirrors the residential form's
189
+ * `testEmail`.
190
+ */
191
+ testEmail?: string;
183
192
  segmentEventId?: string;
184
193
 
185
194
  /** Injected per-action async submit handlers. */
@@ -212,8 +221,10 @@ export interface DynamicFormProps {
212
221
  metadata?: Record<string, any>;
213
222
  }) => void;
214
223
 
215
- /** Whether the current environment is production (affects nothing in the
216
- * base component today but reserved for parity with app behavior). */
224
+ /** Whether the current environment is production. When explicitly `false`,
225
+ * the `email` action is rerouted to `testEmail` (or skipped when `testEmail`
226
+ * is empty) so non-prod submissions never reach real recipients. Leaving it
227
+ * `undefined` preserves the prior behavior (no rerouting). */
217
228
  isProduction?: boolean;
218
229
 
219
230
  /** Field customization + async data. */
@@ -130,7 +130,10 @@ export const Navigation: React.FC<NavigationProps> = props => {
130
130
  )}
131
131
  >
132
132
  <div className="mx-auto flex max-w-120 justify-between">
133
- <ul className="flex gap-5" aria-label="Utility Navigation">
133
+ <ul
134
+ className="flex min-h-11 gap-5"
135
+ aria-label="Utility Navigation"
136
+ >
134
137
  {utilityNavigationLinks?.map((links, index) => {
135
138
  return (
136
139
  <li key={`main-menu-items-${index}`}>
@@ -4,8 +4,8 @@ import { PrimaryHero } from "./index";
4
4
  import { fireEvent, render, screen } from "@testing-library/react";
5
5
 
6
6
  jest.mock("@shared/components/checklist", () => ({
7
- Checklist: ({ items }: any) => (
8
- <ul data-testid="checklist">
7
+ Checklist: ({ items, iconSize }: any) => (
8
+ <ul data-testid="checklist" data-icon-size={iconSize}>
9
9
  {items?.map((i: string, idx: number) => (
10
10
  <li key={idx}>{i}</li>
11
11
  ))}
@@ -128,6 +128,15 @@ describe("PrimaryHero", () => {
128
128
  expect(screen.getByText("Free install")).toBeInTheDocument();
129
129
  });
130
130
 
131
+ it("passes iconSize of 24 to the checklist", () => {
132
+ const checklist = [{ title: "No data caps" }, { title: "Free install" }];
133
+ render(<PrimaryHero {...baseProps} checklist={checklist} />);
134
+ expect(screen.getByTestId("checklist")).toHaveAttribute(
135
+ "data-icon-size",
136
+ "24"
137
+ );
138
+ });
139
+
131
140
  it("does not render checklist when not provided", () => {
132
141
  render(<PrimaryHero {...baseProps} />);
133
142
  expect(screen.queryByTestId("checklist")).not.toBeInTheDocument();
@@ -171,6 +171,7 @@ export const PrimaryHero: React.FC<PrimaryHeroProps> = props => {
171
171
  {/* checklist */}
172
172
  {checklist && (
173
173
  <Checklist
174
+ iconSize={24}
174
175
  listItemClassName={`body2 text-left ${textColorClass}`}
175
176
  items={checklist.map(item =>
176
177
  item.iconUrl
@@ -230,7 +231,7 @@ export const PrimaryHero: React.FC<PrimaryHeroProps> = props => {
230
231
  {...primaryCta1}
231
232
  renderCheckPlans={renderCheckPlans}
232
233
  onModalButtonClick={onModalButtonClick}
233
- fullWidth={true}
234
+ // fullWidth={true}
234
235
  buttonClassName="lg:w-auto text-center"
235
236
  />
236
237
  </div>
@@ -243,7 +244,7 @@ export const PrimaryHero: React.FC<PrimaryHeroProps> = props => {
243
244
  {...primaryCta2}
244
245
  renderCheckPlans={renderCheckPlans}
245
246
  onModalButtonClick={onModalButtonClick}
246
- fullWidth={true}
247
+ // fullWidth={true}
247
248
  buttonClassName="lg:w-auto text-center"
248
249
  />
249
250
  </div>
@@ -13,6 +13,7 @@ import {
13
13
  import { Checklist } from "@shared/components/checklist";
14
14
  import { Link } from "@shared/components/link";
15
15
  import { MaterialIcon } from "@shared/components/material-icon";
16
+ import type { IconName } from "@shared/components/material-icon";
16
17
  import { Text } from "@shared/components/text";
17
18
  import { VideoLink } from "@shared/components/video-link";
18
19
  import type { VideoLinkProps } from "@shared/components/video-link";
@@ -53,7 +54,10 @@ const toVideoLinkProps = (fields: any): VideoLinkProps => ({
53
54
  fields?.image?.fields?.file?.url ??
54
55
  fields?.image?.url ??
55
56
  fields?.image,
56
- link: fields?.link,
57
+ link:
58
+ typeof fields?.link === "string"
59
+ ? fields.link
60
+ : (fields?.link?.fields?.href ?? fields?.link?.href),
57
61
  videoPopup: Boolean(fields?.videoPopup),
58
62
  videoSourceType: fields?.videoSourceType,
59
63
  videoAssetUrl:
@@ -125,7 +129,40 @@ const isExternalUrl = (url: string): boolean => {
125
129
  }
126
130
  };
127
131
 
132
+ // Renders "[icon_name]" tokens (e.g. "look [star] here") as MaterialIcon glyphs.
133
+ const renderInlineIcons = (text: string): ReactNode[] => {
134
+ const iconPattern = /\[([a-zA-Z0-9_-]+)\]/g;
135
+ const parts: ReactNode[] = [];
136
+ let lastIndex = 0;
137
+ let match: RegExpExecArray | null;
138
+
139
+ while ((match = iconPattern.exec(text)) !== null) {
140
+ if (match.index > lastIndex) {
141
+ parts.push(text.substring(lastIndex, match.index));
142
+ }
143
+ parts.push(
144
+ <MaterialIcon
145
+ key={`icon-${match.index}-${match[1]}`}
146
+ name={match[1] as IconName}
147
+ size={20}
148
+ className="inline-block align-middle"
149
+ />
150
+ );
151
+ lastIndex = match.index + match[0].length;
152
+ }
153
+
154
+ if (lastIndex < text.length) {
155
+ parts.push(text.substring(lastIndex));
156
+ }
157
+
158
+ return parts.length > 0 ? parts : [text];
159
+ };
160
+
128
161
  const getDefaultOptions = (links?: any): Options => ({
162
+ renderText: children =>
163
+ children.includes("[") && children.includes("]")
164
+ ? renderInlineIcons(children)
165
+ : children,
129
166
  renderMark: {
130
167
  [MARKS.BOLD]: text => <strong className="font-black">{text}</strong>,
131
168
  [MARKS.ITALIC]: text => <em>{text}</em>,
@@ -467,4 +504,4 @@ export function renderContentfulRichTextTable(
467
504
  };
468
505
 
469
506
  return documentToReactComponents(doc, options);
470
- }
507
+ }
package/src/index.ts CHANGED
@@ -94,6 +94,13 @@ export type { SkeletonProps } from "./components/skeleton";
94
94
  export { Tooltip } from "./components/tooltip";
95
95
  export type { ToolTipProps } from "./components/tooltip";
96
96
 
97
+ export { RichTextBasic } from "./components/rich-text-basic";
98
+ export type {
99
+ RichTextBasicProps,
100
+ RichTextBasicOptions,
101
+ RichTextBasicTextProps,
102
+ } from "./components/rich-text-basic/types";
103
+
97
104
  export {
98
105
  FiberForm,
99
106
  fiberFormAddressCollectionFormSchema,