@arroyavecommerce/theme-kinetic 0.2.0

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 (99) hide show
  1. package/docs/design/README.md +22 -0
  2. package/docs/design/kinetic-street-concept.html +558 -0
  3. package/package.json +45 -0
  4. package/src/blocks/_shared.tsx +174 -0
  5. package/src/blocks/carrusel.tsx +109 -0
  6. package/src/blocks/category-tiles.test.tsx +59 -0
  7. package/src/blocks/category-tiles.tsx +70 -0
  8. package/src/blocks/editorial-banner.test.tsx +96 -0
  9. package/src/blocks/editorial-banner.tsx +205 -0
  10. package/src/blocks/faq.test.tsx +68 -0
  11. package/src/blocks/faq.tsx +122 -0
  12. package/src/blocks/featured-collection.test.tsx +80 -0
  13. package/src/blocks/featured-collection.tsx +97 -0
  14. package/src/blocks/features.test.tsx +71 -0
  15. package/src/blocks/features.tsx +159 -0
  16. package/src/blocks/hero.test.tsx +75 -0
  17. package/src/blocks/hero.tsx +290 -0
  18. package/src/blocks/index.tsx +35 -0
  19. package/src/blocks/lookbook.test.tsx +80 -0
  20. package/src/blocks/lookbook.tsx +117 -0
  21. package/src/blocks/marquee.test.tsx +54 -0
  22. package/src/blocks/marquee.tsx +114 -0
  23. package/src/blocks/newsletter.test.tsx +60 -0
  24. package/src/blocks/newsletter.tsx +105 -0
  25. package/src/blocks/product-grid.test.tsx +160 -0
  26. package/src/blocks/product-grid.tsx +76 -0
  27. package/src/blocks/promo-countdown.tsx +75 -0
  28. package/src/blocks/promo.test.tsx +71 -0
  29. package/src/blocks/promo.tsx +120 -0
  30. package/src/blocks/rich-text.test.tsx +67 -0
  31. package/src/blocks/rich-text.tsx +75 -0
  32. package/src/blocks/spacer.test.tsx +54 -0
  33. package/src/blocks/spacer.tsx +56 -0
  34. package/src/blocks/testimonials.test.tsx +74 -0
  35. package/src/blocks/testimonials.tsx +104 -0
  36. package/src/blocks/video.test.tsx +159 -0
  37. package/src/blocks/video.tsx +226 -0
  38. package/src/catalog/buy-box.test.tsx +225 -0
  39. package/src/catalog/buy-box.tsx +353 -0
  40. package/src/catalog/config.test.ts +115 -0
  41. package/src/catalog/config.ts +167 -0
  42. package/src/catalog/filter-controls.test.tsx +149 -0
  43. package/src/catalog/filter-controls.tsx +187 -0
  44. package/src/catalog/gallery.test.tsx +201 -0
  45. package/src/catalog/gallery.tsx +212 -0
  46. package/src/catalog/lightbox.test.tsx +95 -0
  47. package/src/catalog/lightbox.tsx +184 -0
  48. package/src/catalog/pdp.test.tsx +309 -0
  49. package/src/catalog/pdp.tsx +354 -0
  50. package/src/catalog/plp-arreglos.test.tsx +235 -0
  51. package/src/catalog/plp-inline.tsx +54 -0
  52. package/src/catalog/plp-overlay.tsx +129 -0
  53. package/src/catalog/plp-parts.test.tsx +146 -0
  54. package/src/catalog/plp-parts.tsx +292 -0
  55. package/src/catalog/plp-pills.tsx +44 -0
  56. package/src/catalog/plp-sidebar.tsx +47 -0
  57. package/src/catalog/plp.test.tsx +319 -0
  58. package/src/catalog/plp.tsx +259 -0
  59. package/src/catalog/product-card.test.tsx +221 -0
  60. package/src/catalog/product-card.tsx +411 -0
  61. package/src/catalog/registry.tsx +16 -0
  62. package/src/catalog/related.test.tsx +52 -0
  63. package/src/catalog/related.tsx +40 -0
  64. package/src/catalog/reviews.test.tsx +56 -0
  65. package/src/catalog/reviews.tsx +116 -0
  66. package/src/catalog/skeletons.test.tsx +61 -0
  67. package/src/catalog/skeletons.tsx +103 -0
  68. package/src/chrome/announcement-bar.test.tsx +86 -0
  69. package/src/chrome/announcement-bar.tsx +173 -0
  70. package/src/chrome/drawer-movil.test.tsx +113 -0
  71. package/src/chrome/drawer-movil.tsx +342 -0
  72. package/src/chrome/footer.test.tsx +133 -0
  73. package/src/chrome/footer.tsx +603 -0
  74. package/src/chrome/header.test.tsx +165 -0
  75. package/src/chrome/header.tsx +592 -0
  76. package/src/chrome/index.ts +26 -0
  77. package/src/chrome/mega-menu.test.tsx +161 -0
  78. package/src/chrome/mega-menu.tsx +317 -0
  79. package/src/chrome/mini-cart.test.tsx +111 -0
  80. package/src/chrome/mini-cart.tsx +320 -0
  81. package/src/content/home.ts +19 -0
  82. package/src/content/seed.test.ts +33 -0
  83. package/src/content/seed.ts +28 -0
  84. package/src/context.ts +104 -0
  85. package/src/icons/icons.test.tsx +118 -0
  86. package/src/icons/index.ts +136 -0
  87. package/src/icons/payment-icons.tsx +241 -0
  88. package/src/icons/social-icons.tsx +108 -0
  89. package/src/icons/svg-base.tsx +68 -0
  90. package/src/index.ts +49 -0
  91. package/src/puck-config.tsx +404 -0
  92. package/src/smoke.test.ts +8 -0
  93. package/src/template.test.tsx +88 -0
  94. package/src/test/mock-ctx.test.tsx +77 -0
  95. package/src/test/mock-ctx.tsx +236 -0
  96. package/src/theme-defaults.test.ts +9 -0
  97. package/src/theme.test.ts +82 -0
  98. package/src/theme.ts +195 -0
  99. package/tsconfig.json +4 -0
@@ -0,0 +1,133 @@
1
+ import { readFileSync } from "node:fs"
2
+ import { fileURLToPath } from "node:url"
3
+ import { describe, expect, it } from "vitest"
4
+ import { renderToStaticMarkup } from "react-dom/server"
5
+ import { footerModa, footerMinimal, footerNewsletter } from "./footer"
6
+ import { mockCtx } from "../test/mock-ctx"
7
+
8
+ const variantes = [
9
+ { nombre: "moda", variante: footerModa },
10
+ { nombre: "minimal", variante: footerMinimal },
11
+ { nombre: "newsletter", variante: footerNewsletter },
12
+ ]
13
+
14
+ describe("variantes de footer", () => {
15
+ it.each(variantes)("$nombre renderiza un <footer> sin crash", ({ variante }) => {
16
+ const html = renderToStaticMarkup(
17
+ <variante.Component props={variante.defaults} ctx={mockCtx()} menu={[]} />
18
+ )
19
+ expect(html).toContain("<footer")
20
+ })
21
+
22
+ it.each(variantes)("$nombre: sus defaults pasan el schema", ({ variante }) => {
23
+ expect(variante.schema.safeParse(variante.defaults).success).toBe(true)
24
+ })
25
+
26
+ it.each(variantes)("$nombre: renderiza el legal y no emite <h1>", ({ variante }) => {
27
+ const html = renderToStaticMarkup(
28
+ <variante.Component props={variante.defaults} ctx={mockCtx()} menu={[]} />
29
+ )
30
+ expect(html).toContain(variante.defaults.legal)
31
+ expect(html).not.toMatch(/<h1[\s>]/)
32
+ })
33
+ })
34
+
35
+ describe("footerModa — contenido", () => {
36
+ const html = renderToStaticMarkup(
37
+ <footerModa.Component props={footerModa.defaults} ctx={mockCtx()} menu={[]} />
38
+ )
39
+
40
+ it("renderiza el logo de texto (no el string 'LOGO')", () => {
41
+ expect(html).toContain("MODA")
42
+ expect(html).not.toContain(">LOGO<")
43
+ })
44
+
45
+ it("renderiza cada columna con sus enlaces via ctx.componentes.Enlace", () => {
46
+ for (const columna of footerModa.defaults.columnas) {
47
+ expect(html).toContain(columna.titulo)
48
+ for (const enlace of columna.enlaces) {
49
+ expect(html).toContain(enlace.texto)
50
+ expect(html).toContain(`href="${enlace.href}"`)
51
+ }
52
+ }
53
+ })
54
+
55
+ it("con modo 'icono' los pagos y redes se pintan como SVG", () => {
56
+ expect(html).toContain("<svg")
57
+ // href de cada red presente (Enlace del ctx)
58
+ for (const red of footerModa.defaults.redes.items) {
59
+ expect(html).toContain(`href="${red.href}"`)
60
+ }
61
+ })
62
+
63
+ it("emite las reglas CSS del tintado de iconos", () => {
64
+ expect(html).toContain(".icons--tinta")
65
+ expect(html).toContain(".icons--acento")
66
+ })
67
+ })
68
+
69
+ describe("pagos — modo icono vs texto", () => {
70
+ it("modo 'texto' muestra los labels de los métodos, no SVG de pago", () => {
71
+ const props = {
72
+ ...footerModa.defaults,
73
+ pagos: { ...footerModa.defaults.pagos, modo: "texto" as const, metodos: ["visa"] as const },
74
+ redes: { ...footerModa.defaults.redes, items: [] },
75
+ }
76
+ const html = renderToStaticMarkup(
77
+ <footerModa.Component props={props} ctx={mockCtx()} menu={[]} />
78
+ )
79
+ expect(html).toContain("Visa") // label del método
80
+ expect(html).not.toContain("<svg") // sin iconos (redes vacías + pagos en texto)
81
+ })
82
+
83
+ it("modo 'icono' renderiza SVG y el <title> accesible del método", () => {
84
+ const props = {
85
+ ...footerModa.defaults,
86
+ pagos: { modo: "icono" as const, color: "marca" as const, metodos: ["visa"] as const },
87
+ redes: { ...footerModa.defaults.redes, items: [] },
88
+ }
89
+ const html = renderToStaticMarkup(
90
+ <footerModa.Component props={props} ctx={mockCtx()} menu={[]} />
91
+ )
92
+ expect(html).toContain("<svg")
93
+ expect(html).toContain("<title>Visa</title>")
94
+ })
95
+
96
+ it("color 'tinta' añade la clase contenedora .icons--tinta", () => {
97
+ const props = {
98
+ ...footerModa.defaults,
99
+ pagos: { ...footerModa.defaults.pagos, color: "tinta" as const },
100
+ }
101
+ const html = renderToStaticMarkup(
102
+ <footerModa.Component props={props} ctx={mockCtx()} menu={[]} />
103
+ )
104
+ expect(html).toContain('class="flex flex-wrap items-center gap-2 icons--tinta"')
105
+ })
106
+ })
107
+
108
+ describe("footerNewsletter — formulario", () => {
109
+ const html = renderToStaticMarkup(
110
+ <footerNewsletter.Component props={footerNewsletter.defaults} ctx={mockCtx()} menu={[]} />
111
+ )
112
+
113
+ it("renderiza título, placeholder y botón editables del boletín", () => {
114
+ expect(html).toContain(footerNewsletter.defaults.boletin.titulo)
115
+ expect(html).toContain(`placeholder="${footerNewsletter.defaults.boletin.placeholder}"`)
116
+ expect(html).toContain(footerNewsletter.defaults.boletin.boton)
117
+ expect(html).toContain(footerNewsletter.defaults.boletin.nota)
118
+ })
119
+
120
+ it("incluye un input de email y un submit", () => {
121
+ expect(html).toContain('type="email"')
122
+ expect(html).toContain('type="submit"')
123
+ })
124
+ })
125
+
126
+ describe("footer.tsx — disciplina de render", () => {
127
+ it("no emite <a> ni <img> directos: todo pasa por ctx.componentes", () => {
128
+ const rutaFuente = fileURLToPath(new URL("./footer.tsx", import.meta.url))
129
+ const fuente = readFileSync(rutaFuente, "utf-8")
130
+ expect(fuente).not.toMatch(/<a[\s>]/)
131
+ expect(fuente).not.toMatch(/<img[\s>]/)
132
+ })
133
+ })