Skip to content

Commit

Permalink
Chore/migrate products (#210)
Browse files Browse the repository at this point in the history
* mv image carousel

* base carousel and story

* wrapper

* wiring

* storybook first pass

* story

* mv sort

* mv sort

* pagination

* ssr compat

* revert

* card

* card

* mv and story

* sort story

* ts version bump for next build

* lint fix

* fix link
  • Loading branch information
nlkluth authored Nov 9, 2023
1 parent 0adba6a commit e2730b0
Show file tree
Hide file tree
Showing 36 changed files with 4,575 additions and 4,962 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"start-server-and-test": "^1.15.2",
"storybook": "^7.4.0",
"tailwindcss": "^3.1.8",
"typescript": "4.9.4"
"typescript": "5.2.2"
},
"pnpm": {
"overrides": {
Expand Down
10 changes: 1 addition & 9 deletions packages/nextjs/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,5 @@
"../../.eslintrc.json",
"next/core-web-vitals",
"plugin:storybook/recommended"
],
"rules": {
"import/order": [
"error",
{
"groups": ["type", "builtin", "external", "internal"]
}
]
}
]
}
28 changes: 12 additions & 16 deletions packages/nextjs/components/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import * as React from "react";
import { SanityImageSource } from "@sanity/image-url/lib/types/types";
import Link, { LinkProps } from "next/link";
import classNames from "classnames";

import { currencyFormatter } from "utils/currencyFormatter";
import { Image } from "./Image";
import { Card as BaseCard } from "shared-ui";

export interface CardProps {
title: string;
Expand All @@ -28,18 +26,16 @@ export const Card = ({
className = "",
}: CardProps) => {
return (
<Link href={to} className={`flex flex-col justify-center text-primary group w-full ${className}`}>
<span
className={classNames(
"rounded-xl group-hover:shadow-lg transition-shadow duration-150 overflow-hidden relative",
containerClassName
)}
>
<Image layout="fill" src={imageProps.src} alt={imageProps.alt} objectFit="cover" objectPosition="center" />
</span>
<h2 className="text-h5 font-medium mt-4 mb-1">{title}</h2>
{price && <span className="text-eyebrow font-bold">{currencyFormatter.format(price)}</span>}
{subTitle && <span className="text-eyebrow">{subTitle}</span>}
</Link>
<BaseCard
title={title}
subTitle={subTitle}
to={to}
price={price}
className={className}
imageContainerClass={containerClassName}
Link={Link}
>
<Image layout="fill" src={imageProps.src} alt={imageProps.alt} objectFit="cover" objectPosition="center" />
</BaseCard>
);
};
24 changes: 24 additions & 0 deletions packages/nextjs/components/ImageCarousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ProductImage } from "utils/groqTypes/ProductList";
import * as React from "react";
import { Image } from "components/Image";
import { ImageCarousel as BaseImageCarousel } from "shared-ui";

export type ImageCarouselProps = {
productImages: ProductImage[];
};

export const ImageCarousel = ({ productImages }: ImageCarouselProps) => {
return (
<BaseImageCarousel>
{productImages?.map((image) => (
<Image
className="rounded-2xl aspect-square w-full"
layout="fill"
key={image?.name}
src={image ?? ""}
alt={image?.name ?? ""}
/>
))}
</BaseImageCarousel>
);
};
101 changes: 24 additions & 77 deletions packages/nextjs/components/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as React from "react";
import { Pagination as BasePagination } from "shared-ui";
import Link from "next/link";
import { useRouter } from "next/router";
import { FaChevronLeft, FaChevronRight } from "react-icons/fa";
import classNames from "classnames";
import { motion } from "framer-motion";
import { useRouter } from "next/router";
import { stringify } from "querystring";

type PaginationProps = {
pageCount: number;
Expand All @@ -13,87 +13,34 @@ type PaginationProps = {

export const Pagination = ({ onPageChange, pageCount = 1, currentPage = 1 }: PaginationProps) => {
const router = useRouter();
const { page: _page, ...otherQuery } = router.query;
const baseUrlObj = {
pathname: router.pathname,
query: otherQuery,
};
const getUrlObjWithPage = (pageNum: number) => ({
...baseUrlObj,
query: { ...baseUrlObj.query, page: pageNum.toString() },
});

const totalPages = Array.from({ length: pageCount }, (_item, index) => index + 1);

const handlePageChanged = (e: React.MouseEvent<HTMLAnchorElement>, page: number) => {
if (onPageChange) {
e.preventDefault();
onPageChange(page);
}
};

const prevUrlObj = currentPage <= 2 ? baseUrlObj : getUrlObjWithPage(currentPage - 1);
const nextUrlObj = currentPage >= pageCount ? baseUrlObj : getUrlObjWithPage(currentPage + 1);

return (
<motion.nav className="flex items-center justify-between text-primary" layoutId="pagination-nav">
<MaybeDisabledLink
urlObject={prevUrlObj}
isDisabled={currentPage <= 1}
className="inline-flex items-center gap-2 leading-none"
>
<FaChevronLeft className="h-5 w-5" aria-hidden="true" />
Previous
</MaybeDisabledLink>
<div className="flex gap-x-1">
{totalPages.map((page) => (
<Link key={`page-${page}`} href={page === 1 ? baseUrlObj : getUrlObjWithPage(page)} passHref legacyBehavior>
<a
onClick={(e) => handlePageChanged(e, page)}
className={classNames(
"border rounded w-10 aspect-square flex items-center justify-center",
page === currentPage ? "border-primary" : "border-[transparent]"
)}
aria-current={page === currentPage ? "page" : "false"}
>
{page}
</a>
</Link>
))}
</div>
<MaybeDisabledLink
urlObject={nextUrlObj}
isDisabled={currentPage >= pageCount}
className="inline-flex items-center gap-2 leading-none"
>
Next
<FaChevronRight className="h-5 w-5" aria-hidden="true" />
</MaybeDisabledLink>
</motion.nav>
);
};

const MaybeDisabledLink = ({
isDisabled,
urlObject,
className,
children,
}: React.PropsWithChildren<{
isDisabled?: boolean;
className?: string;
urlObject: any;
}>) => {
if (!isDisabled) {
return (
<Link href={urlObject} className={className}>
{children}
</Link>
);
}

return (
<span aria-disabled className={classNames(className, "opacity-60")}>
{children}
</span>
<BasePagination
pageCount={pageCount}
NextPreviousLink={Link}
currentPage={currentPage}
currentHref={router.pathname}
search={stringify(router.query)}
renderPaginationLink={({ page, href }) => (
<Link key={`page-${page}`} href={href} passHref legacyBehavior>
<a
onClick={(e) => handlePageChanged(e, page)}
className={classNames(
"border rounded w-10 aspect-square flex items-center justify-center",
page === currentPage ? "border-primary" : "border-[transparent]"
)}
aria-current={page === currentPage ? "page" : "false"}
>
{page}
</a>
</Link>
)}
/>
);
};
74 changes: 4 additions & 70 deletions packages/nextjs/components/ProductSort.tsx
Original file line number Diff line number Diff line change
@@ -1,76 +1,10 @@
import * as React from "react";
import { Pill, Select } from "shared-ui";
import { PAGE_QUERY_PARAM, SORT_OPTIONS, SORT_OPTIONS_ARRAY, SORT_QUERY_PARAM, SortType } from "utils/sorting";
import { useRouterQueryParams } from "utils/useRouterQueryParams";
import { ProductSort as BaseProductSort, ProductSortProps as BaseProps } from "shared-ui";

type ProductSortProps = {
as?: "select" | "pills";
showTitle?: boolean;
title?: string;
selectClassName?: string;
};
type ProductSortProps = Pick<BaseProps, "as" | "showTitle" | "title" | "selectClassName">;

export const ProductSort: React.FC<ProductSortProps> = ({
as = "pills",
showTitle = false,
title = "Sort by",
selectClassName = "",
}) => {
export const ProductSort: React.FC<ProductSortProps> = (props) => {
const { replace, clear, query } = useRouterQueryParams();
const defaultSortValue = SORT_OPTIONS_ARRAY.find(({ type }) => type === SortType.Default)?.value;

const handleChange = (value: string) => {
switch (SORT_OPTIONS[value].type) {
case SortType.Natural: {
replace({ [SORT_QUERY_PARAM]: value, [PAGE_QUERY_PARAM]: "1" });
break;
}
case SortType.Default:
default:
clear(SORT_QUERY_PARAM);
}
};

// Display as SELECT element.
if (as === "select") {
const elements = SORT_OPTIONS_ARRAY.map((item) => {
return {
title: item.label,
value: item.value,
};
});

const selectedItem = elements.find((item) => item.value === (query[SORT_QUERY_PARAM] || defaultSortValue));

return (
<div>
{showTitle && <h4 className="text-h4 text-primary mb-1">{title}</h4>}
<Select
label=""
placeholder={title}
selectedItem={selectedItem}
options={elements}
className={selectClassName}
onChange={(item) => handleChange(item?.value ?? "")}
/>
</div>
);
}

return (
<div className="flex flex-col gap-4">
{showTitle && <h4 className="text-h4 text-primary">{title}</h4>}
<div className="flex flex-wrap gap-2">
{SORT_OPTIONS_ARRAY.map(({ value, label }) => (
<Pill
key={value}
onClick={() => handleChange(value)}
selected={value === (query[SORT_QUERY_PARAM] || defaultSortValue)}
>
{label}
</Pill>
))}
</div>
</div>
);
return <BaseProductSort {...props} onClear={clear} onReplace={replace} query={query} />;
};
Loading

1 comment on commit e2730b0

@vercel
Copy link

@vercel vercel bot commented on e2730b0 Nov 9, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.