Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SCC-3776 - Item Table Component #50

Merged
merged 45 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
53583a5
Initialize item table component
dgcohen Nov 15, 2023
494a832
Restore getActivePage
dgcohen Nov 15, 2023
eb90f4b
Refactor item table headings getter function
dgcohen Nov 15, 2023
529ae04
Initialize ItemTableData model
dgcohen Nov 17, 2023
b124ffc
Initialize item table data refactor
dgcohen Nov 17, 2023
6776e03
Remove length check on volume
dgcohen Nov 17, 2023
7e4dd90
Merge branch 'main' of github.com:NYPL/research-catalog into SCC-3776…
dgcohen Nov 27, 2023
05b2cd1
Remove getActivePage util
dgcohen Nov 27, 2023
ee5c1fc
Render table content
dgcohen Nov 27, 2023
1f4806c
Render table content
dgcohen Nov 27, 2023
50a7e4a
Use tableLayout key in css prop
dgcohen Nov 27, 2023
723fd04
Initialize request buttons
dgcohen Nov 28, 2023
5ed422f
Add closed locations env variable
dgcohen Nov 28, 2023
e5046b6
Create separate item table per item on search results
dgcohen Nov 28, 2023
68cbb69
Format environment variables readme
dgcohen Nov 28, 2023
527f32b
Tweak table styles
dgcohen Nov 28, 2023
c66f51a
Tweak table styles on mobile
dgcohen Nov 28, 2023
20d8bbf
Render request link data
dgcohen Nov 28, 2023
9ecf936
Render request link data
dgcohen Nov 28, 2023
cc19610
Increase bottom margin on buttons
dgcohen Nov 28, 2023
e155254
Remove onClick handler comments
dgcohen Nov 28, 2023
e488321
Fix isRecap attribute name
dgcohen Nov 28, 2023
77d2419
Add bottom margin to information links
dgcohen Nov 28, 2023
16485d0
Start off site logic
dgcohen Nov 28, 2023
02ac042
Add not available case to information links
dgcohen Nov 29, 2023
dc145c0
Rename information links to ItemAvailability
dgcohen Nov 29, 2023
ee31a18
Fix issues with DS text components
dgcohen Nov 29, 2023
fd05fe0
Add TODO comments
dgcohen Nov 29, 2023
d369f98
Refactor closed locations logic
dgcohen Nov 29, 2023
36d0a09
Fix broken SearchResult test
dgcohen Nov 29, 2023
18ad591
Add todo comment to ItemTableData class
dgcohen Nov 29, 2023
df4ac1a
Add Todo comment in searchresult
dgcohen Nov 30, 2023
71cda65
Add urls to config
dgcohen Nov 30, 2023
0dd23d2
Add tests for item availability
dgcohen Nov 30, 2023
475c0a0
Replace exact false with regex
dgcohen Nov 30, 2023
d8c6fdc
Add test for Item due date
dgcohen Dec 1, 2023
f87badb
Add tests for request buttons
dgcohen Dec 1, 2023
d10de6d
Use buttondisabled as type when item is not available
dgcohen Dec 7, 2023
a5ae2bb
Remove isDisabled link prop from RCLink
dgcohen Dec 7, 2023
27b56f4
Fix dueDate type
dgcohen Dec 8, 2023
deaa85f
Merge branch 'SCC-3776/item-table-component' of github.com:NYPL/resea…
dgcohen Dec 8, 2023
08ef887
Merge in main and resolve conflicts
dgcohen Dec 12, 2023
624db28
Create showViewAllItems link helper
dgcohen Dec 12, 2023
417f23a
Change date format in fixture
dgcohen Dec 12, 2023
903a570
Use string for search params issuance
dgcohen Dec 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
PLATFORM_API_CLIENT_ID=[secret]
PLATFORM_API_CLIENT_SECRET=[secret]
NYPL_HEADER_URL=https://ds-header.nypl.org
NYPL_HEADER_URL=https://ds-header.nypl.org
NEXT_PUBLIC_CLOSED_LOCATIONS=""
NEXT_PUBLIC_RECAP_CLOSED_LOCATIONS=""
NEXT_PUBLIC_NON_RECAP_CLOSED_LOCATIONS=""
9 changes: 6 additions & 3 deletions ENVIRONMENTVARS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ If an environment variable is updated, make sure to restart the server for the a

These environment variables control how certain elements on the page render and where to fetch data.

| Variable | Type | Value Example | Description |
| ----------------- | ------ | ---------------------------- | ------------------------------------------------------------------------ |
| `NYPL_HEADER_URL` | string | "https://ds-header.nypl.org" | The base URL of the NYPL environment-specific header and footer scripts. |
| Variable | Type | Value Example | Description |
| ---------------------------------------- | ------ | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NYPL_HEADER_URL` | string | "https://ds-header.nypl.org" | The base URL of the NYPL environment-specific header and footer scripts. |
| `NEXT_PUBLIC_CLOSED_LOCATIONS` | string | "all;Library of the Performing Arts" | A semicolon-delimited list of strings. Include quotes around the string. All locations beginning with any string in this list will be removed from the list of request options in the `ElectronicDelivery`, `HoldRequest`, and `ItemTableRow` components. Currently used physical locations: `Schwarzman;Science;Library for the Performing Arts;Schomburg`. To close all locations, add `all`. This will also remove EDD as a request option, the 'Request' buttons, and also disable the hold request/edd forms. If `all` is not present, EDD and 'Request' buttons will still be available. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does NEXT_PUBLIC indicate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That prefix allows env variables to be used on the client side.

| `NEXT_PUBLIC_RECAP_CLOSED_LOCATIONS` | string | "" | A semicolon-delimited list of closed locations that are recap. |
| `NEXT_PUBLIC_NON_RECAP_CLOSED_LOCATIONS` | string | "" | A semicolon-delimited list of closed locations that are not recap. |

## AWS ECS Environment Variables

Expand Down
57 changes: 57 additions & 0 deletions __test__/fixtures/itemFixtures.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ItemLocationEndpoint } from "../../src/types/itemTypes"

export const itemPhysicallyRequestable = {
"@id": "res:i10572545",
"@type": ["bf:Item"],
Expand All @@ -16,6 +18,7 @@ export const itemPhysicallyRequestable = {
prefLabel: "book, limited circ, MaRLI",
},
],
dueDate: ["2023-09-03"],
eddRequestable: false,
enumerationChronology: ["no. 4 (2001)"],
formatLiteral: ["Text"],
Expand Down Expand Up @@ -282,6 +285,60 @@ export const itemNYPLReCAP = {
},
}

export const itemAvailableOnsite = {
"@id": "res:i14119377",
"@type": ["bf:Item"],
accessMessage: [
{
"@id": "accessMessage:1",
prefLabel: "Use in library",
},
],
catalogItemType: [
{
"@id": "catalogItemType:55",
prefLabel: "book, limited circ, MaRLI",
},
],
eddRequestable: true,
formatLiteral: ["Text"],
holdingLocation: [
{
"@id": "loc:mal92",
prefLabel: "Schwarzman Building M2 - General Research Room 315",
endpoint: "schwarzman" as ItemLocationEndpoint,
},
],
idBarcode: ["33433048828085"],
identifier: [
{
"@type": "bf:ShelfMark",
"@value": "JFE 93-253",
},
{
"@type": "bf:Barcode",
"@value": "33433048828085",
},
],
m2CustomerCode: ["XF"],
physRequestable: true,
physicalLocation: ["JFE 93-253"],
requestable: [true],
shelfMark: ["JFE 93-253"],
specRequestable: false,
status: [
{
"@id": "status:a",
prefLabel: "Available",
},
],
uri: "i14119377",
idNyplSourceId: {
"@type": "SierraNypl",
"@value": "14119377",
},
}

export const itemNoShelfMark = {
"@id": "res:i10572546",
"@type": ["bf:Item"],
Expand Down
6 changes: 3 additions & 3 deletions __test__/fixtures/searchResultPhysicalItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const searchResultPhysicalItems = {
},
],
eddRequestable: true,
formatLiteral: ["Text"],
formatLiteral: ["Format"],
holdingLocation: [
{
"@id": "loc:mal82",
Expand Down Expand Up @@ -119,7 +119,7 @@ export const searchResultPhysicalItems = {
},
],
eddRequestable: true,
formatLiteral: ["Text"],
formatLiteral: ["Format"],
holdingLocation: [
{
"@id": "loc:rc2ma",
Expand Down Expand Up @@ -177,7 +177,7 @@ export const searchResultPhysicalItems = {
materialType: [
{
"@id": "resourcetypes:txt",
prefLabel: "Text",
prefLabel: "Material",
},
],
mediaType: [
Expand Down
1 change: 0 additions & 1 deletion pages/search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export default function Search({ results }) {

// Map Search Results Elements from response to SearchResultBib objects
const searchResultBibs = mapElementsToSearchResultsBibs(searchResultsElements)

// Map DRB Works from response to DRBResult objects
const drbResults = mapWorksToDRBResults(drbWorks)

Expand Down
2 changes: 1 addition & 1 deletion src/components/ItemFilters/FiltersContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {

import styles from "../../../styles/components/ItemFilters.module.scss"
import type { ItemAggregation } from "../../types/filterTypes"
import { ItemFilterData, LocationFilterData } from "../../models/itemFilterData"
import { ItemFilterData, LocationFilterData } from "../../models/ItemFilterData"
import ItemFilter from "./ItemFilter"
import {
buildAppliedFiltersString,
Expand Down
2 changes: 1 addition & 1 deletion src/components/ItemFilters/ItemFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
import type { Dispatch } from "react"
import { useCallback, useEffect, useState } from "react"

import type { ItemFilterData } from "../../models/itemFilterData"
import type { ItemFilterData } from "../../models/ItemFilterData"
import type { Option, AppliedFilters } from "../../types/filterTypes"
import styles from "../../../styles/components/ItemFilters.module.scss"

Expand Down
67 changes: 67 additions & 0 deletions src/components/ItemTable/ItemAvailability.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from "react"
import { render, screen } from "@testing-library/react"
import ItemAvailability from "./ItemAvailability"
import Item from "../../models/Item"
import SearchResultsBib from "../../models/SearchResultsBib"
import {
itemNYPLReCAP,
itemPhysicallyRequestable,
itemAvailableOnsite,
itemUnavailable,
} from "../../../__test__/fixtures/itemFixtures"
import { searchResultPhysicalItems } from "../../../__test__/fixtures/searchResultPhysicalItems"

const parentBib = new SearchResultsBib(searchResultPhysicalItems)

describe("ItemAvailability", () => {
it("renders the correct link when item is available, is reCAP, and does not have an aeon url", async () => {
const item = new Item(itemNYPLReCAP, parentBib)
render(<ItemAvailability item={item} />)
expect(
screen.getByRole("link", {
name: "How do I pick up this item and when will it be ready?",
})
).toHaveAttribute(
"href",
"https://www.nypl.org/help/request-research-materials"
)
})
it("renders the correct text when item is available, has an aeon url, and has a location endpoint", async () => {
const item = new Item(itemPhysicallyRequestable, parentBib)
render(<ItemAvailability item={item} />)
expect(screen.getByText("Available by appointment")).toBeInTheDocument()
expect(
screen.getByRole("link", {
name: "Schwarzman Building - Main Reading Room 315",
})
).toHaveAttribute("href", "https://www.nypl.org/locations/schwarzman")
})
it("renders the correct text for an available onsite item", async () => {
const item = new Item(itemAvailableOnsite, parentBib)
render(<ItemAvailability item={item} />)
expect(screen.getByText("Available")).toBeInTheDocument()
expect(
screen.getByText("- Can be used on site. Please visit", { exact: false })
).toBeInTheDocument()
expect(
screen.getByRole("link", {
name: "New York Public Library - Schwarzman Building M2",
})
).toHaveAttribute("href", "https://www.nypl.org/locations/schwarzman")
expect(
screen.getByText("to submit a request in person.", { exact: false })
).toBeInTheDocument()
})
it("renders the correct text for unavailable items", async () => {
const item = new Item(itemUnavailable, parentBib)
render(<ItemAvailability item={item} />)
expect(screen.getByText("Not available")).toBeInTheDocument()
expect(screen.getByText("- Please", { exact: false })).toBeInTheDocument()
screen.getByRole("button", {
name: "contact a librarian",
})
expect(
screen.getByText("for assistance.", { exact: false })
).toBeInTheDocument()
})
})
93 changes: 93 additions & 0 deletions src/components/ItemTable/ItemAvailability.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Text, Link, Button, Box } from "@nypl/design-system-react-components"

import { appConfig } from "../../config/config"
import type Item from "../../models/Item"

interface ItemAvailabilityProps {
item: Item
}

/**
* The ItemAvailability component appears below the Item table and displays
* info about an item's availability.
* TODO: Add Feedback box, Due date, Available font styles
*/
const ItemAvailability = ({ item }: ItemAvailabilityProps) => {
// TODO: Move this logic into a getter function in the Item class that returns an availability status key
dgcohen marked this conversation as resolved.
Show resolved Hide resolved
// and replace this nested If with a simple switch statement
if (item.isAvailable) {
if (item.isReCAP && !item.aeonUrl) {
// Available ReCAP item
return (
<Link
href={appConfig.externalUrls.researchMaterialsHelp}
target="_blank"
fontSize="sm"
>
How do I pick up this item and when will it be ready?
</Link>
)
} else if (item.aeonUrl && item.location?.endpoint) {
return (
<Text>
<Box as="span" color="ui.success.primary">
Available by appointment
</Box>
{!item.isReCAP ? (
<>
{" at "}
<Link
href={`${appConfig.externalUrls.locations}${item.location.endpoint}`}
target="_blank"
>
{item.location.prefLabel}
</Link>
</>
) : null}
</Text>
)
} else {
// Available Onsite item
const locationShort = item.location.prefLabel.split("-")[0]
return (
<Text>
<Box as="span" color="ui.success.primary">
Available
</Box>
{" - Can be used on site. Please visit "}
<Link
href={`${appConfig.externalUrls.locations}${item.location.endpoint}`}
target="_blank"
>
{`New York Public Library - ${locationShort}`}
</Link>
{" to submit a request in person."}
</Text>
)
}
} else {
// Not available
return (
<Text>
<Box as="span" color="ui.warning.primary">
Not available
</Box>
{item.dueDate && ` - In use until ${item.dueDate}`}
{" - Please "}
<Button
id="contact-librarian"
buttonType="link"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try buttonType="text".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this still need to be addressed?

sx={{ display: "inline", fontWeight: "inherit", fontSize: "inherit" }}
onClick={() => {
console.log("TODO: Trigger Feedback box")
}}
>
contact a librarian
</Button>
{" for assistance."}
</Text>
)
}
}

export default ItemAvailability
43 changes: 43 additions & 0 deletions src/components/ItemTable/ItemTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Box, Table } from "@nypl/design-system-react-components"

import type ItemTableData from "../../models/ItemTableData"
import RequestButtons from "./RequestButtons"
import ItemAvailability from "./ItemAvailability"

interface ItemTableProps {
itemTableData: ItemTableData
}

/**
* The ItemTable displays item details, the RequestButtons
*/
const ItemTable = ({ itemTableData }: ItemTableProps) => {
return (
<>
<Table
columnHeaders={itemTableData.tableHeadings}
tableData={itemTableData.tableData}
mb="s"
// TODO: These styles approximate those of the old app.
// Design will VQA the component with the DS defaults, but leaving this for reference.
// sx={{
// tableLayout: "fixed",
// width: "full",
// tr: { border: "0 !important", padding: 0 },
// "th, td, th > span, td > span": {
// paddingTop: "xs",
// paddingBottom: "xs",
// },
// }}
/>
{!itemTableData.isBibPage && (
<Box mb="s">
<RequestButtons item={itemTableData.items[0]} />
<ItemAvailability item={itemTableData.items[0]} />
</Box>
)}
</>
)
}

export default ItemTable
Loading