Repository: strapi/strapi
File: packages/core/content-manager/admin/src/preview/pages/Preview.tsx (TypeScript)

Code before:
  const [isSideEditorOpen, setIsSideEditorOpen] = React.useState(true);

  // Read all the necessary data from the URL to find the right preview URL
  const {
    slug: model,
    id: documentId,
    collectionType,
  } = useParams<{
    slug: UID.ContentType;
    id: string;
    collectionType: string;
  }>();
  const [{ query }] = useQueryParams<{
    plugins?: Record<string, unknown>;
    status?: string;
  }>();

  const params = React.useMemo(() => buildValidParams(query), [query]);

  const devices = [
    {
      name: 'desktop',
      label: formatMessage({
        id: 'content-manager.preview.device.desktop',
        defaultMessage: 'Desktop',
      }),
      width: '100%',
      height: '100%',
    },
    {
      name: 'mobile',
      label: formatMessage({
        id: 'content-manager.preview.device.mobile',
        defaultMessage: 'Mobile',
      }),
      width: '375px',
      height: '667px',
    },
  ];
  const [deviceName, setDeviceName] = React.useState<(typeof devices)[number]['name']>(
    devices[0].name
  );
  const device = devices.find((d) => d.name === deviceName) ?? devices[0];

  if (!collectionType) {
    throw new Error('Could not find collectionType in url params');
  }

  if (!model) {
    throw new Error('Could not find model in url params');
  }

Review comment (suggestion):
Just a thought: you could store the translation object here and only use formatMessage in SelectOption below. Which would mean you could move the devices array outside the component to not pollute that space with a constant. Speaking of which you could consider the `DEVICES` constant casing convention. What you have is just fine too.

Code after:
  title: string;
  document: NonNullable<ReturnType<UseDocument>['document']>;
  meta: NonNullable<ReturnType<UseDocument>['meta']>;
  schema: NonNullable<ReturnType<UseDocument>['schema']>;
  layout: EditLayout;
  onPreview: () => void;
}

const [PreviewProvider, usePreviewContext] = createContext<PreviewContextValue>('PreviewPage');

/* -------------------------------------------------------------------------------------------------
 * PreviewPage
 * -----------------------------------------------------------------------------------------------*/

const AnimatedArrow = styled(ArrowLineLeft)<{ $isSideEditorOpen: boolean }>`
  will-change: transform;
  rotate: ${(props) => (props.$isSideEditorOpen ? '0deg' : '180deg')};
  transition: rotate 0.2s ease-in-out;
`;

const PreviewPage = () => {
  const location = useLocation();
  const { formatMessage } = useIntl();

  const iframeRef = React.useRef<HTMLIFrameElement>(null);
  const [isSideEditorOpen, setIsSideEditorOpen] = React.useState(true);

  // Read all the necessary data from the URL to find the right preview URL
  const {
    slug: model,
    id: documentId,
    collectionType,
  } = useParams<{
    slug: UID.ContentType;
    id: string;
    collectionType: string;
  }>();
  const [{ query }] = useQueryParams<{
    plugins?: Record<string, unknown>;
    status?: string;
  }>();

  const params = React.useMemo(() => buildValidParams(query), [query]);

  const [deviceName, setDeviceName] = React.useState<(typeof DEVICES)[number]['name']>(
    DEVICES[0].name
  );
  const device = DEVICES.find((d) => d.name === deviceName) ?? DEVICES[0];

  if (!collectionType) {
    throw new Error('Could not find collectionType in url params');