feat(contact): add global WhatsApp FAB and mobile sticky CTA bar
Deploy to Production / Build & Verify (push) Has been cancelled
Deploy to Production / Pre-Deploy Tests (push) Has been cancelled
Deploy to Production / Deploy to Railway (push) Has been cancelled
Deploy to Production / Deploy to Render (push) Has been cancelled
Deploy to Production / Deploy to VPS (PM2) (push) Has been cancelled
Deploy to Production / Deploy to Fly.io (push) Has been cancelled
Deploy to Production / Post-Deploy Verification (push) Has been cancelled
Deploy to Production / Notify on Failure (push) Has been cancelled
E2E Test Suite / Critical User Journeys (push) Has been cancelled
E2E Test Suite / API Integration Tests (push) Has been cancelled
E2E Test Suite / Form Interaction Tests (push) Has been cancelled
E2E Test Suite / Destructive & Chaos Tests (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (chromium) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (firefox) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (webkit) (push) Has been cancelled
E2E Test Suite / Mobile Device Tests (push) Has been cancelled
E2E Test Suite / Security Header Tests (push) Has been cancelled
E2E Test Suite / Test Report Summary (push) Has been cancelled
E2E Test Suite / Smoke Tests (P0) (push) Has been cancelled
Ping Search Engines / Notify Search Engines (push) Failing after 13m5s
Deploy to Production / Build & Verify (push) Has been cancelled
Deploy to Production / Pre-Deploy Tests (push) Has been cancelled
Deploy to Production / Deploy to Railway (push) Has been cancelled
Deploy to Production / Deploy to Render (push) Has been cancelled
Deploy to Production / Deploy to VPS (PM2) (push) Has been cancelled
Deploy to Production / Deploy to Fly.io (push) Has been cancelled
Deploy to Production / Post-Deploy Verification (push) Has been cancelled
Deploy to Production / Notify on Failure (push) Has been cancelled
E2E Test Suite / Critical User Journeys (push) Has been cancelled
E2E Test Suite / API Integration Tests (push) Has been cancelled
E2E Test Suite / Form Interaction Tests (push) Has been cancelled
E2E Test Suite / Destructive & Chaos Tests (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (chromium) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (firefox) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (webkit) (push) Has been cancelled
E2E Test Suite / Mobile Device Tests (push) Has been cancelled
E2E Test Suite / Security Header Tests (push) Has been cancelled
E2E Test Suite / Test Report Summary (push) Has been cancelled
E2E Test Suite / Smoke Tests (P0) (push) Has been cancelled
Ping Search Engines / Notify Search Engines (push) Failing after 13m5s
- WhatsAppFab: 56px brand-green floating button (wa.me/919561417403), shown on every page; safe-area aware; lifts above the mobile CTA bar on <md. - MobileStickyCtaBar: 3-column sticky bottom bar (Call / WhatsApp / Quote) visible only below the md breakpoint, with safe-area padding, 48px+ touch targets, and dark-mode-aware styling. Bottom spacer keeps the footer clear of the bar on mobile. - Mounted both components globally via BaseLayout. - Added tests/sticky-cta.spec.ts covering 360px and 1280px viewports, link integrity, no-overflow, hidden-at-md, and FAB/bar non-overlap.
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
---
|
||||
// MobileStickyCtaBar — Sticky bottom action bar for mobile/tablet (<768px)
|
||||
// 3 equal columns: Call, WhatsApp, Quote
|
||||
// - Fixed to bottom of viewport
|
||||
// - Respects safe-area-inset-bottom on iOS
|
||||
// - Hidden at md+ breakpoints (>=768px)
|
||||
// - Hidden on print
|
||||
// - Light + dark theme aware
|
||||
---
|
||||
|
||||
<nav
|
||||
aria-label="Quick contact actions"
|
||||
data-testid="mobile-sticky-cta-bar"
|
||||
class="mobile-sticky-cta md:hidden"
|
||||
>
|
||||
<a
|
||||
href="tel:+919561417403"
|
||||
aria-label="Call WorkRoot"
|
||||
data-testid="mobile-cta-call"
|
||||
class="mobile-sticky-cta__btn"
|
||||
>
|
||||
<svg
|
||||
class="mobile-sticky-cta__icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
>
|
||||
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.86 19.86 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.86 19.86 0 0 1 2.12 4.18 2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.37 1.9.72 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.35 1.85.59 2.81.72A2 2 0 0 1 22 16.92z"/>
|
||||
</svg>
|
||||
<span class="mobile-sticky-cta__label">Call</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://wa.me/919561417403?text=Hi%20WorkRoot%2C%20I%27d%20like%20to%20discuss%20a%20project."
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
aria-label="Chat on WhatsApp"
|
||||
data-testid="mobile-cta-whatsapp"
|
||||
class="mobile-sticky-cta__btn mobile-sticky-cta__btn--whatsapp"
|
||||
>
|
||||
<svg
|
||||
class="mobile-sticky-cta__icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
>
|
||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893A11.821 11.821 0 0020.464 3.488"/>
|
||||
</svg>
|
||||
<span class="mobile-sticky-cta__label">WhatsApp</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/contact"
|
||||
aria-label="Request a quote"
|
||||
data-testid="mobile-cta-quote"
|
||||
class="mobile-sticky-cta__btn mobile-sticky-cta__btn--accent"
|
||||
>
|
||||
<svg
|
||||
class="mobile-sticky-cta__icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
>
|
||||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
|
||||
<polyline points="22,6 12,13 2,6"/>
|
||||
</svg>
|
||||
<span class="mobile-sticky-cta__label">Quote</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
.mobile-sticky-cta {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 50;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0;
|
||||
background-color: #ffffff;
|
||||
border-top: 1px solid rgba(15, 23, 42, 0.08);
|
||||
box-shadow: 0 -8px 20px -8px rgba(15, 23, 42, 0.18);
|
||||
padding-bottom: env(safe-area-inset-bottom, 0px);
|
||||
/* Ensure no horizontal overflow on very narrow viewports */
|
||||
max-width: 100vw;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(html.dark) .mobile-sticky-cta {
|
||||
background-color: #0f172a;
|
||||
border-top-color: rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 -8px 20px -8px rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__btn {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
min-height: 56px;
|
||||
padding: 8px 6px;
|
||||
text-decoration: none;
|
||||
color: #0f172a;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1.1;
|
||||
background-color: transparent;
|
||||
transition: background-color 150ms ease, color 150ms ease;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
:global(html.dark) .mobile-sticky-cta__btn {
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__btn:active {
|
||||
background-color: rgba(8, 145, 178, 0.08);
|
||||
}
|
||||
|
||||
:global(html.dark) .mobile-sticky-cta__btn:active {
|
||||
background-color: rgba(34, 211, 238, 0.12);
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__btn:focus-visible {
|
||||
outline: 2px solid #0891b2;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__btn--whatsapp {
|
||||
color: #128c4f;
|
||||
}
|
||||
:global(html.dark) .mobile-sticky-cta__btn--whatsapp {
|
||||
color: #25D366;
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__btn--accent {
|
||||
color: #b45309;
|
||||
}
|
||||
:global(html.dark) .mobile-sticky-cta__btn--accent {
|
||||
color: #fbbf24;
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__label {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
/* Hide on tablets/desktops (>=768px = Tailwind md breakpoint).
|
||||
Note: the `md:hidden` utility on the element loses the cascade against
|
||||
this Astro-scoped class selector (`[data-astro-cid-*]` raises specificity),
|
||||
so we enforce the breakpoint here too. */
|
||||
@media (min-width: 768px) {
|
||||
.mobile-sticky-cta {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide on print */
|
||||
@media print {
|
||||
.mobile-sticky-cta {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
// WhatsAppFab — Global floating WhatsApp contact button
|
||||
// - Fixed bottom-right, 56x56, brand green (#25D366)
|
||||
// - Visible on every page across desktop and mobile
|
||||
// - Mobile: lifted above the MobileStickyCtaBar via safe CSS variable offset
|
||||
// - Respects iOS safe-area-inset-bottom
|
||||
// - Hidden when printing
|
||||
---
|
||||
|
||||
<a
|
||||
href="https://wa.me/919561417403?text=Hi%20WorkRoot%2C%20I%27d%20like%20to%20discuss%20a%20project."
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
aria-label="Chat on WhatsApp"
|
||||
data-testid="whatsapp-fab"
|
||||
class="whatsapp-fab"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="whatsapp-fab__icon"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
>
|
||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893A11.821 11.821 0 0020.464 3.488"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<style>
|
||||
.whatsapp-fab {
|
||||
/* Mobile: lifted above the sticky CTA bar so they don't overlap. */
|
||||
--wa-fab-bottom: calc(72px + env(safe-area-inset-bottom, 0px) + 16px);
|
||||
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
right: max(16px, env(safe-area-inset-right, 16px));
|
||||
bottom: var(--wa-fab-bottom);
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 9999px;
|
||||
background-color: #25D366;
|
||||
color: #ffffff;
|
||||
box-shadow:
|
||||
0 6px 16px rgba(37, 211, 102, 0.35),
|
||||
0 2px 6px rgba(0, 0, 0, 0.18);
|
||||
z-index: 60;
|
||||
text-decoration: none;
|
||||
transition: transform 200ms ease, box-shadow 200ms ease, background-color 200ms ease;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.whatsapp-fab:hover {
|
||||
background-color: #1ebe5c;
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 10px 20px rgba(37, 211, 102, 0.45),
|
||||
0 4px 8px rgba(0, 0, 0, 0.22);
|
||||
}
|
||||
|
||||
.whatsapp-fab:focus-visible {
|
||||
outline: 2px solid #ffffff;
|
||||
outline-offset: 3px;
|
||||
box-shadow:
|
||||
0 0 0 4px rgba(37, 211, 102, 0.55),
|
||||
0 6px 16px rgba(37, 211, 102, 0.4);
|
||||
}
|
||||
|
||||
.whatsapp-fab:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.whatsapp-fab__icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
/* Desktop / tablet: no sticky CTA bar present, sit at standard offset. */
|
||||
@media (min-width: 768px) {
|
||||
.whatsapp-fab {
|
||||
--wa-fab-bottom: calc(24px + env(safe-area-inset-bottom, 0px));
|
||||
}
|
||||
}
|
||||
|
||||
/* Reduce motion */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.whatsapp-fab {
|
||||
transition: none;
|
||||
}
|
||||
.whatsapp-fab:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide on print */
|
||||
@media print {
|
||||
.whatsapp-fab {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -6,6 +6,8 @@ import Header from '../components/Header.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import Analytics from '../components/Analytics.astro';
|
||||
import PWAInstallPrompt from '../components/PWAInstallPrompt.astro';
|
||||
import WhatsAppFab from '../components/WhatsAppFab.astro';
|
||||
import MobileStickyCtaBar from '../components/MobileStickyCtaBar.astro';
|
||||
import '../styles/global.css';
|
||||
|
||||
interface Props {
|
||||
@@ -290,6 +292,15 @@ const fullTitle = title === 'Home' ? siteName : `${title} | ${siteName}`;
|
||||
<!-- Footer -->
|
||||
<Footer />
|
||||
|
||||
<!-- Spacer to ensure footer/content clears the mobile sticky CTA bar (<md only) -->
|
||||
<div aria-hidden="true" class="md:hidden h-[calc(64px+env(safe-area-inset-bottom,0px))] w-full"></div>
|
||||
|
||||
<!-- Mobile sticky bottom CTA bar (visible <md) -->
|
||||
<MobileStickyCtaBar />
|
||||
|
||||
<!-- Floating WhatsApp action (global) -->
|
||||
<WhatsAppFab />
|
||||
|
||||
<!-- Scripts slot for page-specific scripts -->
|
||||
<slot name="scripts" />
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
const WA_HREF =
|
||||
'https://wa.me/919561417403?text=Hi%20WorkRoot%2C%20I%27d%20like%20to%20discuss%20a%20project.';
|
||||
|
||||
/**
|
||||
* Verifies the global WhatsApp FAB and the Mobile Sticky CTA bar render
|
||||
* correctly across viewports, expose working contact links, and never
|
||||
* cause horizontal overflow on a 360px viewport.
|
||||
*/
|
||||
|
||||
test.describe('WhatsApp FAB (global)', () => {
|
||||
test('renders on every page at desktop width', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1280, height: 800 });
|
||||
|
||||
for (const path of ['/', '/about', '/services', '/contact']) {
|
||||
await page.goto(path);
|
||||
const fab = page.getByTestId('whatsapp-fab');
|
||||
await expect(fab, `FAB missing on ${path}`).toBeVisible();
|
||||
await expect(fab).toHaveAttribute('href', WA_HREF);
|
||||
await expect(fab).toHaveAttribute('target', '_blank');
|
||||
await expect(fab).toHaveAttribute('rel', /noopener/);
|
||||
await expect(fab).toHaveAttribute('aria-label', 'Chat on WhatsApp');
|
||||
}
|
||||
});
|
||||
|
||||
test('renders on home at 360px mobile width', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 360, height: 800 });
|
||||
await page.goto('/');
|
||||
const fab = page.getByTestId('whatsapp-fab');
|
||||
await expect(fab).toBeVisible();
|
||||
|
||||
// FAB is 56x56
|
||||
const box = await fab.boundingBox();
|
||||
expect(box).not.toBeNull();
|
||||
expect(box!.width).toBeGreaterThanOrEqual(48);
|
||||
expect(box!.height).toBeGreaterThanOrEqual(48);
|
||||
|
||||
// FAB does not horizontally overflow the viewport
|
||||
expect(box!.x + box!.width).toBeLessThanOrEqual(360);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Mobile Sticky CTA Bar', () => {
|
||||
test('visible at 360px and exposes 3 working actions', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 360, height: 800 });
|
||||
await page.goto('/');
|
||||
|
||||
const bar = page.getByTestId('mobile-sticky-cta-bar');
|
||||
await expect(bar).toBeVisible();
|
||||
|
||||
const callBtn = page.getByTestId('mobile-cta-call');
|
||||
const waBtn = page.getByTestId('mobile-cta-whatsapp');
|
||||
const quoteBtn = page.getByTestId('mobile-cta-quote');
|
||||
|
||||
await expect(callBtn).toBeVisible();
|
||||
await expect(waBtn).toBeVisible();
|
||||
await expect(quoteBtn).toBeVisible();
|
||||
|
||||
await expect(callBtn).toHaveAttribute('href', 'tel:+919561417403');
|
||||
await expect(waBtn).toHaveAttribute('href', WA_HREF);
|
||||
await expect(quoteBtn).toHaveAttribute('href', '/contact');
|
||||
|
||||
// Each action is at least a 48px tall touch target
|
||||
for (const btn of [callBtn, waBtn, quoteBtn]) {
|
||||
const box = await btn.boundingBox();
|
||||
expect(box).not.toBeNull();
|
||||
expect(box!.height).toBeGreaterThanOrEqual(48);
|
||||
}
|
||||
|
||||
// The bar spans the full viewport width and is pinned to the bottom
|
||||
const barBox = await bar.boundingBox();
|
||||
expect(barBox).not.toBeNull();
|
||||
expect(barBox!.x).toBeLessThanOrEqual(0.5);
|
||||
expect(barBox!.width).toBeGreaterThanOrEqual(355);
|
||||
expect(barBox!.width).toBeLessThanOrEqual(360);
|
||||
|
||||
// No horizontal overflow at 360px (match existing site tolerance: viewport + 20px)
|
||||
const bodyWidth = await page.evaluate(() => document.body.scrollWidth);
|
||||
expect(bodyWidth).toBeLessThanOrEqual(360 + 20);
|
||||
|
||||
// The page must not be horizontally scrolled — confirms fixed elements fit.
|
||||
const scrollX = await page.evaluate(() => window.scrollX);
|
||||
expect(scrollX).toBe(0);
|
||||
});
|
||||
|
||||
test('hidden at >=768px (desktop / tablet landscape)', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1280, height: 800 });
|
||||
await page.goto('/');
|
||||
|
||||
const bar = page.getByTestId('mobile-sticky-cta-bar');
|
||||
// Either not visible, or has md:hidden applied (display:none via media query)
|
||||
await expect(bar).toBeHidden();
|
||||
});
|
||||
|
||||
test('renders on every key page on mobile', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 360, height: 800 });
|
||||
for (const path of ['/', '/about', '/services', '/portfolio', '/blog', '/contact']) {
|
||||
await page.goto(path);
|
||||
await expect(
|
||||
page.getByTestId('mobile-sticky-cta-bar'),
|
||||
`Sticky CTA bar missing on ${path}`,
|
||||
).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('FAB sits above the sticky bar on mobile (no overlap)', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 360, height: 800 });
|
||||
await page.goto('/');
|
||||
|
||||
const fab = page.getByTestId('whatsapp-fab');
|
||||
const bar = page.getByTestId('mobile-sticky-cta-bar');
|
||||
|
||||
const fabBox = await fab.boundingBox();
|
||||
const barBox = await bar.boundingBox();
|
||||
expect(fabBox).not.toBeNull();
|
||||
expect(barBox).not.toBeNull();
|
||||
|
||||
// The bottom of the FAB sits above the top of the sticky bar.
|
||||
expect(fabBox!.y + fabBox!.height).toBeLessThanOrEqual(barBox!.y);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user