[251121] fix: JustForYouTestPanel Top Click

🕐 커밋 시간: 2025. 11. 21. 12:49:09

📊 변경 통계:
  • 총 파일: 5개
  • 추가: +115줄
  • 삭제: -23줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/components/FavoriteBtn.jsx
  ~ com.twin.app.shoptime/src/views/JustForYouPanel/JustForYouPanel.jsx
  ~ com.twin.app.shoptime/src/views/JustForYouPanel/JustForYouPanel.module.less
  ~ com.twin.app.shoptime/src/views/JustForYouTestPanel/JustForYouTestPanel.jsx

🔧 주요 변경 내용:
  • UI 컴포넌트 아키텍처 개선
  • 테스트 커버리지 및 안정성 향상
  • 중간 규모 기능 개선
This commit is contained in:
2025-11-21 12:49:10 +09:00
parent 2c99ab559c
commit 9c5de90098
5 changed files with 115 additions and 23 deletions

View File

@@ -910,6 +910,10 @@ export default function ProductAllSection({
Spotlight.focus('spotlightId_backBtn'); Spotlight.focus('spotlightId_backBtn');
}, []); }, []);
const handleSpotlightDown = useCallback((e) => {
e.stopPropagation();
}, []);
const onFavoriteFlagChanged = useCallback( const onFavoriteFlagChanged = useCallback(
(newFavoriteFlag) => setFavoriteOverride(newFavoriteFlag), (newFavoriteFlag) => setFavoriteOverride(newFavoriteFlag),
[] []
@@ -1265,11 +1269,11 @@ export default function ProductAllSection({
{promotions.map((promotion, idx) => { {promotions.map((promotion, idx) => {
return( return(
<HorizontalContainer className={css.couponContainer} key={idx}> <div className={css.couponContainer} key={idx}>
<div className={css.couponTitleText}> <div className={css.couponTitleText}>
<div className={css.firstTitle}>SPECIAL PROMOTION</div> <div className={css.firstTitle}>SPECIAL PROMOTION</div>
<div className={css.secondTitle}>Coupon only applicable to this product!</div> <div className={css.secondTitle}>Coupon only applicable to this product!</div>
</div> </div>
<TButton <TButton
spotlightId="detail-coupon-button" spotlightId="detail-coupon-button"
className={css.couponButton} className={css.couponButton}
@@ -1277,6 +1281,7 @@ export default function ProductAllSection({
handleCouponClick(idx, promotion); handleCouponClick(idx, promotion);
}} }}
onSpotlightUp={handleSpotlightUpFromCouponButtons} onSpotlightUp={handleSpotlightUpFromCouponButtons}
onSpotlightDown={handleSpotlightDown}
size="detail_very_small" size="detail_very_small"
> >
<div className={css.couponText}> <div className={css.couponText}>
@@ -1284,16 +1289,17 @@ export default function ProductAllSection({
</div> </div>
<img className={css.buttonImg} src={couponImg} /> <img className={css.buttonImg} src={couponImg} />
</TButton> </TButton>
</HorizontalContainer> </div>
) )
})} })}
{isBillingProductVisible && ( {isBillingProductVisible && (
<HorizontalContainer className={css.buyNowCartContainer}> <div className={css.buyNowCartContainer}>
<TButton <TButton
spotlightId="detail-buy-now-button" spotlightId="detail-buy-now-button"
className={css.buyNowButton} className={css.buyNowButton}
onClick={handleBuyNowClick} onClick={handleBuyNowClick}
onSpotlightUp={handleSpotlightUpFromBuyButtons} onSpotlightUp={handleSpotlightUpFromBuyButtons}
onSpotlightDown={handleSpotlightDown}
type="detail_small" type="detail_small"
> >
<div className={css.buyNowText}>{$L('BUY NOW')}</div> <div className={css.buyNowText}>{$L('BUY NOW')}</div>
@@ -1304,14 +1310,15 @@ export default function ProductAllSection({
// onClick={handleAddToCartClick} // onClick={handleAddToCartClick}
onClick={handleBuyNowClick} onClick={handleBuyNowClick}
onSpotlightUp={handleSpotlightUpFromBuyButtons} onSpotlightUp={handleSpotlightUpFromBuyButtons}
onSpotlightDown={handleSpotlightDown}
type="detail_small" type="detail_small"
> >
<div className={css.addToCartText}>{$L('ADD TO CART')}</div> <div className={css.addToCartText}>{$L('ADD TO CART')}</div>
</TButton> </TButton>
</HorizontalContainer> </div>
)} )}
<HorizontalContainer <div
className={classNames( className={classNames(
css.buttonContainer, css.buttonContainer,
isBillingProductVisible && css.buttonHasNoCart isBillingProductVisible && css.buttonHasNoCart
@@ -1323,6 +1330,7 @@ export default function ProductAllSection({
className={css.shopByMobileButton} className={css.shopByMobileButton}
onClick={handleShopByMobileOpen} onClick={handleShopByMobileOpen}
onSpotlightUp={handleSpotlightUpToBackButton} onSpotlightUp={handleSpotlightUpToBackButton}
onSpotlightDown={handleSpotlightDown}
> >
<div className={css.shopByMobileText}>{$L('SHOP BY MOBILE')}</div> <div className={css.shopByMobileText}>{$L('SHOP BY MOBILE')}</div>
</TButton> </TButton>
@@ -1338,7 +1346,7 @@ export default function ProductAllSection({
/> />
</div> </div>
)} )}
</HorizontalContainer> </div>
<div className={css.callToOrderSection}> <div className={css.callToOrderSection}>
{orderPhnNo && ( {orderPhnNo && (

View File

@@ -66,20 +66,33 @@ export default function FavoriteBtn({
onFavoriteFlagChanged(favoriteFlag === 'Y' ? 'N' : 'Y'); onFavoriteFlagChanged(favoriteFlag === 'Y' ? 'N' : 'Y');
}, [dispatch, favoriteFlag, onFavoriteFlagChanged]); }, [dispatch, favoriteFlag, onFavoriteFlagChanged]);
const handleFavoriteKeyDown = useCallback((e) => {
if (e.key === 'Enter') {
e.preventDefault();
handleFavoriteClick();
}
}, [handleFavoriteClick]);
const handleSpotlightDown = useCallback((e) => {
e.stopPropagation();
}, []);
return ( return (
<SpottableDiv <SpottableDiv
className={classNames(css.favorBtnContainer, kind === 'item_detail' ? css.smallSize : '')}
spotlightId="favoriteBtn" spotlightId="favoriteBtn"
onClick={handleFavoriteClick} className={classNames(css.favorBtnContainer, kind === 'item_detail' ? css.smallSize : '')}
role="button" role="button"
aria-label="Register in Favorites"
tabIndex={0} tabIndex={0}
onClick={handleFavoriteClick}
onKeyDown={handleFavoriteKeyDown}
onSpotlightDown={handleSpotlightDown}
> >
<div <div
className={classNames( className={classNames(
css.favoriteButton, css.favoriteButton,
favoriteFlag === 'N' ? css.favorBtn : css.favorUnableBtn favoriteFlag === 'N' ? css.favorBtn : css.favorUnableBtn
)} )}
aria-label="Register in Favorites"
/> />
{activePopup === Config.ACTIVE_POPUP.favoritePopup && ( {activePopup === Config.ACTIVE_POPUP.favoritePopup && (
<TPopUp <TPopUp

View File

@@ -73,6 +73,21 @@ const JustForYouPanel = ({ panelInfo, isOnTop, ...rest }) => {
const currentSentMenuRef = useRef(null); const currentSentMenuRef = useRef(null);
const focusedContainerIdRef = useRef(panelInfo?.focusedContainerId); const focusedContainerIdRef = useRef(panelInfo?.focusedContainerId);
// 마운트 로그
useEffect(() => {
console.log('[JustForYouPanel] 마운트됨', {
isOnTop,
recentItemsCount: recentItems?.length || 0,
timestamp: Date.now(),
});
return () => {
console.log('[JustForYouPanel] 언마운트됨', {
timestamp: Date.now(),
});
};
}, []);
const handleScroll = useCallback((e) => { const handleScroll = useCallback((e) => {
setShowButton(e.scrollTop === 0); setShowButton(e.scrollTop === 0);
}, []); }, []);
@@ -120,7 +135,24 @@ const JustForYouPanel = ({ panelInfo, isOnTop, ...rest }) => {
} }
}, []); }, []);
const handleWrapClickCapture = useCallback(() => {
console.log("[JustForYouPanel] wrapper capture: 상위 div 통과");
}, []);
const handlePanelClickCapture = useCallback(() => {
console.log("[JustForYouPanel] TPanel capture: panel 통과");
}, []);
const handleBodyClickCapture = useCallback(() => {
console.log("[JustForYouPanel] TBody capture: body 통과");
}, []);
const handleButtonWrapperClickCapture = useCallback(() => {
console.log("[JustForYouPanel] 버튼 감싸는 div capture: 버튼 바로 위");
}, []);
const handleTopButtonClick = useCallback(() => { const handleTopButtonClick = useCallback(() => {
console.log("[JustForYouPanel] TButton onClick: TOP 버튼 클릭");
if (cbChangePageRef.current) { if (cbChangePageRef.current) {
cbChangePageRef.current(0, true); cbChangePageRef.current(0, true);
} }
@@ -132,6 +164,10 @@ const JustForYouPanel = ({ panelInfo, isOnTop, ...rest }) => {
} }
}, [recentItems]); }, [recentItems]);
const handleTopButtonSpotlightDown = useCallback((e) => {
e.stopPropagation();
}, []);
const renderItem = useCallback( const renderItem = useCallback(
(item) => { (item) => {
const { const {
@@ -234,9 +270,15 @@ const JustForYouPanel = ({ panelInfo, isOnTop, ...rest }) => {
// </TBody> // </TBody>
// </TPanel> // </TPanel>
// </div> // </div>
<div className={css.justForYouWrap}> <div
<TPanel> className={css.justForYouWrap}
<TBody className={css.justForYou}> onClickCapture={handleWrapClickCapture}
>
<TPanel onClickCapture={handlePanelClickCapture}>
<TBody
className={css.justForYou}
onClickCapture={handleBodyClickCapture}
>
<div className={css.background}> <div className={css.background}>
<CustomImage src={background} animationSpeed="none" /> <CustomImage src={background} animationSpeed="none" />
</div> </div>
@@ -311,15 +353,23 @@ const JustForYouPanel = ({ panelInfo, isOnTop, ...rest }) => {
)} )}
{recentItems && recentItems?.length > 10 && ( {recentItems && recentItems?.length > 10 && (
<RecentSawContainer> <RecentSawContainer onClickCapture={handleButtonWrapperClickCapture}>
<TButton <div
onClick={handleTopButtonClick} onClick={(e) => {
size={null} console.log("[JustForYouPanel] DIV onClick: TOP 버튼 DIV 클릭");
spotlightId={SpotlightIds.JUST_FOR_YOU_TOP_BUTTON} handleTopButtonClick();
ariaLabel="Move to Top" }}
data-wheel-point >
type={TYPES.topButton} <TButton
/> onClick={handleTopButtonClick}
onSpotlightDown={handleTopButtonSpotlightDown}
size={null}
spotlightId={SpotlightIds.JUST_FOR_YOU_TOP_BUTTON}
ariaLabel="Move to Top"
data-wheel-point
type={TYPES.topButton}
/>
</div>
</RecentSawContainer> </RecentSawContainer>
)} )}
</TBody> </TBody>

View File

@@ -15,6 +15,8 @@
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
z-index: -1;
pointer-events: none;
} }
.itemsContainer { .itemsContainer {

View File

@@ -60,19 +60,36 @@ const JustForYouTestPanel = ({ panelInfo, ...rest }) => {
}, [dispatch]); }, [dispatch]);
const handleTopButtonClick = useCallback(() => { const handleTopButtonClick = useCallback(() => {
console.log("[JustForYouTestPanel] TOP 버튼 클릭됨", {
cbChangePageRefExists: !!cbChangePageRef.current,
shelfInfosLength: shelfInfos?.length || 0,
firstShelfProductCount: shelfInfos?.[0]?.productInfos?.length || 0,
});
if (cbChangePageRef.current) { if (cbChangePageRef.current) {
console.log("[JustForYouTestPanel] cbChangePageRef 호출");
cbChangePageRef.current(0, true); cbChangePageRef.current(0, true);
} else {
console.log("[JustForYouTestPanel] ⚠️ cbChangePageRef.current가 null");
} }
if ( if (
shelfInfos && shelfInfos &&
shelfInfos.length > 0 && shelfInfos.length > 0 &&
shelfInfos[0].productInfos?.length > 0 shelfInfos[0].productInfos?.length > 0
) { ) {
console.log("[JustForYouTestPanel] Spotlight.focus('justForYouList_1') 호출");
Spotlight.focus("justForYouList_1"); Spotlight.focus("justForYouList_1");
} else {
console.log("[JustForYouTestPanel] ⚠️ shelfInfos 없음 또는 비어있음");
} }
}, [shelfInfos]); }, [shelfInfos]);
const handleTopButtonSpotlightDown = useCallback((e) => {
console.log("[JustForYouTestPanel] TOP 버튼 SpotlightDown");
e.stopPropagation();
}, []);
const renderItem = useCallback( const renderItem = useCallback(
(shelfId, shelfExpsOrd, productInfos) => (shelfId, shelfExpsOrd, productInfos) =>
({ index, ...rest }) => { ({ index, ...rest }) => {
@@ -138,6 +155,7 @@ const JustForYouTestPanel = ({ panelInfo, ...rest }) => {
className={css.tVerticalPagenator} className={css.tVerticalPagenator}
spotlightId={"justforyouspotlight"} spotlightId={"justforyouspotlight"}
topMargin={470} topMargin={470}
cbChangePageRef={cbChangePageRef}
> >
<div className={css.background}> <div className={css.background}>
<THeaderCustom <THeaderCustom
@@ -204,6 +222,7 @@ const JustForYouTestPanel = ({ panelInfo, ...rest }) => {
<ListContainer> <ListContainer>
<TButton <TButton
onClick={handleTopButtonClick} onClick={handleTopButtonClick}
onSpotlightDown={handleTopButtonSpotlightDown}
size={null} size={null}
spotlightId={SpotlightIds.JUST_FOR_YOU_TOP_BUTTON} spotlightId={SpotlightIds.JUST_FOR_YOU_TOP_BUTTON}
ariaLabel="Move to Top" ariaLabel="Move to Top"