@tapcart/mobile-components 0.11.5 → 0.11.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"use-infinite-wishlist.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-infinite-wishlist.ts"],"names":[],"mappings":"AAOA,KAAK,cAAc,GAAG;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,KAAK,0BAA0B,GAAG;IAChC,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACtB,CAAA;AAED,KAAK,cAAc,GAAG;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;IACD,cAAc,EAAE;QACd,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;IACD,gBAAgB,EAAE,OAAO,CAAA;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,eAAe,EAAE,KAAK,CAAC;QACrB,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,EAAE,MAAM,CAAA;KACd,CAAC,CAAA;IACF,KAAK,CAAC,EAAE;QACN,GAAG,EAAE,MAAM,CAAA;QACX,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;CACF,CAAA;AAED,KAAK,OAAO,GAAG;IACb,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,OAAO,CAAA;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,aAAa,CAAC,EAAE;QACd,GAAG,EAAE,MAAM,CAAA;QACX,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;CACF,CAAA;AAKD,wBAAgB,mBAAmB,CAAC,EAClC,iBAAiB,EACjB,SAAS,EACT,MAAM,EACN,UAAkB,EAClB,cAAmB,EACnB,UAAe,GAChB,EAAE,0BAA0B;;;;;;;;;EAsN5B"}
1
+ {"version":3,"file":"use-infinite-wishlist.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-infinite-wishlist.ts"],"names":[],"mappings":"AAOA,KAAK,cAAc,GAAG;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,KAAK,0BAA0B,GAAG;IAChC,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACtB,CAAA;AAED,KAAK,cAAc,GAAG;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;IACD,cAAc,EAAE;QACd,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;IACD,gBAAgB,EAAE,OAAO,CAAA;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,eAAe,EAAE,KAAK,CAAC;QACrB,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,EAAE,MAAM,CAAA;KACd,CAAC,CAAA;IACF,KAAK,CAAC,EAAE;QACN,GAAG,EAAE,MAAM,CAAA;QACX,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;CACF,CAAA;AAED,KAAK,OAAO,GAAG;IACb,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,OAAO,CAAA;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,aAAa,CAAC,EAAE;QACd,GAAG,EAAE,MAAM,CAAA;QACX,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;CACF,CAAA;AAKD,wBAAgB,mBAAmB,CAAC,EAClC,iBAAiB,EACjB,SAAS,EACT,MAAM,EACN,UAAkB,EAClB,cAAmB,EACnB,UAAe,GAChB,EAAE,0BAA0B;;;;;;;;;EAsO5B"}
@@ -77,7 +77,13 @@ export function useInfiniteWishlist({ initialProductIds, chunkSize, apiUrl, shou
77
77
  handleFetchError(err, "fetch mocked products");
78
78
  return [];
79
79
  }
80
- }), [apiUrl, collectionToFetchID]);
80
+ }), [
81
+ apiUrl,
82
+ collectionToFetchID,
83
+ queryVariables === null || queryVariables === void 0 ? void 0 : queryVariables.language,
84
+ queryVariables === null || queryVariables === void 0 ? void 0 : queryVariables.country,
85
+ queryVariables === null || queryVariables === void 0 ? void 0 : queryVariables.appId,
86
+ ]);
81
87
  const fetchProducts = useCallback((ids) => __awaiter(this, void 0, void 0, function* () {
82
88
  if (!ids.length)
83
89
  return [];
@@ -85,6 +91,13 @@ export function useInfiniteWishlist({ initialProductIds, chunkSize, apiUrl, shou
85
91
  return fetchMockedProducts();
86
92
  const queryParams = new URLSearchParams();
87
93
  queryParams.set("ids", getProductGidsFromIds(ids).join(","));
94
+ // Add country and language parameters for correct currency/pricing
95
+ if (queryVariables === null || queryVariables === void 0 ? void 0 : queryVariables.country) {
96
+ queryParams.set("country", queryVariables.country);
97
+ }
98
+ if (queryVariables === null || queryVariables === void 0 ? void 0 : queryVariables.language) {
99
+ queryParams.set("language", queryVariables.language);
100
+ }
88
101
  const url = `${apiUrl}/products/by-ids?${queryParams.toString()}`;
89
102
  try {
90
103
  const response = yield fetch(url);
@@ -115,7 +128,7 @@ export function useInfiniteWishlist({ initialProductIds, chunkSize, apiUrl, shou
115
128
  handleFetchError(err, "fetch products");
116
129
  return [];
117
130
  }
118
- }), [apiUrl, shouldMock, fetchMockedProducts, variantIds]);
131
+ }), [apiUrl, shouldMock, fetchMockedProducts, variantIds, queryVariables]);
119
132
  const loadMore = useCallback(() => __awaiter(this, void 0, void 0, function* () {
120
133
  if (loadingRef.current ||
121
134
  (!shouldMock && !(allProductIds.length - loadedIndex > 0))) {
@@ -1 +1 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../components/ui/image.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../components/ui/image.tsx"],"names":[],"mappings":"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"}
@@ -14,6 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
14
14
  /* eslint-disable @next/next/no-img-element */
15
15
  import * as React from "react";
16
16
  import ReactDOM from "react-dom";
17
+ import { stripHtml } from "../../lib/utils/html";
17
18
  function ImagePreload({ imgAttributes }) {
18
19
  const opts = {
19
20
  as: "image",
@@ -102,7 +103,7 @@ export const Image = React.forwardRef((_a, ref) => {
102
103
  : "";
103
104
  const nHeight = autoHeight ? "auto" : fixedHeight;
104
105
  const nSrc = src || (data === null || data === void 0 ? void 0 : data.url);
105
- const nAlt = (data === null || data === void 0 ? void 0 : data.altText) && !alt ? data === null || data === void 0 ? void 0 : data.altText : alt || "";
106
+ const nAlt = stripHtml((data === null || data === void 0 ? void 0 : data.altText) && !alt ? data === null || data === void 0 ? void 0 : data.altText : alt || "");
106
107
  const nAspectRatio = aspectRatio
107
108
  ? aspectRatio
108
109
  : normalizedData.unitsMatch
@@ -1 +1 @@
1
- {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../../components/ui/money.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAGjE,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd;AAED,QAAA,MAAM,aAAa,gGAMjB,CAAA;AAEF,MAAM,WAAW,UACf,SAAQ,SAAS,EACf,YAAY,CAAC,OAAO,aAAa,CAAC;IACpC,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,MAAM,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;CAC7B;AAED,iBAAS,KAAK,CAAC,EACb,KAAK,EACL,MAAM,EACN,QAAQ,EACR,aAAqB,EACrB,MAAM,EACN,GAAG,KAAK,EACT,EAAE,UAAU,2CAgDZ;AAED,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA"}
1
+ {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../../components/ui/money.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAGjE,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd;AAED,QAAA,MAAM,aAAa,gGAMjB,CAAA;AAEF,MAAM,WAAW,UACf,SAAQ,SAAS,EACf,YAAY,CAAC,OAAO,aAAa,CAAC;IACpC,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,MAAM,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;CAC7B;AAED,iBAAS,KAAK,CAAC,EACb,KAAK,EACL,MAAM,EACN,QAAQ,EACR,aAAqB,EACrB,MAAM,EACN,GAAG,KAAK,EACT,EAAE,UAAU,2CAiFZ;AAED,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA"}
@@ -25,27 +25,59 @@ function Money(_a) {
25
25
  var _b, _c;
26
26
  var { price, locale, currency, hideZeroCents = false, styles } = _a, props = __rest(_a, ["price", "locale", "currency", "hideZeroCents", "styles"]);
27
27
  const searchParams = useSearchParams();
28
- const countryFromParams = searchParams === null || searchParams === void 0 ? void 0 : searchParams.get("country");
29
- // Only use routeLocale if it's a valid BCP 47 language tag (e.g., "en-US", "fr-FR")
30
- const routeLocale = ((_c = (_b = usePathname()) === null || _b === void 0 ? void 0 : _b.split("/")) === null || _c === void 0 ? void 0 : _c.pop()) || "";
31
- // Valid locale format: 2-3 letter language code, hyphen, 2 letter country code
32
- const localeRegex = /^[a-z]{2,3}-[A-Z]{2}$/;
33
- const isValidLocale = routeLocale && localeRegex.test(routeLocale);
34
- let language = isValidLocale ? routeLocale : locale;
35
- // Fallback to a default locale if none provided or invalid
36
- if (!language || typeof language !== "string") {
37
- language = "en-US";
38
- }
39
- // If locale is just a language code (no hyphen), combine with country to create proper BCP 47 locale
40
- // e.g., lang=ES + country=MX -> es-MX (not es-ES which would default to euros)
41
- if (language && !localeRegex.test(language) && countryFromParams) {
42
- language = `${language.toLowerCase()}-${countryFromParams.toUpperCase()}`;
43
- }
44
- // Fix for when we want to format in USD in english
45
- // but language only contains two letter region
46
- if (currency === "USD" && !language.includes("-")) {
47
- language = `en-${language}`;
48
- }
28
+ const countryFromParams = (searchParams === null || searchParams === void 0 ? void 0 : searchParams.get("country")) || undefined;
29
+ // Derive a candidate locale from the route or prop, then normalize to a valid BCP 47 tag
30
+ const routeCandidate = ((_c = (_b = usePathname()) === null || _b === void 0 ? void 0 : _b.split("/")) === null || _c === void 0 ? void 0 : _c.pop()) || "";
31
+ const normalizeLocale = (baseLocale, countryParam) => {
32
+ const DEFAULT_LOCALE = "en-US";
33
+ const raw = (baseLocale || "").replace(/_/g, "-").trim();
34
+ let languageCode = "";
35
+ let regionCode = "";
36
+ if (raw.includes("-")) {
37
+ const [langPart, regionPart] = raw.split("-");
38
+ if (langPart)
39
+ languageCode = langPart.toLowerCase();
40
+ if (regionPart)
41
+ regionCode = regionPart.toUpperCase();
42
+ }
43
+ else if (raw.length === 2) {
44
+ if (/^[a-z]{2}$/.test(raw)) {
45
+ // language only (e.g., "en")
46
+ languageCode = raw.toLowerCase();
47
+ }
48
+ else if (/^[A-Z]{2}$/.test(raw)) {
49
+ // region only (e.g., "US")
50
+ regionCode = raw.toUpperCase();
51
+ }
52
+ else {
53
+ languageCode = raw.toLowerCase();
54
+ }
55
+ }
56
+ else if (raw) {
57
+ // fallback: treat as language subtag
58
+ languageCode = raw.toLowerCase();
59
+ }
60
+ // If a country param is present, prefer it as region
61
+ if (countryParam) {
62
+ regionCode = countryParam.toUpperCase();
63
+ }
64
+ // Ensure we always have a sensible language
65
+ if (!languageCode) {
66
+ languageCode = "en";
67
+ }
68
+ const candidate = regionCode
69
+ ? `${languageCode}-${regionCode}`
70
+ : languageCode;
71
+ try {
72
+ // Validate and canonicalize via NumberFormat
73
+ const formatter = new Intl.NumberFormat(candidate);
74
+ return formatter.resolvedOptions().locale;
75
+ }
76
+ catch (_a) {
77
+ return DEFAULT_LOCALE;
78
+ }
79
+ };
80
+ const language = normalizeLocale(routeCandidate || locale, countryFromParams);
49
81
  const formatter = React.useMemo(() => new Intl.NumberFormat(language, {
50
82
  style: "currency",
51
83
  currency: currency,
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=money.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"money.test.d.ts","sourceRoot":"","sources":["../../../components/ui/money.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { Money } from "./money";
4
+ jest.mock("next/navigation", () => ({
5
+ usePathname: jest.fn(),
6
+ useSearchParams: jest.fn(),
7
+ }));
8
+ const mockedUsePathname = require("next/navigation").usePathname;
9
+ const mockedUseSearchParams = require("next/navigation")
10
+ .useSearchParams;
11
+ function makeSearchParams(params) {
12
+ return {
13
+ get: (key) => params[key],
14
+ };
15
+ }
16
+ describe("Money", () => {
17
+ beforeEach(() => {
18
+ mockedUsePathname.mockReset();
19
+ mockedUseSearchParams.mockReset();
20
+ });
21
+ it("formats USD with default en-US when no locale provided", () => {
22
+ mockedUsePathname.mockReturnValue("/");
23
+ mockedUseSearchParams.mockReturnValue(makeSearchParams({}));
24
+ render(_jsx(Money, { price: 1234.56, currency: "USD", locale: "" }));
25
+ expect(screen.getByText(/\$1,234.56/)).toBeTruthy();
26
+ });
27
+ it("removes trailing cents when hideZeroCents is true", () => {
28
+ mockedUsePathname.mockReturnValue("/");
29
+ mockedUseSearchParams.mockReturnValue(makeSearchParams({}));
30
+ render(_jsx(Money, { price: 100, currency: "USD", locale: "en-US", hideZeroCents: true }));
31
+ expect(screen.getByText("$100")).toBeTruthy();
32
+ });
33
+ it("uses route locale when valid (e.g., fr-FR)", () => {
34
+ mockedUsePathname.mockReturnValue("/shop/fr-FR");
35
+ mockedUseSearchParams.mockReturnValue(makeSearchParams({}));
36
+ render(_jsx(Money, { price: 1234.56, currency: "EUR", locale: "en-US" }));
37
+ expect(screen.getByText(/1\s?234,56\s?€/)).toBeTruthy();
38
+ });
39
+ it("normalizes underscore and applies country param for region", () => {
40
+ mockedUsePathname.mockReturnValue("/shop/es_es");
41
+ mockedUseSearchParams.mockReturnValue(makeSearchParams({ country: "mx" }));
42
+ render(_jsx(Money, { price: 50, currency: "MXN", locale: "es" }));
43
+ expect(screen.getByText(/\$\s?50(\.00)?/)).toBeTruthy();
44
+ });
45
+ it("avoids malformed tags like en-us-US and falls back safely", () => {
46
+ mockedUsePathname.mockReturnValue("/en-us-US");
47
+ mockedUseSearchParams.mockReturnValue(makeSearchParams({}));
48
+ render(_jsx(Money, { price: 10, currency: "USD", locale: "en-us-US" }));
49
+ expect(screen.getByText(/\$10(\.00)?/)).toBeTruthy();
50
+ });
51
+ it("handles region-only input via country param", () => {
52
+ mockedUsePathname.mockReturnValue("/US");
53
+ mockedUseSearchParams.mockReturnValue(makeSearchParams({ country: "US" }));
54
+ render(_jsx(Money, { price: 20, currency: "USD", locale: "" }));
55
+ expect(screen.getByText(/\$20(\.00)?/)).toBeTruthy();
56
+ });
57
+ });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Strips HTML tags from a string using DOM parsing to handle nested elements correctly.
3
+ *
4
+ * @param html - The HTML string to strip tags from
5
+ * @returns Clean text content with HTML tags removed
6
+ *
7
+ * @example
8
+ * ```
9
+ * stripHtml('<p><strong>New Arrivals</strong></p>') // returns 'New Arrivals'
10
+ * stripHtml('<div>Hello <span>World</span>!</div>') // returns 'Hello World!'
11
+ * stripHtml('<p>First</p><p>Second</p>') // returns 'First Second'
12
+ * stripHtml('Plain text') // returns 'Plain text'
13
+ * ```
14
+ */
15
+ export declare const stripHtml: (html: string | null | undefined) => string;
16
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../lib/utils/html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,SAAS,SAAU,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,MAgD3D,CAAA"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Strips HTML tags from a string using DOM parsing to handle nested elements correctly.
3
+ *
4
+ * @param html - The HTML string to strip tags from
5
+ * @returns Clean text content with HTML tags removed
6
+ *
7
+ * @example
8
+ * ```
9
+ * stripHtml('<p><strong>New Arrivals</strong></p>') // returns 'New Arrivals'
10
+ * stripHtml('<div>Hello <span>World</span>!</div>') // returns 'Hello World!'
11
+ * stripHtml('<p>First</p><p>Second</p>') // returns 'First Second'
12
+ * stripHtml('Plain text') // returns 'Plain text'
13
+ * ```
14
+ */
15
+ export const stripHtml = (html) => {
16
+ // Handle null, undefined, or empty inputs
17
+ if (typeof html !== "string") {
18
+ return "";
19
+ }
20
+ try {
21
+ // Create a temporary DOM element
22
+ const tempDiv = document.createElement("div");
23
+ // Set the HTML content
24
+ tempDiv.innerHTML = html;
25
+ // Add spaces between block-level elements for accessibility and screen reader compatibility
26
+ // This ensures proper separation between content blocks while preserving existing meaningful whitespace
27
+ const blockElements = tempDiv.querySelectorAll("p, div, h1, h2, h3, h4, h5, h6, li, blockquote, pre, section, article, aside, header, footer, main, nav");
28
+ blockElements.forEach((element, index) => {
29
+ var _a;
30
+ // Skip the first element (index 0) to avoid adding a leading space
31
+ if (index === 0)
32
+ return;
33
+ // Check if there's already meaningful whitespace before this element
34
+ const prevSibling = element.previousSibling;
35
+ const hasWhitespaceBefore = prevSibling &&
36
+ prevSibling.nodeType === Node.TEXT_NODE &&
37
+ /\s$/.test(prevSibling.textContent || "");
38
+ // If whitespace already exists, don't add another space
39
+ if (hasWhitespaceBefore)
40
+ return;
41
+ // Only insert a space if there's no existing whitespace
42
+ // This prevents "FirstSecond" and ensures "First Second" for screen readers
43
+ const spaceNode = document.createTextNode(" ");
44
+ (_a = element.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(spaceNode, element);
45
+ });
46
+ // Extract text content (this handles nested elements automatically)
47
+ const textContent = tempDiv.textContent || tempDiv.innerText || "";
48
+ // Return trimmed text
49
+ return textContent.trim();
50
+ }
51
+ catch (error) {
52
+ // If DOM parsing fails for any reason, return the original string
53
+ console.warn("Failed to strip HTML, returning original string:", error);
54
+ return html;
55
+ }
56
+ };
package/dist/styles.css CHANGED
@@ -841,6 +841,10 @@ video {
841
841
  margin-top: 0px;
842
842
  margin-bottom: 0px;
843
843
  }
844
+ .my-1 {
845
+ margin-top: 0.25rem;
846
+ margin-bottom: 0.25rem;
847
+ }
844
848
  .my-3 {
845
849
  margin-top: 0.75rem;
846
850
  margin-bottom: 0.75rem;
@@ -1679,6 +1683,9 @@ video {
1679
1683
  .border-t-\[0px\] {
1680
1684
  border-top-width: 0px;
1681
1685
  }
1686
+ .border-solid {
1687
+ border-style: solid;
1688
+ }
1682
1689
  .border-none {
1683
1690
  border-style: none;
1684
1691
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=html.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.test.d.ts","sourceRoot":"","sources":["../../../tests/utils/html.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,104 @@
1
+ import { stripHtml } from "../../lib/utils/html";
2
+ describe("stripHtml", () => {
3
+ describe("nested HTML handling", () => {
4
+ it("should strip nested HTML tags correctly", () => {
5
+ const result = stripHtml("<p><strong>New Arrivals</strong></p>");
6
+ expect(result).toBe("New Arrivals");
7
+ });
8
+ it("should handle multiple nested elements", () => {
9
+ const result = stripHtml("<div>Hello <span>World</span>!</div>");
10
+ expect(result).toBe("Hello World!");
11
+ });
12
+ it("should handle deeply nested HTML", () => {
13
+ const result = stripHtml("<div><p><span><strong>Deep</strong> <em>nested</em></span> text</p></div>");
14
+ expect(result).toBe("Deep nested text");
15
+ });
16
+ it("should handle multiple paragraph elements at the top level", () => {
17
+ const result = stripHtml("<p>First paragraph</p><p>Second paragraph</p><p>Third paragraph</p>");
18
+ expect(result).toBe("First paragraph Second paragraph Third paragraph");
19
+ });
20
+ it("should handle mixed block-level elements with spaces", () => {
21
+ const result = stripHtml("<h1>Title</h1><p>Content</p><div>Footer</div>");
22
+ expect(result).toBe("Title Content Footer");
23
+ });
24
+ it("should handle list items with spaces", () => {
25
+ const result = stripHtml("<li>Item 1</li><li>Item 2</li><li>Item 3</li>");
26
+ expect(result).toBe("Item 1 Item 2 Item 3");
27
+ });
28
+ });
29
+ describe("plain text handling", () => {
30
+ it("should return plain text unchanged", () => {
31
+ const result = stripHtml("Plain text");
32
+ expect(result).toBe("Plain text");
33
+ });
34
+ it("should trim whitespace from result", () => {
35
+ const result = stripHtml("<p> Trimmed text </p>");
36
+ expect(result).toBe("Trimmed text");
37
+ });
38
+ });
39
+ describe("edge cases", () => {
40
+ it("should handle null input", () => {
41
+ const result = stripHtml(null);
42
+ expect(result).toBe("");
43
+ });
44
+ it("should handle undefined input", () => {
45
+ const result = stripHtml(undefined);
46
+ expect(result).toBe("");
47
+ });
48
+ it("should handle empty string", () => {
49
+ const result = stripHtml("");
50
+ expect(result).toBe("");
51
+ });
52
+ it("should handle non-string input", () => {
53
+ const result = stripHtml(123);
54
+ expect(result).toBe("");
55
+ });
56
+ it("should handle malformed HTML", () => {
57
+ const result = stripHtml("<p>Malformed <strong>content</p>");
58
+ expect(result).toBe("Malformed content");
59
+ });
60
+ it("should handle HTML with script tags", () => {
61
+ const result = stripHtml("<p>Safe content</p><script>alert('xss')</script>");
62
+ expect(result).toBe("Safe contentalert('xss')");
63
+ });
64
+ it("should handle self-closing tags", () => {
65
+ const result = stripHtml("<p>Line 1<br/>Line 2</p>");
66
+ expect(result).toBe("Line 1Line 2");
67
+ });
68
+ });
69
+ describe("error handling", () => {
70
+ it("should handle DOM parsing errors gracefully", () => {
71
+ const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
72
+ // Create a scenario that might cause DOM parsing to fail
73
+ const originalCreateElement = document.createElement;
74
+ document.createElement = jest.fn().mockImplementation(() => {
75
+ throw new Error("DOM error");
76
+ });
77
+ const result = stripHtml("<p>Error content</p>");
78
+ expect(result).toBe("<p>Error content</p>");
79
+ expect(consoleSpy).toHaveBeenCalledWith("Failed to strip HTML, returning original string:", expect.any(Error));
80
+ document.createElement = originalCreateElement;
81
+ consoleSpy.mockRestore();
82
+ });
83
+ });
84
+ describe("HTML entities", () => {
85
+ it("should handle HTML entities correctly", () => {
86
+ const result = stripHtml("<p>Hello &amp; goodbye &lt; &gt; &quot; &#x27;</p>");
87
+ expect(result).toBe("Hello & goodbye < > \" '");
88
+ });
89
+ it("should handle numeric HTML entities", () => {
90
+ const result = stripHtml("<p>&#8364; &#169; &#174;</p>");
91
+ expect(result).toBe("€ © ®");
92
+ });
93
+ });
94
+ describe("whitespace handling", () => {
95
+ it("should preserve meaningful whitespace", () => {
96
+ const result = stripHtml("<p>Hello <span>beautiful</span> world</p>");
97
+ expect(result).toBe("Hello beautiful world");
98
+ });
99
+ it("should handle line breaks and tabs", () => {
100
+ const result = stripHtml("<div>Line 1\n<p>Line 2</p>\t<span>Line 3</span></div>");
101
+ expect(result).toBe("Line 1\nLine 2\tLine 3");
102
+ });
103
+ });
104
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tapcart/mobile-components",
3
- "version": "0.11.5",
3
+ "version": "0.11.7",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "style": "dist/styles.css",