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

Chore/migrate products #210

Merged
merged 18 commits into from
Nov 9, 2023
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
Loading