Skip to content

Commit 13e9517

Browse files
committed
feat: 샵 컴포넌트 아코디언들이 하나씩만 열리도록 수정
1 parent 6727027 commit 13e9517

File tree

5 files changed

+253
-142
lines changed

5 files changed

+253
-142
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { AccordionProps } from "@mui/material";
2+
import * as React from "react";
3+
4+
type PHOpenOneFoldMgrPropType = {
5+
children: React.ReactElement<AccordionProps>[];
6+
};
7+
8+
export const OneDetailsOpener: React.FC<PHOpenOneFoldMgrPropType> = (props) => {
9+
const childrenCount = React.Children.count(props.children);
10+
const initialFoldState = new Array(childrenCount).fill(false);
11+
const [oneFoldOpener, setOneFoldOpener] = React.useState(initialFoldState);
12+
const foldStateSwitcher =
13+
(index: number): ((event: React.SyntheticEvent<Element, Event>, expanded: boolean) => void) =>
14+
(event, expanded) => {
15+
event.preventDefault();
16+
event.stopPropagation();
17+
18+
const newOneFoldOpener = [...initialFoldState];
19+
newOneFoldOpener[index] = expanded;
20+
21+
setOneFoldOpener(newOneFoldOpener);
22+
};
23+
24+
return (
25+
<>
26+
{React.Children.map(props.children, (child, index) =>
27+
React.cloneElement(child, { expanded: oneFoldOpener[index], onChange: foldStateSwitcher(index) })
28+
)}
29+
</>
30+
);
31+
};

packages/common/src/components/mdx_components/styled_details.tsx

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
Accordion,
44
AccordionActions,
55
AccordionDetails,
6+
AccordionProps,
67
AccordionSummary,
78
PaletteColor,
89
styled,
@@ -12,12 +13,13 @@ import {
1213
} from "@mui/material";
1314
import * as React from "react";
1415

15-
type StyledDetailsProps = React.PropsWithChildren<{
16-
expandIcon?: React.ReactNode;
17-
open?: boolean;
18-
summary?: React.ReactNode;
19-
actions?: React.ReactNode;
20-
}>;
16+
type StyledDetailsProps = React.PropsWithChildren<
17+
AccordionProps & {
18+
expandIcon?: React.ReactNode;
19+
summary?: React.ReactNode;
20+
actions?: React.ReactNode;
21+
}
22+
>;
2123

2224
type BaseStyledDetailsProps = StyledDetailsProps & {
2325
paletteColor: PaletteColor;
@@ -26,12 +28,14 @@ type BaseStyledDetailsProps = StyledDetailsProps & {
2628

2729
const BaseStyledDetails: React.FC<BaseStyledDetailsProps> = ({
2830
expandIcon,
29-
open,
31+
expanded,
32+
onChange,
3033
summary,
3134
children,
3235
actions,
3336
paletteColor,
3437
transparencyOnExpand,
38+
...props
3539
}) => {
3640
const StyledAccordion = styled(Accordion)(({ theme }) => ({
3741
width: "100%",
@@ -59,7 +63,15 @@ const BaseStyledDetails: React.FC<BaseStyledDetailsProps> = ({
5963
}));
6064

6165
return (
62-
<StyledAccordion expanded={open} disableGutters square elevation={0} slotProps={{ root: { sx: rootSx } }}>
66+
<StyledAccordion
67+
{...props}
68+
expanded={expanded}
69+
onChange={onChange}
70+
disableGutters
71+
square
72+
elevation={0}
73+
slotProps={{ root: { sx: rootSx } }}
74+
>
6375
<StyledAccordionSummary expandIcon={expandIcon || <DefaultExpandIcon />}>
6476
{typeof summary === "string" ? <Typography variant="h5">{summary}</Typography> : summary}
6577
</StyledAccordionSummary>

packages/shop/src/components/features/cart.tsx

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import * as Common from "@frontend/common";
2-
import { Backdrop, Button, CircularProgress, Divider, Stack, Typography } from "@mui/material";
2+
import { AccordionProps, Backdrop, Button, CircularProgress, Divider, Stack, Typography } from "@mui/material";
33
import { ErrorBoundary, Suspense } from "@suspensive/react";
44
import { useQueryClient } from "@tanstack/react-query";
5+
import { enqueueSnackbar, OptionsObject } from "notistack";
56
import * as React from "react";
67
import * as R from "remeda";
78

@@ -10,12 +11,14 @@ import ShopSchemas from "../../schemas";
1011
import ShopUtils from "../../utils";
1112
import CommonComponents from "../common";
1213

13-
const CartItem: React.FC<{
14-
language: "ko" | "en";
15-
cartProdRel: ShopSchemas.OrderProductItem;
16-
removeItemFromCartFunc: (cartProductId: string) => void;
17-
disabled?: boolean;
18-
}> = ({ language, cartProdRel, disabled, removeItemFromCartFunc }) => {
14+
const CartItem: React.FC<
15+
Omit<AccordionProps, "children"> & {
16+
language: "ko" | "en";
17+
cartProdRel: ShopSchemas.OrderProductItem;
18+
removeItemFromCartFunc: (cartProductId: string) => void;
19+
disabled?: boolean;
20+
}
21+
> = ({ language, cartProdRel, disabled, removeItemFromCartFunc, ...props }) => {
1922
const cannotModifyOptionsStr =
2023
language === "ko"
2124
? "상품 옵션을 수정하려면 장바구니에서 상품을 삭제한 후 다시 담아주세요."
@@ -25,6 +28,7 @@ const CartItem: React.FC<{
2528

2629
return (
2730
<Common.Components.MDX.PrimaryStyledDetails
31+
{...props}
2832
summary={cartProdRel.product.name}
2933
actions={[
3034
<Button
@@ -74,9 +78,14 @@ export const CartStatus: React.FC<{ onPaymentCompleted?: () => void }> = Suspens
7478
openBackdrop: false,
7579
});
7680

81+
const addSnackbar = (c: string | React.ReactNode, variant: OptionsObject["variant"]) =>
82+
enqueueSnackbar(c, { variant, anchorOrigin: { vertical: "bottom", horizontal: "center" } });
83+
7784
const cartIsEmptyStr = language === "ko" ? "장바구니가 비어있어요!" : "Your cart is empty!";
7885
const totalPriceStr = language === "ko" ? "총 결제 금액" : "Total Payment Amount";
7986
const orderCartStr = language === "ko" ? "장바구니에 담긴 상품 결제" : "Pay for Items in Cart";
87+
const succeededToRemoveItemFromCartStr =
88+
language === "ko" ? "장바구니에서 상품을 삭제했습니다." : "Item has been removed from the cart.";
8089
const errorWhileLoadingCartStr =
8190
language === "ko"
8291
? "장바구니 정보를 불러오는 중 문제가 발생했습니다."
@@ -90,7 +99,14 @@ export const CartStatus: React.FC<{ onPaymentCompleted?: () => void }> = Suspens
9099
? "장바구니 결제에 실패했습니다.\n잠시 후 다시 시도해주세요.\n"
91100
: "Failed to complete the cart order.\nPlease try again later.\n";
92101

93-
const removeItemFromCart = (cartProductId: string) => removeItemFromCartMutation.mutate({ cartProductId });
102+
const removeItemFromCart = (cartProductId: string) =>
103+
removeItemFromCartMutation.mutate(
104+
{ cartProductId },
105+
{
106+
onSuccess: () => addSnackbar(succeededToRemoveItemFromCartStr, "success"),
107+
onError: (error) => addSnackbar(error.message || errorWhilePreparingOrderStr, "error"),
108+
}
109+
);
94110

95111
const openDialog = () => setState((ps) => ({ ...ps, openDialog: true }));
96112
const closeDialog = () => setState((ps) => ({ ...ps, openDialog: false }));
@@ -141,15 +157,17 @@ export const CartStatus: React.FC<{ onPaymentCompleted?: () => void }> = Suspens
141157
defaultValue={data.customer_info}
142158
/>
143159
<Stack spacing={2}>
144-
{data.products.map((prodRel) => (
145-
<CartItem
146-
language={language}
147-
key={prodRel.id}
148-
cartProdRel={prodRel}
149-
disabled={disabled}
150-
removeItemFromCartFunc={removeItemFromCart}
151-
/>
152-
))}
160+
<Common.Components.MDX.OneDetailsOpener>
161+
{data.products.map((prodRel) => (
162+
<CartItem
163+
language={language}
164+
key={prodRel.id}
165+
cartProdRel={prodRel}
166+
disabled={disabled}
167+
removeItemFromCartFunc={removeItemFromCart}
168+
/>
169+
))}
170+
</Common.Components.MDX.OneDetailsOpener>
153171
</Stack>
154172
<br />
155173
<Divider />

packages/shop/src/components/features/order.tsx

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as Common from "@frontend/common";
22
import {
3+
AccordionProps,
34
Button,
45
CircularProgress,
56
Divider,
@@ -38,7 +39,7 @@ const PaymentHistoryStatusEn: {
3839
refunded: "Refunded",
3940
};
4041

41-
type OrderProductRelationItemProps = {
42+
type OrderProductRelationItemProps = Omit<AccordionProps, "children"> & {
4243
language: "ko" | "en";
4344
order: ShopSchemas.Order;
4445
prodRel: ShopSchemas.OrderProductItem;
@@ -54,6 +55,7 @@ const OrderProductRelationItem: React.FC<OrderProductRelationItemProps> = ({
5455
isPending,
5556
oneItemRefundMutation,
5657
optionsOfOneItemInOrderPatchMutation,
58+
...props
5759
}) => {
5860
const formRef = React.useRef<HTMLFormElement>(null);
5961
const currentCustomOptionValues: { [k: string]: string } = prodRel.options
@@ -122,6 +124,7 @@ const OrderProductRelationItem: React.FC<OrderProductRelationItemProps> = ({
122124

123125
return (
124126
<Common.Components.MDX.PrimaryStyledDetails
127+
{...props}
125128
key={prodRel.id}
126129
summary={<Typography variant="h6">{prodRel.product.name}</Typography>}
127130
actions={actionButtons}
@@ -147,7 +150,9 @@ const OrderProductRelationItem: React.FC<OrderProductRelationItemProps> = ({
147150
);
148151
};
149152

150-
const OrderItem: React.FC<{ order: ShopSchemas.Order; disabled?: boolean }> = ({ order, disabled }) => {
153+
type OrderItemProps = Omit<AccordionProps, "children"> & { order: ShopSchemas.Order; disabled?: boolean };
154+
155+
const OrderItem: React.FC<OrderItemProps> = ({ order, disabled, ...props }) => {
151156
const { language, shopApiDomain } = ShopHooks.useShopContext();
152157
const shopAPIClient = ShopHooks.useShopClient();
153158
const orderRefundMutation = ShopHooks.useOrderRefundMutation(shopAPIClient);
@@ -192,7 +197,7 @@ const OrderItem: React.FC<{ order: ShopSchemas.Order; disabled?: boolean }> = ({
192197
);
193198

194199
return (
195-
<Common.Components.MDX.PrimaryStyledDetails summary={order.name} actions={actionButtons}>
200+
<Common.Components.MDX.PrimaryStyledDetails {...props} summary={order.name} actions={actionButtons}>
196201
<Table>
197202
<TableHead>
198203
<TableRow>
@@ -241,17 +246,19 @@ const OrderItem: React.FC<{ order: ShopSchemas.Order; disabled?: boolean }> = ({
241246
<Typography variant="h6">{productsInOrderStr}</Typography>
242247
<br />
243248
<Stack spacing={2}>
244-
{order.products.map((prodRel) => (
245-
<OrderProductRelationItem
246-
key={prodRel.id}
247-
language={language}
248-
order={order}
249-
prodRel={prodRel}
250-
isPending={isPending}
251-
oneItemRefundMutation={oneItemRefundMutation}
252-
optionsOfOneItemInOrderPatchMutation={optionsOfOneItemInOrderPatchMutation}
253-
/>
254-
))}
249+
<Common.Components.MDX.OneDetailsOpener>
250+
{order.products.map((prodRel) => (
251+
<OrderProductRelationItem
252+
key={prodRel.id}
253+
language={language}
254+
order={order}
255+
prodRel={prodRel}
256+
isPending={isPending}
257+
oneItemRefundMutation={oneItemRefundMutation}
258+
optionsOfOneItemInOrderPatchMutation={optionsOfOneItemInOrderPatchMutation}
259+
/>
260+
))}
261+
</Common.Components.MDX.OneDetailsOpener>
255262
</Stack>
256263
<br />
257264
<Divider />
@@ -264,7 +271,13 @@ export const OrderList: React.FC = () => {
264271
const shopAPIClient = ShopHooks.useShopClient();
265272
const { data } = ShopHooks.useOrders(shopAPIClient);
266273

267-
return data.map((item) => <OrderItem key={item.id} order={item} />);
274+
return (
275+
<Common.Components.MDX.OneDetailsOpener>
276+
{data.map((item) => (
277+
<OrderItem key={item.id} order={item} />
278+
))}
279+
</Common.Components.MDX.OneDetailsOpener>
280+
);
268281
};
269282

270283
return (

0 commit comments

Comments
 (0)