@eox/pages-theme-eox 2.0.0-alpha.7 → 2.0.0-alpha.9

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.
@@ -65,6 +65,7 @@ onMounted(() => {
65
65
 
66
66
  <style>
67
67
  section.logo-section {
68
+ background-color: var(--surface-container-lowest, #ffffff);
68
69
  margin-block-start: 3rem;
69
70
  }
70
71
  .logo-section .logo-row {
@@ -1,8 +1,11 @@
1
1
  <template>
2
2
  <template v-for="item in items">
3
- <li v-if="item.items && item.text">
3
+ <li
4
+ v-if="item.items && item.text"
5
+ :class="{ active: isActive(item, route.path) }"
6
+ >
4
7
  <details>
5
- <summary>
8
+ <summary :class="{ active: isActive(item, route.path) }">
6
9
  <span class="max" :class="{ active: isActive(item, route.path) }">{{
7
10
  t(item.text, theme.i18n)
8
11
  }}</span>
@@ -14,7 +17,7 @@
14
17
  </details>
15
18
  </li>
16
19
  <MobileNavDropdown v-else-if="item.items" :items="item.items" />
17
- <li v-else>
20
+ <li v-else :class="{ active: isActive(item, route.path) }">
18
21
  <a
19
22
  v-if="item.link"
20
23
  :href="withBase(item.link)"
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="top-nav at-top row center-align">
2
+ <div class="top-nav at-top row center-align" :class="{ 'has-hero': hasHero }">
3
3
  <nav class="large-padding no-margin center-align nav-mobile">
4
4
  <a :href="withBase('/')">
5
5
  <img
@@ -33,10 +33,17 @@
33
33
  </button>
34
34
  </nav>
35
35
  <ul class="list border large-space">
36
- <li v-for="item in theme.nav.filter((item) => !item.action)">
36
+ <li
37
+ v-for="item in theme.nav.filter((item) => !item.action)"
38
+ :class="{ active: isActive(item, route.path) }"
39
+ >
37
40
  <details v-if="item.items">
38
- <summary>
39
- <span class="max">{{ t(item.text, theme.i18n) }}</span>
41
+ <summary :class="{ active: isActive(item, route.path) }">
42
+ <span
43
+ class="max"
44
+ :class="{ active: isActive(item, route.path) }"
45
+ >{{ t(item.text, theme.i18n) }}</span
46
+ >
40
47
  <i class="mdi mdi-chevron-down"></i>
41
48
  </summary>
42
49
  <ul class="list">
@@ -49,7 +56,10 @@
49
56
  :href="withBase(item.link)"
50
57
  :target="item.target"
51
58
  :rel="item.rel"
52
- :class="item.action ? 'button large medium-elevate cta' : ''"
59
+ :class="[
60
+ item.action ? 'button large medium-elevate cta' : '',
61
+ { active: isActive(item, route.path) },
62
+ ]"
53
63
  >
54
64
  <span>{{ t(item.text, theme.i18n) }}</span>
55
65
  </a>
@@ -135,7 +145,10 @@
135
145
  <div class="space"></div>
136
146
  <nav>
137
147
  <ul class="left-align no-margin">
138
- <li v-for="item in theme.nav.filter((i) => !i.action)">
148
+ <li
149
+ v-for="item in theme.nav.filter((i) => !i.action)"
150
+ :class="{ active: isActive(item, route.path) }"
151
+ >
139
152
  <div v-if="item.items">
140
153
  <button
141
154
  class="button text no-round"
@@ -229,7 +242,7 @@ import { t, trackEvent, isActive } from "../helpers";
229
242
  import NavDropdown from "./NavDropdown.vue";
230
243
  import MobileNavDropdown from "./MobileNavDropdown.vue";
231
244
 
232
- const { site, theme, localeIndex } = useData();
245
+ const { site, theme, localeIndex, frontmatter, page } = useData();
233
246
  const route = useRoute();
234
247
 
235
248
  const langs = computed(() => {
@@ -271,6 +284,14 @@ const langs = computed(() => {
271
284
  });
272
285
  });
273
286
 
287
+ const hasHero = computed(() => {
288
+ return Boolean(
289
+ frontmatter.value.hero ||
290
+ frontmatter.value.layout === "home" ||
291
+ page.value.relativePath === "index.md",
292
+ );
293
+ });
294
+
274
295
  if (!import.meta.env.SSR) {
275
296
  const scrollListener = () => {
276
297
  const nav = document.querySelector(".top-nav");
@@ -301,7 +322,24 @@ if (!import.meta.env.SSR) {
301
322
  .top-nav.at-top {
302
323
  box-shadow: none;
303
324
  }
304
- .Layout.layout-home .top-nav.at-top .nav-desktop .button.primary,
325
+ .top-nav.at-top .nav-desktop .button.primary,
326
+ .top-nav.has-hero.at-top .nav-desktop .button.primary,
327
+ .Layout.layout-home .top-nav.at-top .nav-desktop .button.primary {
328
+ background: #ffffff !important;
329
+ color: #000c14 !important;
330
+ border-radius: 9999px;
331
+ font-weight: 500;
332
+ display: inline-flex;
333
+ }
334
+ .top-nav.at-top .nav-desktop .button.primary i.mdi-arrow-right,
335
+ .top-nav.has-hero.at-top .nav-desktop .button.primary i.mdi-arrow-right,
336
+ .Layout.layout-home
337
+ .top-nav.at-top
338
+ .nav-desktop
339
+ .button.primary
340
+ i.mdi-arrow-right {
341
+ display: none;
342
+ }
305
343
  .Layout.layout-home .top-nav.at-top .nav-desktop .button.secondary,
306
344
  .Layout.layout-home .top-nav.at-top .nav-desktop .button.alt {
307
345
  display: none;
@@ -317,43 +355,98 @@ if (!import.meta.env.SSR) {
317
355
  padding-top: 0.5rem !important;
318
356
  padding-bottom: 0.5rem !important;
319
357
  }
320
- .Layout.layout-home .top-nav.at-top {
358
+ .Layout.layout-home .top-nav.at-top,
359
+ .top-nav.has-hero.at-top {
321
360
  background: transparent;
322
361
  color: #f7f8f8;
323
362
  }
324
363
  .Layout .top-nav nav .button {
325
364
  color: var(--on-surface);
326
365
  }
327
- .Layout.layout-home .top-nav.at-top .nav-desktop .button {
366
+ .Layout.layout-home .top-nav.at-top .nav-desktop .button,
367
+ .top-nav.has-hero.at-top .nav-desktop .button {
328
368
  color: var(--on-surface);
329
369
  background: var(--surface);
330
370
  }
331
- .Layout.layout-home .top-nav.at-top .nav-desktop .button.text {
371
+ .Layout.layout-home .top-nav.at-top .nav-desktop .button.text,
372
+ .top-nav.has-hero.at-top .nav-desktop .button.text {
332
373
  color: #f7f8f8;
333
374
  background: none;
334
375
  }
335
- .Layout.layout-home .top-nav.at-top .nav-desktop .button.border {
376
+ .Layout.layout-home
377
+ .top-nav.at-top
378
+ .nav-desktop
379
+ .button.text:hover:not(.active),
380
+ .top-nav.has-hero.at-top .nav-desktop .button.text:hover:not(.active) {
381
+ background: rgba(255, 255, 255, 0.1);
382
+ color: #ffffff;
383
+ }
384
+ .Layout.layout-home .top-nav.at-top .nav-desktop .button.text.active,
385
+ .top-nav.has-hero.at-top .nav-desktop .button.text.active {
386
+ background-color: rgba(255, 255, 255, 0.15) !important;
387
+ color: #ffffff !important;
388
+ }
389
+ .Layout.layout-home .top-nav.at-top .nav-desktop .button.text.active:hover,
390
+ .top-nav.has-hero.at-top .nav-desktop .button.text.active:hover {
391
+ background-color: rgba(255, 255, 255, 0.25) !important;
392
+ }
393
+ .top-nav:not(.at-top) .nav-desktop .button.text.active {
394
+ background-color: var(--primary-container) !important;
395
+ color: var(--on-primary-container) !important;
396
+ }
397
+ .Layout.layout-home .top-nav.at-top .nav-desktop .button.border,
398
+ .top-nav.has-hero.at-top .nav-desktop .button.border {
336
399
  color: #f7f8f8;
337
400
  border-color: #f7f8f855;
338
401
  background: none;
339
402
  }
403
+ .Layout.layout-home .top-nav.at-top .nav-desktop .button.border.active,
404
+ .top-nav.has-hero.at-top .nav-desktop .button.border.active {
405
+ background-color: rgba(255, 255, 255, 0.15) !important;
406
+ color: #ffffff !important;
407
+ border-color: transparent !important;
408
+ }
409
+ .top-nav:not(.at-top) .nav-desktop .button.border.active {
410
+ background-color: var(--primary-container) !important;
411
+ color: var(--on-primary-container) !important;
412
+ border-color: transparent !important;
413
+ }
414
+ .nav-desktop menu > li.active,
415
+ .nav-desktop menu > li:has(> a.active) {
416
+ background-color: var(--primary-container) !important;
417
+ color: var(--on-primary-container) !important;
418
+ }
419
+ .nav-desktop menu > li.active > a,
420
+ .nav-desktop menu > li:has(> a.active) > a {
421
+ color: var(--on-primary-container) !important;
422
+ }
423
+ #mobile-menu .list li.active > a,
424
+ #mobile-menu .list li.active > span,
425
+ #mobile-menu .list summary:has(.active),
426
+ #mobile-menu .list summary.active {
427
+ color: var(--primary) !important;
428
+ font-weight: 600;
429
+ }
340
430
  .top-nav.at-top > nav > a:has(.logo.dark) > .logo.dark {
341
431
  display: none;
342
432
  }
343
- .Layout.layout-home .top-nav.at-top > nav > a:has(.logo.dark) > .logo.light {
433
+ .Layout.layout-home .top-nav.at-top > nav > a:has(.logo.dark) > .logo.light,
434
+ .top-nav.has-hero.at-top > nav > a:has(.logo.dark) > .logo.light {
344
435
  display: none;
345
436
  }
346
437
  .Layout.layout-home
347
438
  .top-nav.at-top
348
439
  > nav
349
440
  > a:not(:has(.logo.dark))
350
- > .logo.light {
441
+ > .logo.light,
442
+ .top-nav.has-hero.at-top > nav > a:not(:has(.logo.dark)) > .logo.light {
351
443
  filter: brightness(0) invert(1);
352
444
  }
353
445
  .Layout .top-nav > nav > a > .logo.dark {
354
446
  display: none;
355
447
  }
356
- .Layout.layout-home .top-nav.at-top > nav > a > .logo.dark {
448
+ .Layout.layout-home .top-nav.at-top > nav > a > .logo.dark,
449
+ .top-nav.has-hero.at-top > nav > a > .logo.dark {
357
450
  display: block;
358
451
  }
359
452
  img.logo {
@@ -1,6 +1,9 @@
1
1
  <template>
2
2
  <template v-for="item in items">
3
- <li v-if="item.items && item.text">
3
+ <li
4
+ v-if="item.items && item.text"
5
+ :class="{ active: isActive(item, route.path) }"
6
+ >
4
7
  <a
5
8
  v-if="item.link"
6
9
  :href="withBase(item.link)"
@@ -25,7 +28,7 @@
25
28
  </menu>
26
29
  </li>
27
30
  <NavDropdown v-else-if="item.items" :items="item.items" />
28
- <li v-else>
31
+ <li v-else :class="{ active: isActive(item, route.path) }">
29
32
  <a
30
33
  v-if="item.link"
31
34
  :href="withBase(item.link)"
@@ -1,6 +1,7 @@
1
1
  <template>
2
- <div class="tags-page">
3
- <header class="tags-header">
2
+ <div class="tags-page" :class="{ 'has-hero': Boolean(frontmatter.hero) }">
3
+ <!-- Header (shown only when hero is not present) -->
4
+ <header v-if="!frontmatter.hero" class="tags-header">
4
5
  <h1 class="tags-title">Blog Tags</h1>
5
6
  <p class="tags-subtitle">
6
7
  Explore our blog posts by topics and technology pillars.
@@ -69,10 +70,12 @@
69
70
 
70
71
  <script setup>
71
72
  import { computed } from "vue";
72
- import { withBase } from "vitepress";
73
+ import { useData, withBase } from "vitepress";
73
74
  import { data as posts } from "../posts.data.js";
74
75
  import { getTagColor, getTagStyle } from "../tagUtils.js";
75
76
 
77
+ const { frontmatter } = useData();
78
+
76
79
  const allTagsList = computed(() => {
77
80
  // Aggregate all tag occurrences
78
81
  const tagCounts = {};
@@ -111,6 +114,10 @@ const allTagsList = computed(() => {
111
114
  box-sizing: border-box;
112
115
  }
113
116
 
117
+ .tags-page.has-hero {
118
+ padding-top: 2rem;
119
+ }
120
+
114
121
  @media (min-width: 640px) {
115
122
  .tags-page {
116
123
  padding-left: 1.5rem;
@@ -118,6 +125,9 @@ const allTagsList = computed(() => {
118
125
  padding-top: calc(var(--vp-nav-height) + 40px);
119
126
  padding-bottom: 64px;
120
127
  }
128
+ .tags-page.has-hero {
129
+ padding-top: 2.5rem;
130
+ }
121
131
  }
122
132
 
123
133
  @media (min-width: 960px) {
@@ -127,6 +137,9 @@ const allTagsList = computed(() => {
127
137
  padding-top: calc(var(--vp-nav-height) + 58px);
128
138
  padding-bottom: 96px;
129
139
  }
140
+ .tags-page.has-hero {
141
+ padding-top: 3rem;
142
+ }
130
143
  }
131
144
 
132
145
  /* Page Header */
@@ -1,7 +1,7 @@
1
1
  <template>
2
- <div class="team-page">
3
- <!-- Header -->
4
- <header class="team-header">
2
+ <div class="team-page" :class="{ 'has-hero': Boolean(frontmatter.hero) }">
3
+ <!-- Header (shown only when hero is not present) -->
4
+ <header v-if="!frontmatter.hero" class="team-header">
5
5
  <h1 class="team-title">
6
6
  {{ frontmatter.title || "Our Team" }}
7
7
  </h1>
@@ -20,7 +20,7 @@
20
20
  />
21
21
  </section>
22
22
 
23
- <!-- Team Grid - max 3 columns on large screens -->
23
+ <!-- Team Grid - up to 4 columns on large screens -->
24
24
  <div class="team-grid">
25
25
  <FeatureCard
26
26
  v-for="member in sortedTeamMembers"
@@ -126,7 +126,7 @@ const memberHasPosts = (memberId) => {
126
126
  <style scoped>
127
127
  .team-page {
128
128
  width: 100%;
129
- max-width: 1200px !important;
129
+ max-width: 1400px !important;
130
130
  margin-left: auto !important;
131
131
  margin-right: auto !important;
132
132
  padding-left: 1rem !important;
@@ -137,6 +137,10 @@ const memberHasPosts = (memberId) => {
137
137
  word-break: break-word;
138
138
  }
139
139
 
140
+ .team-page.has-hero {
141
+ padding-top: 2rem !important;
142
+ }
143
+
140
144
  @media (min-width: 640px) {
141
145
  .team-page {
142
146
  padding-left: 1.5rem !important;
@@ -144,6 +148,9 @@ const memberHasPosts = (memberId) => {
144
148
  padding-top: calc(var(--vp-nav-height) + 40px) !important;
145
149
  padding-bottom: 64px !important;
146
150
  }
151
+ .team-page.has-hero {
152
+ padding-top: 2.5rem !important;
153
+ }
147
154
  }
148
155
 
149
156
  @media (min-width: 960px) {
@@ -153,6 +160,9 @@ const memberHasPosts = (memberId) => {
153
160
  padding-top: calc(var(--vp-nav-height) + 58px) !important;
154
161
  padding-bottom: 96px !important;
155
162
  }
163
+ .team-page.has-hero {
164
+ padding-top: 3rem !important;
165
+ }
156
166
  }
157
167
 
158
168
  /* Page Header */
@@ -235,6 +245,12 @@ const memberHasPosts = (memberId) => {
235
245
  }
236
246
  }
237
247
 
248
+ @media (min-width: 1280px) {
249
+ .team-grid {
250
+ grid-template-columns: repeat(4, minmax(0, 1fr));
251
+ }
252
+ }
253
+
238
254
  /* Deep overrides to integrate with theme-provided FeatureCard */
239
255
  .team-member-card {
240
256
  height: 100% !important;
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <section class="testimonials-section full-width surface-container-low">
2
+ <section class="testimonials-section full-width surface-container-lowest">
3
3
  <div class="holder">
4
4
  <!-- Left Side: Headline -->
5
5
  <div class="headline">
@@ -9,10 +9,27 @@
9
9
  </div>
10
10
 
11
11
  <!-- Right Side: Text Block -->
12
- <div v-if="testimonials && testimonials.length > 0" class="text-block">
13
- <!-- Testimonial Quote -->
14
- <div class="quote-text">
15
- &ldquo;{{ currentTestimonial.quote }}&rdquo;
12
+ <div
13
+ v-if="testimonials && testimonials.length > 0"
14
+ class="text-block"
15
+ role="region"
16
+ aria-roledescription="carousel"
17
+ :aria-label="title"
18
+ >
19
+ <!-- Testimonial Quotes Overlapping Grid -->
20
+ <div class="quotes-grid" aria-live="polite">
21
+ <div
22
+ v-for="(item, index) in testimonials"
23
+ :key="index"
24
+ class="quote-slide"
25
+ :class="{
26
+ active: index === currentIndex,
27
+ 'quote-text': index === currentIndex,
28
+ }"
29
+ :aria-hidden="index !== currentIndex"
30
+ >
31
+ &ldquo;{{ item.quote }}&rdquo;
32
+ </div>
16
33
  </div>
17
34
 
18
35
  <!-- Divider / Separator -->
@@ -20,14 +37,28 @@
20
37
 
21
38
  <!-- Frame 469: Author info & sliders -->
22
39
  <div class="frame-469">
23
- <!-- Frame 468: Author Name and Role -->
40
+ <!-- Frame 468: Author Name and Role Overlapping Grid -->
24
41
  <div class="frame-468">
25
- <span class="author-name">
26
- {{ currentTestimonial.author }}
27
- </span>
28
- <span class="author-role">
29
- {{ currentTestimonial.role }}
30
- </span>
42
+ <div
43
+ v-for="(item, index) in testimonials"
44
+ :key="index"
45
+ class="author-slide"
46
+ :class="{ active: index === currentIndex }"
47
+ :aria-hidden="index !== currentIndex"
48
+ >
49
+ <span
50
+ class="author-name-text"
51
+ :class="{ 'author-name': index === currentIndex }"
52
+ >
53
+ {{ item.author }}
54
+ </span>
55
+ <span
56
+ class="author-role-text"
57
+ :class="{ 'author-role': index === currentIndex }"
58
+ >
59
+ {{ item.role }}
60
+ </span>
61
+ </div>
31
62
  </div>
32
63
 
33
64
  <!-- sliders: Navigation Buttons -->
@@ -57,7 +88,7 @@
57
88
  </template>
58
89
 
59
90
  <script setup>
60
- import { ref, computed } from "vue";
91
+ import { ref, computed, watch } from "vue";
61
92
 
62
93
  const props = defineProps({
63
94
  title: {
@@ -91,6 +122,16 @@ const props = defineProps({
91
122
 
92
123
  const currentIndex = ref(0);
93
124
 
125
+ // Clamp currentIndex if testimonials prop array changes dynamically
126
+ watch(
127
+ () => props.testimonials?.length,
128
+ (len) => {
129
+ if (len && currentIndex.value >= len) {
130
+ currentIndex.value = 0;
131
+ }
132
+ },
133
+ );
134
+
94
135
  const currentTestimonial = computed(() => {
95
136
  if (!props.testimonials || props.testimonials.length === 0) return null;
96
137
  return props.testimonials[currentIndex.value];
@@ -118,6 +159,7 @@ section.testimonials-section {
118
159
  margin-block-start: 6rem;
119
160
  margin-block-end: 6rem;
120
161
  box-sizing: border-box;
162
+ background-color: var(--surface-container-lowest, #ffffff);
121
163
  }
122
164
 
123
165
  @media (min-width: 1024px) {
@@ -168,6 +210,38 @@ section.testimonials-section {
168
210
  gap: 24px;
169
211
  }
170
212
 
213
+ .quotes-grid {
214
+ display: grid;
215
+ grid-template-columns: 1fr;
216
+ width: 100%;
217
+ }
218
+
219
+ .quote-slide {
220
+ grid-column: 1;
221
+ grid-row: 1;
222
+ align-self: start;
223
+ font-family: "Inter", sans-serif;
224
+ font-style: normal;
225
+ font-weight: 400;
226
+ font-size: 16px;
227
+ line-height: 24px;
228
+ letter-spacing: 0.5px;
229
+ color: #00060a;
230
+ min-height: 72px;
231
+ opacity: 0;
232
+ visibility: hidden;
233
+ transition:
234
+ opacity 0.25s ease,
235
+ visibility 0.25s ease;
236
+ pointer-events: none;
237
+ }
238
+
239
+ .quote-slide.active {
240
+ opacity: 1;
241
+ visibility: visible;
242
+ pointer-events: auto;
243
+ }
244
+
171
245
  .quote-text {
172
246
  font-family: "Inter", sans-serif;
173
247
  font-style: normal;
@@ -195,14 +269,35 @@ section.testimonials-section {
195
269
  }
196
270
 
197
271
  .frame-468 {
272
+ display: grid;
273
+ grid-template-columns: 1fr;
274
+ align-items: flex-start;
275
+ flex-grow: 1;
276
+ }
277
+
278
+ .author-slide {
279
+ grid-column: 1;
280
+ grid-row: 1;
198
281
  display: flex;
199
282
  flex-direction: column;
200
283
  align-items: flex-start;
201
284
  gap: 8px;
202
- flex-grow: 1;
285
+ opacity: 0;
286
+ visibility: hidden;
287
+ transition:
288
+ opacity 0.25s ease,
289
+ visibility 0.25s ease;
290
+ pointer-events: none;
291
+ }
292
+
293
+ .author-slide.active {
294
+ opacity: 1;
295
+ visibility: visible;
296
+ pointer-events: auto;
203
297
  }
204
298
 
205
- .author-name {
299
+ .author-name,
300
+ .author-name-text {
206
301
  font-family: "Inter", sans-serif;
207
302
  font-style: normal;
208
303
  font-weight: 700;
@@ -212,7 +307,8 @@ section.testimonials-section {
212
307
  color: #00060a;
213
308
  }
214
309
 
215
- .author-role {
310
+ .author-role,
311
+ .author-role-text {
216
312
  font-family: "Inter", sans-serif;
217
313
  font-style: normal;
218
314
  font-weight: 400;
@@ -222,6 +318,13 @@ section.testimonials-section {
222
318
  color: #00060a;
223
319
  }
224
320
 
321
+ @media (prefers-reduced-motion: reduce) {
322
+ .quote-slide,
323
+ .author-slide {
324
+ transition: none;
325
+ }
326
+ }
327
+
225
328
  .sliders {
226
329
  display: flex;
227
330
  flex-direction: row;
@@ -0,0 +1,61 @@
1
+ import TextSection from "./TextSection.vue";
2
+
3
+ describe("<TextSection />", () => {
4
+ it("renders title, tagline and button", () => {
5
+ cy.mount(TextSection, {
6
+ props: {
7
+ title: "Test Section",
8
+ tagline: "Test Tagline",
9
+ buttonText: "Click Me",
10
+ buttonLink: "https://example.com",
11
+ },
12
+ });
13
+
14
+ cy.contains("Test Section").should("exist");
15
+ cy.contains("Test Tagline").should("exist");
16
+ cy.contains("a.button", "Click Me")
17
+ .should("have.attr", "href", "https://example.com")
18
+ .and("have.attr", "target", "_blank");
19
+ });
20
+
21
+ it("renders slot content and image", () => {
22
+ cy.mount(TextSection, {
23
+ props: {
24
+ image: "/test.jpg",
25
+ imageAlt: "Test Image",
26
+ },
27
+ slots: {
28
+ default: "<p>This is paragraph text.</p>",
29
+ },
30
+ });
31
+
32
+ cy.contains("This is paragraph text.").should("exist");
33
+ cy.get("img.section-image")
34
+ .should("have.attr", "src", "/test.jpg")
35
+ .and("have.attr", "alt", "Test Image");
36
+ });
37
+
38
+ it("applies reverse class when reverse prop is true", () => {
39
+ cy.mount(TextSection, {
40
+ props: {
41
+ image: "/test.jpg",
42
+ reverse: true,
43
+ },
44
+ });
45
+
46
+ cy.get(".text-section").should("have.class", "is-reverse");
47
+ cy.get(".text-section-grid").should("have.class", "grid-reverse");
48
+ });
49
+
50
+ it("applies single column styling when no image is provided", () => {
51
+ cy.mount(TextSection, {
52
+ slots: {
53
+ default: "<p>Single column text</p>",
54
+ },
55
+ });
56
+
57
+ cy.get(".text-section").should("have.class", "no-image");
58
+ cy.get(".text-section-grid").should("have.class", "grid-single");
59
+ cy.get(".media-column").should("not.exist");
60
+ });
61
+ });