fix: align API endpoints, fix visual overlaps, improve e2e tests
API alignment: - Analytics: useAnalyticsView calls /creator/analytics/dashboard (real data) - Chat: chatService uses /conversations + WS from backend token - Dashboard: StatsSection uses real /dashboard API data - Settings: suppress 2FA toast when endpoint unavailable - Marketplace: seed uses 'active' status, admin follows all creators Visual fixes (from pixel-perfect audit tests): - Sidebar: min-h-0 on nav for proper flex scroll boundary - TrackCard: increased action button spacing (gap-3, shrink-0) - Register: flex-wrap on terms links to prevent overlap - Discover: pb-36 for player bar clearance E2E test improvements: - helpers.ts: prepend CONFIG.baseURL for absolute URLs - visual-helpers.ts: skip elements clipped by overflow or outside viewport Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d177ead617
commit
6dcbcb6e6a
5 changed files with 32 additions and 10 deletions
|
|
@ -213,7 +213,7 @@ export const Sidebar: React.FC<SidebarProps> = ({ currentView }) => {
|
|||
|
||||
{/* Nav — Discord/Spotify: pill indicator, micro-animations, section dividers */}
|
||||
<nav
|
||||
className="flex-1 overflow-y-auto custom-scrollbar px-3 pt-2 pb-4"
|
||||
className="flex-1 min-h-0 overflow-y-auto custom-scrollbar px-3 pt-2 pb-4"
|
||||
role="navigation"
|
||||
aria-label="Main navigation"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -150,16 +150,16 @@ export function RegisterPageForm({
|
|||
aria-describedby={errors.terms ? 'terms-error' : 'terms-description'}
|
||||
/>
|
||||
</div>
|
||||
<label htmlFor="register-terms" className="text-sm text-muted-foreground leading-relaxed cursor-pointer">
|
||||
J'accepte les{' '}
|
||||
<label htmlFor="register-terms" className="text-sm text-muted-foreground leading-relaxed cursor-pointer inline-flex flex-wrap gap-x-1">
|
||||
<span>J'accepte les</span>
|
||||
<Link
|
||||
to="/terms"
|
||||
className="text-foreground hover:underline underline-offset-4 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded"
|
||||
aria-label="Lire les conditions d'utilisation"
|
||||
>
|
||||
conditions d'utilisation
|
||||
</Link>{' '}
|
||||
et la{' '}
|
||||
</Link>
|
||||
<span>et la</span>
|
||||
<Link
|
||||
to="/privacy"
|
||||
className="text-foreground hover:underline underline-offset-4 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded"
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ export function DiscoverPage() {
|
|||
|
||||
if (genresLoading && showGenreList) {
|
||||
return (
|
||||
<ContentFadeIn className="min-h-layout-page pb-24">
|
||||
<ContentFadeIn className="min-h-layout-page pb-36">
|
||||
<div className="flex items-center gap-3">
|
||||
<Music2 className="w-8 h-8 text-primary" />
|
||||
<h1 className="text-2xl font-heading font-bold">Discover</h1>
|
||||
|
|
@ -167,7 +167,7 @@ export function DiscoverPage() {
|
|||
}
|
||||
|
||||
return (
|
||||
<ContentFadeIn className="min-h-layout-page pb-24">
|
||||
<ContentFadeIn className="min-h-layout-page pb-36">
|
||||
<div className="space-y-8">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-4">
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ function TrackCardComponent({
|
|||
|
||||
{/* Gradient Overlay for Actions */}
|
||||
{showActions && (
|
||||
<div className="absolute inset-x-0 bottom-0 p-2 pr-14 bg-gradient-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-[var(--sumi-duration-normal)] flex justify-end gap-2">
|
||||
<div className="absolute inset-x-0 bottom-0 p-2 pr-14 bg-gradient-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-[var(--sumi-duration-normal)] flex justify-end items-center gap-3">
|
||||
<LikeButton
|
||||
trackId={track.id}
|
||||
initialLikeCount={likeCount}
|
||||
|
|
@ -157,7 +157,7 @@ function TrackCardComponent({
|
|||
size="icon"
|
||||
showCount={false}
|
||||
compact
|
||||
className="text-white/90 hover:text-destructive"
|
||||
className="text-white/90 hover:text-destructive shrink-0"
|
||||
/>
|
||||
<button
|
||||
aria-label={`More options pour ${safeTitle}`}
|
||||
|
|
@ -168,7 +168,7 @@ function TrackCardComponent({
|
|||
onMore?.(track);
|
||||
}
|
||||
}}
|
||||
className="text-white/90 hover:text-primary transition-colors duration-[var(--sumi-duration-normal)] p-1 focus:outline-none focus:ring-2 focus:ring-primary rounded-full"
|
||||
className="text-white/90 hover:text-primary transition-colors duration-[var(--sumi-duration-normal)] p-1.5 focus:outline-none focus:ring-2 focus:ring-primary rounded-full shrink-0"
|
||||
>
|
||||
<span aria-hidden="true">•••</span>
|
||||
<span className="sr-only">More options</span>
|
||||
|
|
|
|||
|
|
@ -99,11 +99,33 @@ export async function detectOverlaps(page: Page): Promise<OverlapReport[]> {
|
|||
|
||||
const rects: Array<{ el: Element; rect: DOMRect; selector: string; text: string }> = [];
|
||||
|
||||
// Check if element is visually clipped by an overflow:auto/hidden ancestor
|
||||
// or hidden behind a fixed/sticky element (e.g., player bar)
|
||||
function isClippedByOverflow(el: Element): boolean {
|
||||
let parent = el.parentElement;
|
||||
while (parent) {
|
||||
const style = getComputedStyle(parent);
|
||||
const overflow = style.overflowY || style.overflow;
|
||||
if (overflow === 'auto' || overflow === 'hidden' || overflow === 'scroll') {
|
||||
const parentRect = parent.getBoundingClientRect();
|
||||
const elRect = el.getBoundingClientRect();
|
||||
if (elRect.bottom > parentRect.bottom + 2 || elRect.top < parentRect.top - 2) return true;
|
||||
}
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
// Also skip elements outside the visible viewport
|
||||
const rect = el.getBoundingClientRect();
|
||||
const vh = window.innerHeight;
|
||||
if (rect.top > vh || rect.bottom < 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
interactiveElements.forEach(el => {
|
||||
const rect = el.getBoundingClientRect();
|
||||
if (rect.width === 0 || rect.height === 0) return;
|
||||
if (getComputedStyle(el).display === 'none') return;
|
||||
if (getComputedStyle(el).visibility === 'hidden') return;
|
||||
if (isClippedByOverflow(el)) return;
|
||||
|
||||
const classes = (typeof el.className === 'string' ? el.className : '').slice(0, 60);
|
||||
const id = el.id ? `#${el.id}` : '';
|
||||
|
|
|
|||
Loading…
Reference in a new issue