diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 75d4a993..2f1dd849 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -15,18 +15,18 @@ jobs: steps: - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.9.1 + uses: styfle/cancel-workflow-action@0.12.1 - name: Get files - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup NodeJS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: "lts/*" - name: Install dependencies - run: npm ci + run: npm ci --force - name: Run tests run: npm run test:ci diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index d1520c68..00000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -npm run ts-lint-commit-hook -npm run lint && npm run test:ci \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..75fa1341 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "tabWidth": 2 +} diff --git a/README.md b/README.md index 1bddff02..810f7a1a 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,22 @@ The API endpoint is an environment variable which is accessed in a local dev env ## Code Quality -The app uses ESLint with a plugin for TypeScript support. +### Prettier + +There are no pre-commit hooks, however deploy CI will run a Prettier check on all files to ensure code quality. It's recommended to: + +- Install the [Prettier VSCode extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) +- Enable the "Format on Save" setting in VSCode +- Manually validate and/or fix, by running Prettier locally using the following commands: + +```bash +npm run prettier:check # Check for formatting issues +npm run prettier:fix # Fix formatting issues +``` + +### ESLint + +The app uses ESLint with a plugin for TypeScript support. Note currently with NextJS `v14`, this command won't run due to a dependency issue with ESLint `v9` support. Hopefully resolved by NextJS soon. ```bash npm run lint diff --git a/components/About/CollectionGrid.tsx b/components/About/CollectionGrid.tsx index e2159e43..eeac95a7 100644 --- a/components/About/CollectionGrid.tsx +++ b/components/About/CollectionGrid.tsx @@ -1,6 +1,7 @@ import PhotoFeature, { PhotoFeatureProps, } from "@/components/Shared/PhotoFeature/PhotoFeature"; + import { CollectionGridStyled } from "components/About/CollectionGridStyled"; interface Props { diff --git a/components/Clover/ViewerWrapper.test.tsx b/components/Clover/ViewerWrapper.test.tsx index 3a276877..d0a75f5a 100644 --- a/components/Clover/ViewerWrapper.test.tsx +++ b/components/Clover/ViewerWrapper.test.tsx @@ -34,7 +34,7 @@ describe("WorkViewerWrapper", () => { isWorkRestricted={true} manifestId="http://testing.com" /> - + , ); expect(await screen.findByText(readingRoomMessage)).toBeInTheDocument(); @@ -50,7 +50,7 @@ describe("WorkViewerWrapper", () => { isWorkRestricted={false} manifestId="http://testing.com" /> - + , ); let el; diff --git a/components/Clover/ViewerWrapper.tsx b/components/Clover/ViewerWrapper.tsx index 76be53a6..24076213 100644 --- a/components/Clover/ViewerWrapper.tsx +++ b/components/Clover/ViewerWrapper.tsx @@ -18,7 +18,7 @@ export const CloverViewer = dynamic( () => import("@samvera/clover-iiif/viewer"), { ssr: false, - } + }, ); interface WrapperProps { diff --git a/components/Collection/Tabs/Explore.test.tsx b/components/Collection/Tabs/Explore.test.tsx index abdff570..b18e4393 100644 --- a/components/Collection/Tabs/Explore.test.tsx +++ b/components/Collection/Tabs/Explore.test.tsx @@ -17,7 +17,7 @@ const topMetadata = [ describe("CollectionTabsExplore", () => { it("renders the description", async () => { render( - + , ); expect(screen.getByTestId("explore-wrapper")); }); diff --git a/components/Collection/Tabs/Explore.tsx b/components/Collection/Tabs/Explore.tsx index eb9f83d3..5f426975 100644 --- a/components/Collection/Tabs/Explore.tsx +++ b/components/Collection/Tabs/Explore.tsx @@ -28,7 +28,7 @@ const CollectionTabsExplore: React.FC = ({ subject.value.map((subjectValue) => { const str = `${url}/search?query=collection.id:"${collectionId}" AND ${subject.field}:"${subjectValue}"&collectionLabel=${subjectValue}&collectionSummary=${""}&as=iiif`; return str; - }) + }), ); }, [collectionId, topMetadata]); diff --git a/components/Facets/Facet/GenericFacet.test.tsx b/components/Facets/Facet/GenericFacet.test.tsx index 9c3e079b..4c797b17 100644 --- a/components/Facets/Facet/GenericFacet.test.tsx +++ b/components/Facets/Facet/GenericFacet.test.tsx @@ -83,7 +83,7 @@ describe("Facet GenericFacet UI component", () => { id: "genre", label: "Genre", }} - /> + />, ); } it("renders facet title", async () => { diff --git a/components/Facets/Facet/GenericFacet.tsx b/components/Facets/Facet/GenericFacet.tsx index e668e76a..4ce24139 100644 --- a/components/Facets/Facet/GenericFacet.tsx +++ b/components/Facets/Facet/GenericFacet.tsx @@ -22,7 +22,7 @@ const GenericFacet: React.FC = ({ facet }) => { /* eslint-disable */ const debouncedHandler = React.useCallback( debounce(handleFindChange, 200), - [] + [], ); /* eslint-enable */ diff --git a/components/Facets/Facet/Option.tsx b/components/Facets/Facet/Option.tsx index 4e3c7333..f6596dc1 100644 --- a/components/Facets/Facet/Option.tsx +++ b/components/Facets/Facet/Option.tsx @@ -40,7 +40,10 @@ const Option: React.FC = ({ bucket, facet, index }) => { */ const facetObject = getFacetById(facet); if (facetObject) { - filterDispatch({ facet: facetObject, type: "updateRecentFacet" }); + filterDispatch({ + facet: facetObject, + type: "updateRecentFacet", + }); } } else { /** diff --git a/components/Facets/Filter/GroupList.test.tsx b/components/Facets/Filter/GroupList.test.tsx index 1014efd3..939dc925 100644 --- a/components/Facets/Filter/GroupList.test.tsx +++ b/components/Facets/Filter/GroupList.test.tsx @@ -75,7 +75,7 @@ describe("FacetsGroupList component", () => { }} > - + , ); /** * Looks like Radix puts this active state data attribute diff --git a/components/Facets/Filter/Modal.test.tsx b/components/Facets/Filter/Modal.test.tsx index 8048d803..012f05be 100644 --- a/components/Facets/Filter/Modal.test.tsx +++ b/components/Facets/Filter/Modal.test.tsx @@ -72,7 +72,7 @@ describe("FilterModal component while `open`", () => { false} /> - + , ); it("Has text rendering the search query param.", async () => { @@ -81,7 +81,7 @@ describe("FilterModal component while `open`", () => { const content = await screen.findByTestId("modal-content"); expect(content).toContainHTML( - `Results for “joan` + `Results for “joan`, ); expect(singletonRouter).toMatchObject({ asPath: "/search?q=joan", diff --git a/components/Facets/Filter/Preview.test.tsx b/components/Facets/Filter/Preview.test.tsx index 26c2cec4..ce0e8ffa 100644 --- a/components/Facets/Filter/Preview.test.tsx +++ b/components/Facets/Filter/Preview.test.tsx @@ -10,7 +10,7 @@ describe("Submit component", () => { it("renders the component with heading 3", () => { renderHelper(); expect(screen.getByRole("heading", { level: 3 })).toHaveTextContent( - "Preview Results" + "Preview Results", ); }); @@ -29,7 +29,7 @@ describe("Submit component", () => { expect(links.length).toBe(2); links.forEach((link, index) => { expect(link.getAttribute("href")).toBe( - `/items/${sampleSearchShape[index].id}` + `/items/${sampleSearchShape[index].id}`, ); }); }); diff --git a/components/Facets/UserFacets/UserFacets.test.tsx b/components/Facets/UserFacets/UserFacets.test.tsx index 9baf2f8a..1a320d37 100644 --- a/components/Facets/UserFacets/UserFacets.test.tsx +++ b/components/Facets/UserFacets/UserFacets.test.tsx @@ -43,7 +43,7 @@ describe("UserFacet UI component", () => { - + , ); const userFacets = screen.queryByText(`facet-user-component`); @@ -72,7 +72,7 @@ describe("UserFacet UI component", () => { }} /> - + , ); const userFacets = await screen.findByTestId(`facet-user-component`); expect(userFacets).toBeInTheDocument(); @@ -101,7 +101,7 @@ describe("UserFacet UI component", () => { }} /> - + , ); const userFacets = screen.getByTestId(`facet-user-component`); expect(userFacets).toBeInTheDocument(); diff --git a/components/Facets/UserFacets/UserFacets.tsx b/components/Facets/UserFacets/UserFacets.tsx index 31216004..370deaa7 100644 --- a/components/Facets/UserFacets/UserFacets.tsx +++ b/components/Facets/UserFacets/UserFacets.tsx @@ -51,7 +51,7 @@ const FacetsCurrentUser: React.FC = ({ break; default: console.error( - `Screen value "${screen} is not valid on "` + `Screen value "${screen} is not valid on "`, ); break; } diff --git a/components/Facets/UserFacets/Value.test.tsx b/components/Facets/UserFacets/Value.test.tsx index bdd40fd0..8ac3316b 100644 --- a/components/Facets/UserFacets/Value.test.tsx +++ b/components/Facets/UserFacets/Value.test.tsx @@ -16,7 +16,7 @@ describe("UserFacet UI component", () => { handleRemoval={() => { // nada }} - /> + />, ); const value = screen.getByRole("button"); diff --git a/components/Facets/WorkType/RadioGroup.tsx b/components/Facets/WorkType/RadioGroup.tsx index f302665a..a7b299c6 100644 --- a/components/Facets/WorkType/RadioGroup.tsx +++ b/components/Facets/WorkType/RadioGroup.tsx @@ -25,7 +25,7 @@ const RadioGroup: React.FC = ({ const repositionHighlight = ( e: MouseEvent, - option: string + option: string, ) => { if (!option || !wrapperRef?.current) return; diff --git a/components/Footer/SiteContentMessage/SiteContentMessage.tsx b/components/Footer/SiteContentMessage/SiteContentMessage.tsx index bd819eac..5a681c20 100644 --- a/components/Footer/SiteContentMessage/SiteContentMessage.tsx +++ b/components/Footer/SiteContentMessage/SiteContentMessage.tsx @@ -18,7 +18,7 @@ const SiteContentMessage = () => { const [status, setStatus] = useSessionStorage("message_status", true); const [timestamp, setTimestamp] = useSessionStorage( "message_timestamp", - current + current, ); const handleDismiss = () => { diff --git a/components/Grid/Item.test.tsx b/components/Grid/Item.test.tsx index 111eb71e..ebe6bd41 100644 --- a/components/Grid/Item.test.tsx +++ b/components/Grid/Item.test.tsx @@ -35,7 +35,7 @@ describe("GridItem component", () => { render(); expect(screen.getByAltText(mockItem.title).getAttribute("src")).toContain( - mockItem.thumbnail + mockItem.thumbnail, ); }); @@ -43,11 +43,11 @@ describe("GridItem component", () => { render(); expect( - screen.getByAltText(mockItem.title).getAttribute("src") + screen.getByAltText(mockItem.title).getAttribute("src"), ).not.toContain(mockItem.thumbnail); expect(screen.getByAltText(mockItem.title).getAttribute("src")).toContain( - "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/b92874a0-72b7-4479-979e-38860c412a13/square/512,/0/default.jpg" + "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/b92874a0-72b7-4479-979e-38860c412a13/square/512,/0/default.jpg", ); }); }); diff --git a/components/Header/Header.tsx b/components/Header/Header.tsx index a01ad7ab..678ebad5 100644 --- a/components/Header/Header.tsx +++ b/components/Header/Header.tsx @@ -2,6 +2,7 @@ import { HeaderStyled, HeaderVariants, } from "@/components/Header/Header.styled"; + import HeaderLockup from "@/components/Header/Lockup"; import HeaderPrimary from "@/components/Header/Primary"; import HeaderSuper from "@/components/Header/Super"; diff --git a/components/Header/Primary.tsx b/components/Header/Primary.tsx index 228a5a07..9ee319a1 100644 --- a/components/Header/Primary.tsx +++ b/components/Header/Primary.tsx @@ -32,7 +32,7 @@ const HeaderPrimary: React.FC = () => { searchFixed: scrollPosition > 0, type: "updateSearchFixed", }), - [searchDispatch, scrollPosition] + [searchDispatch, scrollPosition], ); const handleIsSearchActive = (status: boolean) => { diff --git a/components/Search/JumpTo.test.tsx b/components/Search/JumpTo.test.tsx index 77a93496..762578a1 100644 --- a/components/Search/JumpTo.test.tsx +++ b/components/Search/JumpTo.test.tsx @@ -17,7 +17,7 @@ describe("SearchJumpTo component", () => {
Outside search form -
+ , ); const form = screen.getByTestId("search-jump-to-form"); diff --git a/components/Search/JumpToList.test.tsx b/components/Search/JumpToList.test.tsx index b8d907bf..b03622ee 100644 --- a/components/Search/JumpToList.test.tsx +++ b/components/Search/JumpToList.test.tsx @@ -20,7 +20,10 @@ const mockSetShowJumpTo = jest.fn(); describe("SearchJumpToList component", () => { it("renders search value in list items", () => { render( - + , ); expect(screen.getByTestId("jump-to-wrapper")); expect(screen.getAllByText("Dylan")).toHaveLength(2); @@ -28,7 +31,7 @@ describe("SearchJumpToList component", () => { it("renders Helper components in each JumpTo item", () => { render( - + , ); const helpers = screen.getAllByTestId("helper"); expect(helpers[0]).toHaveTextContent(/in this collection/i); @@ -37,7 +40,7 @@ describe("SearchJumpToList component", () => { it.only("renders route query params in JumpTo items", async () => { render( - + , ); await act(async () => { @@ -48,19 +51,19 @@ describe("SearchJumpToList component", () => { }); expect( - await screen.findByTestId("helper-anchor-collection") + await screen.findByTestId("helper-anchor-collection"), ).toHaveAttribute("href", `/search?collection=Best+Collection+Ever&q=foo`); expect(screen.getByTestId("helper-anchor-all")).toHaveAttribute( "href", - "/search?q=foo" + "/search?q=foo", ); }); it("selects items correctly on arrow key presses", async () => { const user = userEvent.setup(); render( - + , ); const listItems = await screen.findAllByRole("option"); @@ -86,7 +89,7 @@ describe("SearchJumpToList component", () => { it("handles the Escape key press", async () => { const user = userEvent.setup(); render( - + , ); await user.keyboard("{Escape}"); diff --git a/components/Search/JumpToList.tsx b/components/Search/JumpToList.tsx index 6ff51dd3..d5306a78 100644 --- a/components/Search/JumpToList.tsx +++ b/components/Search/JumpToList.tsx @@ -88,7 +88,7 @@ const SearchJumpToList: React.FC = ({ } catch (err) { console.error( "Error getting Collection title in JumpTo component", - err + err, ); } } diff --git a/components/Search/Pagination.tsx b/components/Search/Pagination.tsx index 25a3cdcb..ea23cec4 100644 --- a/components/Search/Pagination.tsx +++ b/components/Search/Pagination.tsx @@ -32,8 +32,8 @@ export const Pagination: React.FC = ({ pagination }) => { query: { ...query, page: current_page - 1 }, }} > - -