Repository: expo/expo
File: docs/ui/components/VideoBoxLink/index.tsx (TypeScript)

Code before:
import { PlaySolidIcon } from '@expo/styleguide-icons/solid/PlaySolidIcon';
import { type ReactNode } from 'react';

import { StructuredData } from '~/ui/components/StructuredData';
import { CALLOUT, LABEL } from '~/ui/components/Text';

type VideoBoxLinkProps = {
  title: string;
  description: ReactNode;
  videoId: string;
  time?: number;
  className?: string;
};

export function VideoBoxLink({ title, description, videoId, time, className }: VideoBoxLinkProps) {
  const videoStructuredData = {
    '@context': 'https://schema.org',
    '@type': 'VideoObject',
    name: title,
    description: title,
    thumbnailUrl: `https://i3.ytimg.com/vi/${videoId}/maxresdefault.jpg`,
    embedUrl: `https://www.youtube.com/embed/${videoId}`,
    contentUrl: `https://www.youtube.com/watch?v=${videoId}`,
  };

  return (
    <>
      <StructuredData data={videoStructuredData} id={`video-${videoId}`} />
      <LinkBase
        openInNewTab
        href={`https://www.youtube.com/watch?v=${videoId}${time ? `&t=${time}` : ''}`}
        className={mergeClasses(
          'group relative flex items-stretch overflow-hidden rounded-lg border border-default bg-default shadow-xs transition',
          'hocus:bg-subtle hocus:shadow-sm',
          'max-sm-gutters:flex-col',
          '[&+hr]:!mt-6',
          className
        )}
        aria-label={`Watch video: ${title} (opens in new tab)`}>
        <div
          className={mergeClasses(
            'relative flex max-w-[200px] items-center justify-center overflow-hidden border-r border-secondary bg-element',
            'max-sm-gutters:max-w-full max-sm-gutters:border-b max-sm-gutters:border-r-0'
          )}>
          <img
            src={`https://i3.ytimg.com/vi/${videoId}/maxresdefault.jpg`}
            className="aspect-video transition duration-300 group-hover:scale-105 group-focus-visible:scale-105"
            alt={title}
            aria-label={`Video thumbnail for ${title}`}
          />
          <div

Review comment (suggestion):
We're missing the [required](https://developers.google.com/search/docs/appearance/structured-data/video#video-object) `uploadDate` property.

Code after:
import { PlaySolidIcon } from '@expo/styleguide-icons/solid/PlaySolidIcon';
import { type ReactNode } from 'react';

import { getVideoUploadDate } from '~/public/static/talks';
import { StructuredData } from '~/ui/components/StructuredData';
import { CALLOUT, LABEL } from '~/ui/components/Text';

type VideoBoxLinkProps = {
  title: string;
  description: ReactNode;
  videoId: string;
  time?: number;
  className?: string;
};

export function VideoBoxLink({ title, description, videoId, time, className }: VideoBoxLinkProps) {
  const uploadDate = getVideoUploadDate(videoId);
  const videoStructuredData = {
    '@context': 'https://schema.org',
    '@type': 'VideoObject',
    name: title,
    thumbnailUrl: `https://i3.ytimg.com/vi/${videoId}/maxresdefault.jpg`,
    embedUrl: `https://www.youtube.com/embed/${videoId}`,
    contentUrl: `https://www.youtube.com/watch?v=${videoId}`,
    ...(uploadDate && { uploadDate }),
  };

  return (
    <>
      <StructuredData data={videoStructuredData} id={`video-${videoId}`} />
      <LinkBase
        openInNewTab
        href={`https://www.youtube.com/watch?v=${videoId}${time ? `&t=${time}` : ''}`}
        className={mergeClasses(
          'group relative flex items-stretch overflow-hidden rounded-lg border border-default bg-default shadow-xs transition',
          'hocus:bg-subtle hocus:shadow-sm',
          'max-sm-gutters:flex-col',
          '[&+hr]:!mt-6',
          className
        )}
        aria-label={`Watch video: ${title} (opens in new tab)`}>
        <div
          className={mergeClasses(
            'relative flex max-w-[200px] items-center justify-center overflow-hidden border-r border-secondary bg-element',
            'max-sm-gutters:max-w-full max-sm-gutters:border-b max-sm-gutters:border-r-0'
          )}>
          <img
            src={`https://i3.ytimg.com/vi/${videoId}/maxresdefault.jpg`}
            className="aspect-video transition duration-300 group-hover:scale-105 group-focus-visible:scale-105"
            alt={title}
            aria-label={`Video thumbnail for ${title}`}