Skip to content

Commit

Permalink
Merge pull request #214 from NYPL/DR-3056/feature/next-image
Browse files Browse the repository at this point in the history
DR-3056: Next Image in Card component
  • Loading branch information
7emansell authored Oct 29, 2024
2 parents c03f650 + 5642fc8 commit 35a9852
Show file tree
Hide file tree
Showing 14 changed files with 139 additions and 46 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Updated

- Updated cards to use Next Image (DR-3056)
- Updated links in QA to point internally for reverse proxy (DR-3237)
- Updated how env vars are read for New Relic Browser implementation (DR-3235)

Expand Down
3 changes: 3 additions & 0 deletions app/src/components/card/card.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { mockItems } from "__tests__/__mocks__/data/mockItems";
describe("Collection DCCard component", () => {
const mockCollectionProps = {
cardOffset: [0, -130],
imageHeight: 144,
slug: "test-slug",
id: "1",
isLargerThanLargeTablet: true,
Expand All @@ -15,6 +16,7 @@ describe("Collection DCCard component", () => {

const mockCollectionPropsNoOnSite = {
cardOffset: [0, -130],
imageHeight: 144,
slug: "test-slug",
id: "1",
isLargerThanLargeTablet: true,
Expand Down Expand Up @@ -60,6 +62,7 @@ describe("Collection DCCard component", () => {
describe("Item DCCard component", () => {
const mockItemProps = {
cardOffset: [0, -130],
imageHeight: 144,
id: "1",
isLargerThanLargeTablet: true,
record: new ItemCardModel(mockItems[0]),
Expand Down
36 changes: 11 additions & 25 deletions app/src/components/card/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,28 @@ import { TRUNCATED_LENGTH } from "@/src/config/constants";
import ItemCardDataType from "@/src/types/ItemCardDataType";
import { CollectionCardDataType } from "../../types/CollectionCardDataType";
import { Offset } from "@/src/hooks/useTooltipOffset";
import CardImage from "./cardImage";
import { stringToSlug } from "@/src/utils/utils";
interface DCCardProps {
export interface DCCardProps {
tooltipOffset?: Offset;
id: string;
isLargerThanLargeTablet: boolean;
slug?: string;
imageHeight: number;
record: CollectionCardDataType | ItemCardDataType;
}

function isCollectionCardDataType(
export function isCollectionCardDataType(
record: CollectionCardDataType | ItemCardDataType
): record is CollectionCardDataType {
return "numberOfDigitizedItems" in record;
}

export const Card = forwardRef<HTMLDivElement, DCCardProps>(
({ tooltipOffset, id, isLargerThanLargeTablet, slug, record }, ref) => {
(
{ tooltipOffset, imageHeight, id, isLargerThanLargeTablet, slug, record },
ref
) => {
const truncatedTitle = record.title.length > TRUNCATED_LENGTH;
const isCollection = isCollectionCardDataType(record);
const identifier = slug
Expand All @@ -41,28 +46,9 @@ export const Card = forwardRef<HTMLDivElement, DCCardProps>(
ref={ref}
id={`card-${identifier}`}
mainActionLink={record.url}
imageProps={
record.imageID
? {
alt: "",
id: `image-${identifier}`,
isLazy: true,
aspectRatio: "twoByOne",
fallbackSrc: "/noImage.png",
onError: (_event) =>
console.warn(
`Card image failed to load, fallback image loaded instead. ImageURL: ${record.imageURL}`
),
src: record.imageURL,
}
: {
alt: "",
id: `no-image-${identifier}`,
isLazy: true,
aspectRatio: "twoByOne",
src: "/noImage.png",
}
}
imageProps={{
component: <CardImage imageHeight={imageHeight} record={record} />,
}}
>
<CardContent>
{isCollection && record.containsOnSiteMaterials && (
Expand Down
49 changes: 49 additions & 0 deletions app/src/components/card/cardImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Image from "next/image";
import React from "react";
import { useState } from "react";
import { DCCardProps } from "./card";

interface CardImageProps extends Pick<DCCardProps, "record"> {
imageHeight: number;
}

export const CardImage = ({ record, imageHeight }: CardImageProps) => {
const [imageSrc, setImageSrc] = useState(
record.imageID ? record.imageURL : "/noImage.png"
);
const initialImageHeight = 144;
return (
<div
style={{
overflow: "hidden",
height: imageHeight,
}}
>
<Image
src={imageSrc}
alt=""
id={
record.imageID
? `image-${record.imageID}`
: `no-image-${record.imageID}`
}
sizes="(max-width: 480px) 100vw, (max-width: 1024px) 50vw, 25vw"
style={{
width: "100%",
minHeight: "100%",
height: "auto",
}}
width={initialImageHeight * 2}
height={initialImageHeight}
onError={(_event) => {
console.warn(
`CardImage: Card image failed to load, fallback image loaded instead. ImageURL: ${record.imageURL}`
);
setImageSrc("/noImage.png");
}}
/>
</div>
);
};

export default CardImage;
19 changes: 12 additions & 7 deletions app/src/components/exploreFurther/exploreFurther.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import {
import { ExploreFurtherDataType } from "../../types/ExploreFurtherDataType";
import exploreFurtherData from "../../data/exploreFurtherData";
import { headerBreakpoints } from "../../utils/breakpoints";
import Image from "next/image";

const ExploreFurther = () => {
const data: ExploreFurtherDataType[] = exploreFurtherData;

return (
<Box
sx={{
Expand Down Expand Up @@ -49,13 +49,18 @@ const ExploreFurther = () => {
data-testid={`test-id-${index}`}
mainActionLink={item.url}
imageProps={{
alt: "",
aspectRatio: "sixteenByNine",
component: undefined,
component: (
<Image
src={item.image}
alt={""}
sizes="(max-width: 600px) 100vw, 25vw"
style={{
width: "100%",
minHeight: "100%",
}}
/>
),
isAtEnd: false,
isLazy: true,
size: "default",
src: `/${item.image}`,
}}
layout="row"
sx={{
Expand Down
6 changes: 3 additions & 3 deletions app/src/components/featuredItem/campaignHero.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ describe("Campaign Hero", () => {
const mockFeaturedItemData = {
featuredItem: {
imageID: "1269908",
backgroundImageSrc: "foobar.jpg", //bad image
foregroundImageSrc: "foobar.jpg",
backgroundImageSrc: "/foobar.jpg", //bad image
foregroundImageSrc: "/foobar.jpg",
uuid: "510d47e0-cb17-a3d9-e040-e00a18064a99",
title: "Momoyogusa",
href: "https://digitalcollections.nypl.org/items/510d47e0-cb17-a3d9-e040-e00a18064a99",
Expand All @@ -35,7 +35,7 @@ describe("Campaign Hero", () => {
fireEvent.error(screen.getByRole("img"));

expect(
screen.getByText("Watuppa, From water front, Brooklyn") //default fetured item title
screen.getByText("Watuppa, From water front, Brooklyn") //default featured item title
).toBeInTheDocument();
});
});
Expand Down
4 changes: 4 additions & 0 deletions app/src/components/grids/cardsGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ItemCardModel } from "@/src/models/itemCard";
import { SimpleGrid as DCSimpleGrid } from "../simpleGrid/simpleGrid";
import { Card as DCCard } from "../card/card";
import { useTooltipOffset } from "@/src/hooks/useTooltipOffset";
import { useCardImageHeight } from "@/src/hooks/useCardImageHeight";

interface CardsGridProps {
records: CollectionDataType[] | ItemDataType[];
Expand All @@ -20,6 +21,7 @@ export const CardsGrid = ({ records }: CardsGridProps) => {
const isCollections = isCollectionType(records);
const cardRef = useRef<HTMLDivElement>(null);
const tooltipOffset = useTooltipOffset(cardRef);
const imageHeight = useCardImageHeight(cardRef);

return (
<DCSimpleGrid>
Expand All @@ -32,6 +34,7 @@ export const CardsGrid = ({ records }: CardsGridProps) => {
id={index}
ref={cardRef}
tooltipOffset={tooltipOffset}
imageHeight={imageHeight}
record={collectionCardModel}
isLargerThanLargeTablet={isLargerThanLargeTablet}
/>
Expand All @@ -44,6 +47,7 @@ export const CardsGrid = ({ records }: CardsGridProps) => {
id={index}
ref={cardRef}
tooltipOffset={tooltipOffset}
imageHeight={imageHeight}
record={itemCardModel}
isLargerThanLargeTablet={isLargerThanLargeTablet}
/>
Expand Down
4 changes: 4 additions & 0 deletions app/src/components/lane/lane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import ItemDataType from "@/src/types/ItemDataType";
import useBreakpoints from "@/src/hooks/useBreakpoints";
import { useTooltipOffset } from "@/src/hooks/useTooltipOffset";
import { isCollectionType } from "@/src/utils/utils";
import { useCardImageHeight } from "@/src/hooks/useCardImageHeight";

interface LaneProps {
seeMoreLink: string;
Expand All @@ -37,6 +38,7 @@ export const Lane = ({
const { isLargerThanLargeTablet } = useBreakpoints();
const cardRef = useRef<HTMLDivElement>(null);
const tooltipOffset = useTooltipOffset(cardRef);
const imageHeight = useCardImageHeight(cardRef);
const isCollections = isCollectionType(records);

const laneContents = isCollections
Expand Down Expand Up @@ -121,6 +123,7 @@ export const Lane = ({
isLargerThanLargeTablet={isLargerThanLargeTablet}
ref={cardRef}
tooltipOffset={tooltipOffset}
imageHeight={imageHeight}
/>
);
} else {
Expand All @@ -133,6 +136,7 @@ export const Lane = ({
isLargerThanLargeTablet={isLargerThanLargeTablet}
tooltipOffset={tooltipOffset}
ref={cardRef}
imageHeight={imageHeight}
/>
);
}
Expand Down
15 changes: 10 additions & 5 deletions app/src/data/exploreFurtherData.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,49 @@
import { ExploreFurtherDataType } from "../types/ExploreFurtherDataType";
import serviceArtehouse from "../../../public/service-artehouse.jpg";
import researchCatalog from "../../../public/ResearchCatalogThumbnail_v2.jpg";
import serviceArchives from "../../../public/service-archives.jpg";
import serviceApi from "../../../public/service-api.jpg";
import serviceDpla from "../../../public/service-dpla.jpg";

const exploreFurtherData: ExploreFurtherDataType[] = [
{
title: "Digital Collections Print Store",
description:
"Decorative prints for purchase: choose from archival prints, framed art, stretched canvas, vintage wood, and wall murals.",
url: "http://nypl.artehouse.com/perl/home.pl",
image: "service-artehouse.jpg",
image: serviceArtehouse,
imgAlt: "Service Artehouse",
},
{
title: "NYPL Archives and Manuscripts",
description:
"Contains finding aids for over 10,000 unique collections (digitized and non-digitized) in almost every conceivable format.",
url: "http://archives.nypl.org/",
image: "service-archives.jpg",
image: serviceArchives,
imgAlt: "Service Archives",
},
{
title: "NYPL Research Catalog",
description:
"Discover NYPL's world-renowned research collections, featuring more than 46 million items. Plus, access materials from library collections at Columbia University, Harvard University, and Princeton University.",
url: "https://www.nypl.org/research/research-catalog/ ",
image: "ResearchCatalogThumbnail_v2.jpg",
image: researchCatalog,
imgAlt: "NYPL Research Catalog",
},
{
title: "NYPL Digital Collections API",
description:
"The Library's digitized collections are available as machine-readable data: over 1 million objects and records for you to search, crawl and compute.",
url: "https://api.repo.nypl.org/",
image: "service-api.jpg",
image: serviceApi,
imgAlt: "Service API",
},
{
title: "Digital Public Library of America",
description:
"Brings together the riches of America's libraries, archives, and museums, and makes them freely available to the world.",
url: "http://dp.la/",
image: "service-dpla.jpg",
image: serviceDpla,
imgAlt: "Service Digital Public Library",
},
];
Expand Down
25 changes: 25 additions & 0 deletions app/src/hooks/useCardImageHeight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState, useEffect } from "react";

/**
* Calculates the height of the image in the DCCard component as it resizes with the window,
* returns it to the CardImage so its parent container can cut off overflow accordingly.
*/

export function useCardImageHeight(cardRef) {
const [imageHeight, setImageHeight] = useState(144);
useEffect(() => {
const updateImageHeight = () => {
if (cardRef.current) {
const image = cardRef.current as HTMLElement;
const imageWidth = image.offsetWidth;

setImageHeight(0.5 * imageWidth);
}
};
updateImageHeight();
window.addEventListener("resize", updateImageHeight);

return () => window.removeEventListener("resize", updateImageHeight);
}, []);
return imageHeight;
}
3 changes: 2 additions & 1 deletion app/src/types/ExploreFurtherDataType.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { StaticImageData } from "next/image";
export interface ExploreFurtherDataType {
title: string;
url: string;
description: string;
imgAlt: string;
image: string;
image: StaticImageData;
}
1 change: 1 addition & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const nextConfig = {
hostname: "iiif-qa.nypl.org",
},
],
deviceSizes: [480, 768, 1024, 1280],
},
generateEtags: false,
// In order for newrelic to effectively instrument a Next.js application,
Expand Down
Loading

0 comments on commit 35a9852

Please sign in to comment.