Skip to content

Lauranosaure/dailymotion-interview

Repository files navigation

Dailymotion Interview

A React front-end prototype inspired by Dailymotion: home feed, search, and video watch experience with vertical/horizontal orientation, EN/FR i18n, and dark/light theme.

Live demo: dailymotion-interview.vercel.app

Overview

This project implements two main routes:

  • Home / search (/) — discovery feed (“For You”), hero search, topic filters, user-aware search (handle matching + relevance ranking), orientation toggle, and load-more pagination.
  • Watch (/videos/:videoId) — embedded player, author info, expandable description, comments, and related videos (layout adapts to vertical vs horizontal video).

The UI is built from scratch as a deliberate choice (no component library beyond Radix primitives for accessible menus, toasts, and tooltips). Data comes from the Dailymotion public API, with a few client-side layers on top where the API alone does not match the desired UX.

Tech stack

Layer Choice
UI React 19, TypeScript
Build Vite 5
Routing React Router 7
Styling Co-located CSS, design tokens (tokens.css), CSS variables for theming
Accessibility Radix UI (Popover, Toast, Tooltip)
Icons react-icons
i18n JSON locale files bundled at build time (en / fr)
Tests Vitest (unit), Playwright (e2e)
Lint ESLint 9 + TypeScript ESLint

Getting started

Requirements: Node.js 20+ and npm.

npm install
npm run dev

Open http://localhost:5173.

Scripts

Command Description
npm run dev Development server (includes comments proxy — see below)
npm run build Type-check + production build
npm run preview Preview production build locally
npm run test Run unit tests once
npm run test:watch Run unit tests in watch mode
npm run test:e2e Run Playwright end-to-end tests
npm run test:e2e:ui Run Playwright with interactive UI
npm run lint ESLint

No environment variables are required; the app calls the public Dailymotion API directly from the browser.

Comments in development

Dailymotion’s legacy /video/{id}/comments endpoint often returns empty results for newer “Neon” comments. In dev and preview, a Vite plugin (vite/dmCommentsPlugin.ts) fetches the watch page HTML server-side and exposes parsed comments at /api/dm/comments/:videoId.

In a static production deploy (dist/ only), that middleware is not available — comments fall back to the legacy API and may appear empty on some videos. This is documented as a known tradeoff below.

Project structure

src/
├── app/              Route definitions
├── pages/            SearchPage, VideoPage (+ hooks)
├── components/       UI by domain (layout, home, video, search)
├── hooks/            Shared hooks (e.g. desktop carousel nav)
├── constants/        Layout breakpoints + E2E test IDs
├── lib/              API client, feed curation, search ranking
├── utils/            Pure helpers (video, comments parsing, text)
├── types/            Shared TypeScript types
├── i18n/             Provider + locale JSON (en/fr)
├── theme/            Dark/light mode
└── styles/           Global styles + design tokens (spacing, z-index, breakpoints)

e2e/                  Playwright specs
test-fixtures/        Captured HTML for unit tests
vite/                 Vite plugins (comments proxy)

Approach

Design tokens

Shared layout values live in src/styles/tokens.css (spacing, z-index scale, radii) and src/constants/layout.ts (901px desktop breakpoint for JS media queries and image sizes). CSS uses (width >= 901px) / (width <= 900px) consistently. Component styles are co-located — e.g. VideoPlayer.css and VideoRelated.css own their own classes instead of VideoPage.css styling child components.

API layer first

All Dailymotion fetching and response normalization live in src/lib/ (dailymotionApi.ts, discoveryFeed.ts). Components and hooks consume typed data — they do not build URLs or parse dotted API field names.

Search as a product feature, not just ?search=

Search goes beyond a single API call:

  • User handle priority — resolves @username, partial matches (e.g. nozman → Dr Nozman), merges creator videos with keyword results.
  • Client-side relevance — when sort is “Most relevant”, results are re-ranked using title, description, and owner fields (lib/searchRanking.ts).
  • Orientation filter — API returns both formats; vertical/horizontal filtering happens client-side via aspect_ratio / dimensions.

Feed curation

The raw “trending” public Dailymotion API seems to skew toward low-quality vertical channels. discoveryFeed.ts applies client-side curation: exclude noisy channels, minimum view thresholds and orientation filter across categories when the feed is thin.

Page vs component responsibilities

  • Pages wire hooks, URL state, and layout.
  • Components handle presentation; shared search UI lives under components/search/.
  • Hooks on SearchPage (useSearchCatalog, useDisplayedVideos) isolate fetch/state from render.

i18n and theming

Translations are the single source of truth in src/i18n/locales/{en,fr}/*.json, imported at build time (no runtime fetch, no flash of English). Theme uses data-theme on <html> with CSS variables.

Tradeoffs

Decision Why Cost
No React Query / SWR Scope fits local useState + useEffect; fewer dependencies Manual loading/error boilerplate; no cache/dedup
Client-side orientation filter API has no reliable orientation param for all endpoints Extra API rows fetched, filtered in browser
Client-side feed curation Trending quality varies; no backend to curate Heuristic tuning; not deterministic across time
HTML comment scraping Legacy comments API empty for Neon content Fragile if DOM changes but is only a problem on public API
Plain CSS vs CSS modules Faster iteration, matches interview scope and easier to read code Global class names; requires discipline
Local-only interactions Like, save, follow are UI demos because of no API key State resets on navigation
Strict TypeScript Catches bad API shapes early Slightly more typing around Dailymotion’s dotted fields

Testing

Tests focus on code we own and failures that would be silent (wrong order, empty comments, bad parsing) — not on “does React render”.

Area File Why
Comment HTML parsing utils/parseWatchPageComments.test.ts DOM structure can change; fixture-based
Comment sort + French dates utils/commentSort.test.ts Custom Top/Recent logic
API → watch fallback lib/getVideoComments.test.ts Dual-fetch + abort path
Search relevance lib/searchRanking.test.ts Client ranking has no API contract
Orientation + dedup utils/video.test.ts User-facing filter + load-more merge
Description sanitization utils/videoText.test.ts External HTML in descriptions

Intentionally not tested (unit): full page smoke tests, Radix/component snapshots, live Dailymotion API calls in Vitest, discoveryFeed curation (depends on live trending data — would need heavy mocking for little confidence).

Unit tests

npm test              # run once
npm run test:watch    # watch mode

Fixtures live in test-fixtures/ (captured watch-page HTML for comment parsing).

End-to-end (Playwright)

Two specs cover the core journeys against a running dev server and the live Dailymotion API. Selectors use shared data-testid values from src/constants/testIds.ts.

Spec Flow
e2e/happy-path.spec.ts Home feed → first video → watch page
e2e/search-path.spec.ts Hero search (music) → results → watch page
# First time only: install browser binaries
npx playwright install chromium

# Linux/WSL: install OS dependencies if the browser fails to launch
# npx playwright install-deps chromium

npm run test:e2e

Playwright starts npm run dev automatically (reuseExistingServer locally if one is already running). Requires network access for API calls.

Possible improvements

Given more time or a production target:

  1. Comments — Move watch-page fetch to a small serverless/edge function so production gets the same comment path as dev.
  2. Data fetching — TanStack Query for caching, dedup, stale-while-revalidate, and simpler pagination.
  3. Search load-more — Paginate matched-user videos (carousel currently shows page 1 only).
  4. Feed curation — Extract pure curation functions from discoveryFeed.ts and unit-test them; tune with recorded API fixtures.
  5. Error boundaries — Graceful error UI instead of white screen on route/render failures.
  6. More E2E — Search flow, orientation toggle, i18n switch; run against Vercel preview in CI.
  7. CI — GitHub Actions: lint, test, test:e2e, build on every push.
  8. Skeleton UI - Add skeleton UI to loading components so that layout does not shift when fetching content.

Built as a front-end technical exercise for Dailymotion.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors