@rpg-engine/long-bow 0.8.68 → 0.8.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Store/Store.d.ts +5 -0
- package/dist/long-bow.cjs.development.js +20340 -135
- package/dist/long-bow.cjs.development.js.map +1 -1
- package/dist/long-bow.cjs.production.min.js +1 -1
- package/dist/long-bow.cjs.production.min.js.map +1 -1
- package/dist/long-bow.esm.js +20340 -135
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Store/CartView.tsx +52 -42
- package/src/components/Store/Store.tsx +29 -8
- package/src/components/Store/StoreCharacterSkinRow.tsx +40 -128
- package/src/components/Store/__test__/MetadataCollector.spec.tsx +2 -1
- package/src/components/Store/__test__/useStoreMetadata.spec.tsx +10 -10
- package/src/stories/Features/store/Store.stories.tsx +3 -0
- package/src/types/index.d.ts +1 -0
package/package.json
CHANGED
|
@@ -2,10 +2,13 @@ import { IStoreItem, MetadataType } from '@rpg-engine/shared';
|
|
|
2
2
|
import React, { useState } from 'react';
|
|
3
3
|
import { FaInfoCircle, FaShoppingBag, FaTimes, FaTrash } from 'react-icons/fa';
|
|
4
4
|
import styled from 'styled-components';
|
|
5
|
+
import characterAtlasJSON from '../../mocks/atlas/entities/entities.json';
|
|
6
|
+
import characterAtlasIMG from '../../mocks/atlas/entities/entities.png';
|
|
5
7
|
import { CTAButton } from '../shared/CTAButton/CTAButton';
|
|
6
8
|
import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
|
|
7
9
|
|
|
8
10
|
|
|
11
|
+
|
|
9
12
|
interface ICartViewProps {
|
|
10
13
|
cartItems: {
|
|
11
14
|
item: IStoreItem;
|
|
@@ -31,7 +34,7 @@ const MetadataDisplay: React.FC<{
|
|
|
31
34
|
<FaInfoCircle />
|
|
32
35
|
<span>Skin:</span>
|
|
33
36
|
</MetadataLabel>
|
|
34
|
-
<MetadataValue>{metadata.
|
|
37
|
+
<MetadataValue>{metadata.selectedSkinName || 'Custom skin'}</MetadataValue>
|
|
35
38
|
</MetadataInfo>
|
|
36
39
|
);
|
|
37
40
|
default:
|
|
@@ -87,48 +90,55 @@ export const CartView: React.FC<ICartViewProps> = ({
|
|
|
87
90
|
{cartItems.length === 0 ? (
|
|
88
91
|
<EmptyCart>Your cart is empty</EmptyCart>
|
|
89
92
|
) : (
|
|
90
|
-
cartItems.map(cartItem =>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
<span>×</span>
|
|
108
|
-
<span>{cartItem.quantity}</span>
|
|
109
|
-
<span>=</span>
|
|
110
|
-
<span>
|
|
111
|
-
${formatPrice(cartItem.item.price * cartItem.quantity)}
|
|
112
|
-
</span>
|
|
113
|
-
</ItemInfo>
|
|
114
|
-
|
|
115
|
-
{cartItem.metadata && cartItem.item.metadataType && (
|
|
116
|
-
<MetadataDisplay
|
|
117
|
-
type={cartItem.item.metadataType}
|
|
118
|
-
metadata={cartItem.metadata}
|
|
93
|
+
cartItems.map(cartItem => {
|
|
94
|
+
console.log('Item metadataType: , texturePath:', cartItem.item.metadataType , cartItem.item.texturePath);
|
|
95
|
+
const getSpriteKey = (textureKey: string) => {
|
|
96
|
+
return textureKey + '/down/standing/0.png';
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<CartItemRow key={cartItem.item.key}>
|
|
101
|
+
<ItemIconContainer>
|
|
102
|
+
<SpriteFromAtlas
|
|
103
|
+
atlasJSON={cartItem.item.metadataType === MetadataType.CharacterSkin ? characterAtlasJSON : atlasJSON}
|
|
104
|
+
atlasIMG={cartItem.item.metadataType === MetadataType.CharacterSkin ? characterAtlasIMG : atlasIMG}
|
|
105
|
+
spriteKey={cartItem.item.metadataType === MetadataType.CharacterSkin && cartItem.metadata?.selectedSkinTextureKey ? getSpriteKey(cartItem.metadata.selectedSkinTextureKey) : cartItem.item.texturePath}
|
|
106
|
+
width={32}
|
|
107
|
+
height={32}
|
|
108
|
+
imgScale={2}
|
|
109
|
+
centered
|
|
119
110
|
/>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
111
|
+
</ItemIconContainer>
|
|
112
|
+
<ItemDetails>
|
|
113
|
+
<ItemName>{cartItem.item.name}</ItemName>
|
|
114
|
+
<ItemInfo>
|
|
115
|
+
<span>${formatPrice(cartItem.item.price)}</span>
|
|
116
|
+
<span>×</span>
|
|
117
|
+
<span>{cartItem.quantity}</span>
|
|
118
|
+
<span>=</span>
|
|
119
|
+
<span>
|
|
120
|
+
${formatPrice(cartItem.item.price * cartItem.quantity)}
|
|
121
|
+
</span>
|
|
122
|
+
</ItemInfo>
|
|
123
|
+
|
|
124
|
+
{cartItem.metadata && cartItem.item.metadataType && (
|
|
125
|
+
<MetadataDisplay
|
|
126
|
+
type={cartItem.item.metadataType}
|
|
127
|
+
metadata={cartItem.metadata}
|
|
128
|
+
/>
|
|
129
|
+
)}
|
|
130
|
+
</ItemDetails>
|
|
131
|
+
|
|
132
|
+
<CTAButton
|
|
133
|
+
icon={<FaTrash />}
|
|
134
|
+
onClick={e => {
|
|
135
|
+
e.stopPropagation();
|
|
136
|
+
onRemoveFromCart(cartItem.item.key);
|
|
137
|
+
}}
|
|
138
|
+
/>
|
|
139
|
+
</CartItemRow>
|
|
140
|
+
);
|
|
141
|
+
})
|
|
132
142
|
)}
|
|
133
143
|
</CartItems>
|
|
134
144
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IItemPack, IPurchase, IStoreItem, ItemRarities, ItemSubType, ItemType, UserAccountTypes } from '@rpg-engine/shared';
|
|
2
|
-
import React, { useMemo, useState } from 'react';
|
|
2
|
+
import React, { ReactNode, useMemo, useState } from 'react';
|
|
3
3
|
import { FaHistory, FaShoppingCart } from 'react-icons/fa';
|
|
4
4
|
import styled from 'styled-components';
|
|
5
5
|
import { uiColors } from '../../constants/uiColors';
|
|
@@ -14,6 +14,9 @@ import { StoreItemsSection } from './sections/StoreItemsSection';
|
|
|
14
14
|
import { StorePacksSection } from './sections/StorePacksSection';
|
|
15
15
|
import { StoreItemDetails } from './StoreItemDetails';
|
|
16
16
|
|
|
17
|
+
// Define TabId union type for tab identifiers
|
|
18
|
+
type TabId = 'premium' | 'packs' | 'items';
|
|
19
|
+
|
|
17
20
|
// Define IStoreProps locally as a workaround
|
|
18
21
|
export interface IStoreProps {
|
|
19
22
|
items: IStoreItem[];
|
|
@@ -26,6 +29,9 @@ export interface IStoreProps {
|
|
|
26
29
|
loading?: boolean;
|
|
27
30
|
error?: string;
|
|
28
31
|
onClose?: () => void;
|
|
32
|
+
hidePremiumTab?: boolean;
|
|
33
|
+
tabOrder?: TabId[];
|
|
34
|
+
defaultActiveTab?: TabId;
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
export const Store: React.FC<IStoreProps> = ({
|
|
@@ -39,9 +45,18 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
39
45
|
loading = false,
|
|
40
46
|
error,
|
|
41
47
|
onClose,
|
|
48
|
+
hidePremiumTab = false,
|
|
49
|
+
tabOrder,
|
|
50
|
+
defaultActiveTab,
|
|
42
51
|
}) => {
|
|
43
52
|
const [selectedPack, setSelectedPack] = useState<IItemPack | null>(null);
|
|
44
|
-
const [activeTab, setActiveTab] = useState(
|
|
53
|
+
const [activeTab, setActiveTab] = useState<TabId>(() => {
|
|
54
|
+
const initialTabs = (tabOrder ?? ['premium', 'packs', 'items']).filter(id => !(hidePremiumTab && id === 'premium'));
|
|
55
|
+
if (defaultActiveTab && initialTabs.includes(defaultActiveTab)) {
|
|
56
|
+
return defaultActiveTab;
|
|
57
|
+
}
|
|
58
|
+
return hidePremiumTab ? 'items' : 'premium';
|
|
59
|
+
});
|
|
45
60
|
const {
|
|
46
61
|
cartItems,
|
|
47
62
|
handleAddToCart,
|
|
@@ -136,8 +151,12 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
136
151
|
return <ErrorMessage>{error}</ErrorMessage>;
|
|
137
152
|
}
|
|
138
153
|
|
|
139
|
-
|
|
140
|
-
|
|
154
|
+
// Build tabs dynamically based on props
|
|
155
|
+
const tabIds: TabId[] = tabOrder ?? ['premium', 'packs', 'items'];
|
|
156
|
+
const availableTabIds: TabId[] = tabIds.filter(id => !(hidePremiumTab && id === 'premium'));
|
|
157
|
+
|
|
158
|
+
const tabsMap: Record<TabId, { id: TabId; title: string; content: ReactNode }> = {
|
|
159
|
+
premium: {
|
|
141
160
|
id: 'premium',
|
|
142
161
|
title: 'Premium',
|
|
143
162
|
content: (
|
|
@@ -148,7 +167,7 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
148
167
|
/>
|
|
149
168
|
),
|
|
150
169
|
},
|
|
151
|
-
{
|
|
170
|
+
packs: {
|
|
152
171
|
id: 'packs',
|
|
153
172
|
title: 'Packs',
|
|
154
173
|
content: (
|
|
@@ -159,7 +178,7 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
159
178
|
/>
|
|
160
179
|
),
|
|
161
180
|
},
|
|
162
|
-
{
|
|
181
|
+
items: {
|
|
163
182
|
id: 'items',
|
|
164
183
|
title: 'Items',
|
|
165
184
|
content: (
|
|
@@ -172,7 +191,9 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
172
191
|
/>
|
|
173
192
|
),
|
|
174
193
|
},
|
|
175
|
-
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const tabs = availableTabIds.map(id => tabsMap[id]);
|
|
176
197
|
|
|
177
198
|
return (
|
|
178
199
|
<DraggableContainer
|
|
@@ -243,7 +264,7 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
243
264
|
borderColor="#f59e0b"
|
|
244
265
|
hoverColor="#fef3c7"
|
|
245
266
|
activeTab={activeTab}
|
|
246
|
-
onTabChange={setActiveTab}
|
|
267
|
+
onTabChange={(tabId: string) => setActiveTab(tabId as TabId)}
|
|
247
268
|
/>
|
|
248
269
|
</MainContent>
|
|
249
270
|
{cartItems.length > 0 && (
|
|
@@ -4,7 +4,6 @@ import { FaCartPlus } from 'react-icons/fa';
|
|
|
4
4
|
import styled from 'styled-components';
|
|
5
5
|
import { SelectArrow } from '../Arrow/SelectArrow';
|
|
6
6
|
import { ICharacterProps } from '../Character/CharacterSelection';
|
|
7
|
-
import { ErrorBoundary } from '../Item/Inventory/ErrorBoundary';
|
|
8
7
|
import { CTAButton } from '../shared/CTAButton/CTAButton';
|
|
9
8
|
import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
|
|
10
9
|
|
|
@@ -23,7 +22,6 @@ export const StoreCharacterSkinRow: React.FC<IStoreCharacterSkinRowProps> = ({
|
|
|
23
22
|
onAddToCart,
|
|
24
23
|
userAccountType,
|
|
25
24
|
}) => {
|
|
26
|
-
const [quantity, setQuantity] = useState(1);
|
|
27
25
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
28
26
|
|
|
29
27
|
// Get available characters from metadata
|
|
@@ -40,32 +38,14 @@ export const StoreCharacterSkinRow: React.FC<IStoreCharacterSkinRowProps> = ({
|
|
|
40
38
|
setCurrentIndex(0);
|
|
41
39
|
}, [item._id]);
|
|
42
40
|
|
|
43
|
-
const handleQuantityChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
44
|
-
const value = parseInt(e.target.value) || 1;
|
|
45
|
-
setQuantity(Math.min(Math.max(1, value), 99));
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const handleBlur = () => {
|
|
49
|
-
if (quantity < 1) setQuantity(1);
|
|
50
|
-
if (quantity > 99) setQuantity(99);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const incrementQuantity = () => {
|
|
54
|
-
setQuantity(prev => Math.min(prev + 1, 99));
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const decrementQuantity = () => {
|
|
58
|
-
setQuantity(prev => Math.max(1, prev - 1));
|
|
59
|
-
};
|
|
60
|
-
|
|
61
41
|
const handlePreviousSkin = () => {
|
|
62
|
-
setCurrentIndex((prevIndex) =>
|
|
42
|
+
setCurrentIndex((prevIndex) =>
|
|
63
43
|
prevIndex === 0 ? availableCharacters.length - 1 : prevIndex - 1
|
|
64
44
|
);
|
|
65
45
|
};
|
|
66
46
|
|
|
67
47
|
const handleNextSkin = () => {
|
|
68
|
-
setCurrentIndex((prevIndex) =>
|
|
48
|
+
setCurrentIndex((prevIndex) =>
|
|
69
49
|
prevIndex === availableCharacters.length - 1 ? 0 : prevIndex + 1
|
|
70
50
|
);
|
|
71
51
|
};
|
|
@@ -77,17 +57,15 @@ export const StoreCharacterSkinRow: React.FC<IStoreCharacterSkinRowProps> = ({
|
|
|
77
57
|
const handleAddToCart = () => {
|
|
78
58
|
if (!hasRequiredAccount) return;
|
|
79
59
|
|
|
80
|
-
//
|
|
81
|
-
if (availableCharacters.length > 0) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
60
|
+
// Always use a quantity of 1
|
|
61
|
+
if (availableCharacters.length > 0 && currentCharacter) {
|
|
62
|
+
onAddToCart(item, 1, {
|
|
63
|
+
selectedSkinName: currentCharacter.name,
|
|
64
|
+
selectedSkinTextureKey: currentCharacter.textureKey
|
|
85
65
|
});
|
|
86
66
|
} else {
|
|
87
|
-
onAddToCart(item,
|
|
67
|
+
onAddToCart(item, 1);
|
|
88
68
|
}
|
|
89
|
-
|
|
90
|
-
setQuantity(1); // Reset quantity after adding to cart
|
|
91
69
|
};
|
|
92
70
|
|
|
93
71
|
const getSpriteKey = (textureKey: string) => {
|
|
@@ -99,34 +77,16 @@ export const StoreCharacterSkinRow: React.FC<IStoreCharacterSkinRowProps> = ({
|
|
|
99
77
|
return (
|
|
100
78
|
<ItemWrapper>
|
|
101
79
|
<ItemIconContainer>
|
|
102
|
-
{
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<SpriteFromAtlas
|
|
113
|
-
atlasJSON={entityAtlasJSON}
|
|
114
|
-
atlasIMG={entityAtlasIMG}
|
|
115
|
-
spriteKey={getSpriteKey(currentCharacter.textureKey)}
|
|
116
|
-
width={32}
|
|
117
|
-
height={32}
|
|
118
|
-
imgScale={2}
|
|
119
|
-
centered
|
|
120
|
-
/>
|
|
121
|
-
</ErrorBoundary>
|
|
122
|
-
</SpriteContainer>
|
|
123
|
-
|
|
124
|
-
<NavArrow
|
|
125
|
-
direction="right"
|
|
126
|
-
onPointerDown={handleNextSkin}
|
|
127
|
-
size={24}
|
|
128
|
-
/>
|
|
129
|
-
</CharacterSkinPreviewContainer>
|
|
80
|
+
{entityAtlasJSON && entityAtlasIMG && currentCharacter ? (
|
|
81
|
+
<SpriteFromAtlas
|
|
82
|
+
atlasJSON={entityAtlasJSON}
|
|
83
|
+
atlasIMG={entityAtlasIMG}
|
|
84
|
+
spriteKey={getSpriteKey(currentCharacter.textureKey)}
|
|
85
|
+
width={32}
|
|
86
|
+
height={32}
|
|
87
|
+
imgScale={2}
|
|
88
|
+
centered
|
|
89
|
+
/>
|
|
130
90
|
) : (
|
|
131
91
|
<SpriteFromAtlas
|
|
132
92
|
atlasJSON={atlasJSON}
|
|
@@ -141,38 +101,20 @@ export const StoreCharacterSkinRow: React.FC<IStoreCharacterSkinRowProps> = ({
|
|
|
141
101
|
</ItemIconContainer>
|
|
142
102
|
|
|
143
103
|
<ItemDetails>
|
|
144
|
-
<
|
|
104
|
+
<Header>
|
|
105
|
+
<ItemName>{item.name}</ItemName>
|
|
106
|
+
</Header>
|
|
145
107
|
{availableCharacters.length > 0 && currentCharacter && (
|
|
146
|
-
<
|
|
108
|
+
<SelectedSkinNav>
|
|
109
|
+
<SelectedSkin>Selected:</SelectedSkin>
|
|
110
|
+
<SkinNavArrow direction="left" onPointerDown={handlePreviousSkin} size={24} />
|
|
111
|
+
<SelectedSkin>{currentCharacter.name}</SelectedSkin>
|
|
112
|
+
<SkinNavArrow direction="right" onPointerDown={handleNextSkin} size={24} />
|
|
113
|
+
</SelectedSkinNav>
|
|
147
114
|
)}
|
|
148
115
|
<ItemPrice>${item.price}</ItemPrice>
|
|
149
116
|
</ItemDetails>
|
|
150
|
-
|
|
151
117
|
<Controls>
|
|
152
|
-
<ArrowsContainer>
|
|
153
|
-
<SelectArrow
|
|
154
|
-
direction="left"
|
|
155
|
-
onPointerDown={decrementQuantity}
|
|
156
|
-
size={24}
|
|
157
|
-
/>
|
|
158
|
-
|
|
159
|
-
<QuantityInput
|
|
160
|
-
type="number"
|
|
161
|
-
value={quantity}
|
|
162
|
-
onChange={handleQuantityChange}
|
|
163
|
-
onBlur={handleBlur}
|
|
164
|
-
min={1}
|
|
165
|
-
max={99}
|
|
166
|
-
className="rpgui-input"
|
|
167
|
-
/>
|
|
168
|
-
|
|
169
|
-
<SelectArrow
|
|
170
|
-
direction="right"
|
|
171
|
-
onPointerDown={incrementQuantity}
|
|
172
|
-
size={24}
|
|
173
|
-
/>
|
|
174
|
-
</ArrowsContainer>
|
|
175
|
-
|
|
176
118
|
<CTAButton
|
|
177
119
|
icon={<FaCartPlus />}
|
|
178
120
|
label="Add"
|
|
@@ -197,7 +139,8 @@ const ItemWrapper = styled.div`
|
|
|
197
139
|
`;
|
|
198
140
|
|
|
199
141
|
const ItemIconContainer = styled.div`
|
|
200
|
-
|
|
142
|
+
width: 32px;
|
|
143
|
+
height: 32px;
|
|
201
144
|
display: flex;
|
|
202
145
|
align-items: center;
|
|
203
146
|
justify-content: center;
|
|
@@ -205,28 +148,6 @@ const ItemIconContainer = styled.div`
|
|
|
205
148
|
padding: 4px;
|
|
206
149
|
`;
|
|
207
150
|
|
|
208
|
-
const CharacterSkinPreviewContainer = styled.div`
|
|
209
|
-
position: relative;
|
|
210
|
-
display: flex;
|
|
211
|
-
align-items: center;
|
|
212
|
-
width: 140px;
|
|
213
|
-
height: 42px;
|
|
214
|
-
justify-content: space-between;
|
|
215
|
-
`;
|
|
216
|
-
|
|
217
|
-
const SpriteContainer = styled.div`
|
|
218
|
-
display: flex;
|
|
219
|
-
align-items: center;
|
|
220
|
-
justify-content: center;
|
|
221
|
-
position: absolute;
|
|
222
|
-
left: 50%;
|
|
223
|
-
transform: translateX(-50%);
|
|
224
|
-
`;
|
|
225
|
-
|
|
226
|
-
const NavArrow = styled(SelectArrow)`
|
|
227
|
-
z-index: 2;
|
|
228
|
-
`;
|
|
229
|
-
|
|
230
151
|
const ItemDetails = styled.div`
|
|
231
152
|
flex: 1;
|
|
232
153
|
display: flex;
|
|
@@ -259,28 +180,19 @@ const Controls = styled.div`
|
|
|
259
180
|
min-width: fit-content;
|
|
260
181
|
`;
|
|
261
182
|
|
|
262
|
-
|
|
263
|
-
|
|
183
|
+
// Styled arrow override for inline nav arrows
|
|
184
|
+
const SkinNavArrow = styled(SelectArrow)`
|
|
185
|
+
position: static;
|
|
186
|
+
`;
|
|
187
|
+
|
|
188
|
+
const Header = styled.div`
|
|
264
189
|
display: flex;
|
|
265
190
|
align-items: center;
|
|
266
|
-
|
|
267
|
-
height: 42px;
|
|
268
|
-
justify-content: space-between;
|
|
191
|
+
gap: 0.5rem;
|
|
269
192
|
`;
|
|
270
193
|
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
font-size: 0.875rem;
|
|
276
|
-
background: rgba(0, 0, 0, 0.2);
|
|
277
|
-
color: #ffffff;
|
|
278
|
-
border: none;
|
|
279
|
-
padding: 0.25rem;
|
|
280
|
-
|
|
281
|
-
&::-webkit-inner-spin-button,
|
|
282
|
-
&::-webkit-outer-spin-button {
|
|
283
|
-
-webkit-appearance: none;
|
|
284
|
-
margin: 0;
|
|
285
|
-
}
|
|
194
|
+
const SelectedSkinNav = styled.div`
|
|
195
|
+
display: flex;
|
|
196
|
+
align-items: center;
|
|
197
|
+
gap: 0.5rem;
|
|
286
198
|
`;
|
|
@@ -24,9 +24,10 @@ jest.mock('../../Character/CharacterSkinSelectionModal', () => ({
|
|
|
24
24
|
jest.mock('../MetadataCollector', () => ({
|
|
25
25
|
MetadataCollector: jest.fn(({ metadataType, config, onCollect, onCancel }) => {
|
|
26
26
|
// Set up cleanup function for the useEffect tests
|
|
27
|
+
const React = require('react');
|
|
27
28
|
React.useEffect(() => {
|
|
28
29
|
return () => {
|
|
29
|
-
if (
|
|
30
|
+
if (globalThis.__metadataResolvers) {
|
|
30
31
|
onCancel();
|
|
31
32
|
}
|
|
32
33
|
};
|
|
@@ -20,15 +20,15 @@ jest.mock('../hooks/useStoreMetadata', () => {
|
|
|
20
20
|
|
|
21
21
|
const collectMetadata = async (item) => {
|
|
22
22
|
// If no metadata type or None, return null immediately
|
|
23
|
-
if (!item.metadataType || item.metadataType ===
|
|
23
|
+
if (!item.metadataType || item.metadataType === 'None') {
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// Setup for valid metadata types
|
|
28
|
-
|
|
28
|
+
globalThis.__metadataResolvers = {
|
|
29
29
|
resolve: (metadata) => {
|
|
30
30
|
// Clean up
|
|
31
|
-
|
|
31
|
+
globalThis.__metadataResolvers = undefined;
|
|
32
32
|
return metadata;
|
|
33
33
|
},
|
|
34
34
|
item
|
|
@@ -36,7 +36,7 @@ jest.mock('../hooks/useStoreMetadata', () => {
|
|
|
36
36
|
|
|
37
37
|
// Handle the last test case specifically
|
|
38
38
|
if (item.key === 'item-character-skin-metadata-cancel') {
|
|
39
|
-
|
|
39
|
+
globalThis.__metadataResolvers = undefined; // Make sure it's cleaned up
|
|
40
40
|
return Promise.resolve(null);
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -139,9 +139,9 @@ describe('useStoreMetadata', () => {
|
|
|
139
139
|
const metadataPromise = hook.collectMetadata(mockItemWithCharacterSkinMetadata);
|
|
140
140
|
|
|
141
141
|
// Setup our mock resolver
|
|
142
|
-
if (
|
|
143
|
-
const originalResolve =
|
|
144
|
-
|
|
142
|
+
if (globalThis.__metadataResolvers) {
|
|
143
|
+
const originalResolve = globalThis.__metadataResolvers.resolve;
|
|
144
|
+
globalThis.__metadataResolvers.resolve = function(metadata) {
|
|
145
145
|
resolveSpy(metadata);
|
|
146
146
|
return originalResolve(metadata);
|
|
147
147
|
};
|
|
@@ -149,8 +149,8 @@ describe('useStoreMetadata', () => {
|
|
|
149
149
|
|
|
150
150
|
// Resolve the metadata
|
|
151
151
|
const mockMetadata = { selectedSkin: 'test-skin' };
|
|
152
|
-
if (
|
|
153
|
-
|
|
152
|
+
if (globalThis.__metadataResolvers) {
|
|
153
|
+
globalThis.__metadataResolvers.resolve(mockMetadata);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
// Wait for the promise to resolve
|
|
@@ -159,7 +159,7 @@ describe('useStoreMetadata', () => {
|
|
|
159
159
|
// Assertions
|
|
160
160
|
expect(result).toEqual(mockMetadata);
|
|
161
161
|
expect(resolveSpy).toHaveBeenCalledWith(mockMetadata);
|
|
162
|
-
expect(
|
|
162
|
+
expect(globalThis.__metadataResolvers).toBeUndefined(); // Should be cleaned up
|
|
163
163
|
});
|
|
164
164
|
|
|
165
165
|
it('should clean up window.__metadataResolvers when collection is cancelled', async () => {
|
package/src/types/index.d.ts
CHANGED