@adobe-commerce/elsie 1.7.0-beta1 → 1.7.0-beta3
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/package.json +1 -1
- package/src/components/CartItem/CartItem.css +7 -0
- package/src/components/CartItem/CartItem.stories.tsx +102 -57
- package/src/components/CartItem/CartItem.tsx +10 -0
- package/src/components/CartList/CartList.stories.tsx +66 -3
- package/src/components/Picker/Picker.stories.tsx +21 -3
- package/src/components/Picker/Picker.tsx +6 -5
- package/src/components/RadioButton/RadioButton.css +8 -3
- package/src/components/RadioButton/RadioButton.stories.tsx +37 -3
- package/src/components/RadioButton/RadioButton.tsx +18 -4
- package/src/components/RadioButton/index.ts +3 -3
- package/src/lib/get-price-formatter.ts +1 -1
package/package.json
CHANGED
|
@@ -276,6 +276,13 @@
|
|
|
276
276
|
color: var(--color-neutral-800);
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
+
.dropin-cart-item__row-total-footer {
|
|
280
|
+
margin-top: var(--spacing-xsmall);
|
|
281
|
+
font: var(--type-body-2-emphasized-font);
|
|
282
|
+
letter-spacing: var(--type-body-2-emphasized-letter-spacing);
|
|
283
|
+
color: var(--color-neutral-800);
|
|
284
|
+
}
|
|
285
|
+
|
|
279
286
|
.dropin-cart-item__total-tax-message {
|
|
280
287
|
margin-left: var(--spacing-xxsmall);
|
|
281
288
|
}
|
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
CartItem as component,
|
|
18
18
|
CartItemProps,
|
|
19
19
|
CartItemSkeleton,
|
|
20
|
-
Icon,
|
|
20
|
+
Icon,
|
|
21
|
+
Button,
|
|
21
22
|
} from '@adobe-commerce/elsie/components';
|
|
22
23
|
import { WarningWithCircle } from '@adobe-commerce/elsie/icons';
|
|
23
24
|
|
|
@@ -43,13 +44,13 @@ const meta: Meta<CartItemProps> = {
|
|
|
43
44
|
options: ['Image', 'None'],
|
|
44
45
|
mapping: {
|
|
45
46
|
Image: (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
<Image
|
|
48
|
+
src="https://picsum.photos/132/184"
|
|
49
|
+
width="132"
|
|
50
|
+
height="184"
|
|
51
|
+
alt="Some alternative text"
|
|
52
|
+
loading="lazy"
|
|
53
|
+
/>
|
|
53
54
|
),
|
|
54
55
|
None: undefined,
|
|
55
56
|
},
|
|
@@ -83,9 +84,9 @@ const meta: Meta<CartItemProps> = {
|
|
|
83
84
|
options: ['Description', 'None'],
|
|
84
85
|
mapping: {
|
|
85
86
|
Description: (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
<div>
|
|
88
|
+
Secondary product information such as brand name, description, etc.
|
|
89
|
+
</div>
|
|
89
90
|
),
|
|
90
91
|
None: undefined,
|
|
91
92
|
},
|
|
@@ -118,12 +119,12 @@ const meta: Meta<CartItemProps> = {
|
|
|
118
119
|
options: ['Attributes', 'none'],
|
|
119
120
|
mapping: {
|
|
120
121
|
Attributes: (
|
|
122
|
+
<div>
|
|
121
123
|
<div>
|
|
122
|
-
|
|
123
|
-
Activity: Gym, Hiking, Overnight, School, Trail, Travel, Urban
|
|
124
|
-
</div>
|
|
125
|
-
<div>Material: Nylon, Polyester</div>
|
|
124
|
+
Activity: Gym, Hiking, Overnight, School, Trail, Travel, Urban
|
|
126
125
|
</div>
|
|
126
|
+
<div>Material: Nylon, Polyester</div>
|
|
127
|
+
</div>
|
|
127
128
|
),
|
|
128
129
|
none: undefined,
|
|
129
130
|
},
|
|
@@ -170,10 +171,10 @@ const meta: Meta<CartItemProps> = {
|
|
|
170
171
|
options: ['Total', 'Final', 'None'],
|
|
171
172
|
mapping: {
|
|
172
173
|
Total: (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
174
|
+
<>
|
|
175
|
+
<Price amount={59.98} variant="strikethrough" />
|
|
176
|
+
<Price amount={55.95} sale />
|
|
177
|
+
</>
|
|
177
178
|
),
|
|
178
179
|
Final: <Price amount={55.95} sale />,
|
|
179
180
|
None: undefined,
|
|
@@ -192,9 +193,9 @@ const meta: Meta<CartItemProps> = {
|
|
|
192
193
|
options: ['totalExcludingTax', 'None'],
|
|
193
194
|
mapping: {
|
|
194
195
|
totalExcludingTax: (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
<>
|
|
197
|
+
<Price amount={53.99} weight="normal" />
|
|
198
|
+
</>
|
|
198
199
|
),
|
|
199
200
|
None: undefined,
|
|
200
201
|
},
|
|
@@ -277,9 +278,9 @@ const meta: Meta<CartItemProps> = {
|
|
|
277
278
|
mapping: {
|
|
278
279
|
none: null,
|
|
279
280
|
RunningOut: (
|
|
280
|
-
|
|
281
|
+
<span>
|
|
281
282
|
<Icon source={WarningWithCircle} size={'16'} aria-hidden={true} />{' '}
|
|
282
|
-
|
|
283
|
+
Out of stock!
|
|
283
284
|
</span>
|
|
284
285
|
),
|
|
285
286
|
},
|
|
@@ -325,26 +326,42 @@ const meta: Meta<CartItemProps> = {
|
|
|
325
326
|
type: 'select',
|
|
326
327
|
labels: {
|
|
327
328
|
Button: 'Button',
|
|
328
|
-
None: 'None'
|
|
329
|
+
None: 'None',
|
|
329
330
|
},
|
|
330
331
|
},
|
|
331
332
|
description: 'Wishlist control.',
|
|
332
333
|
options: ['Button', 'None'],
|
|
333
334
|
mapping: {
|
|
334
335
|
Button: (
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
336
|
+
<Button
|
|
337
|
+
size="medium"
|
|
338
|
+
type="submit"
|
|
339
|
+
icon={<Icon source="Heart" />}
|
|
340
|
+
variant="tertiary"
|
|
341
|
+
>
|
|
342
|
+
Move to wishlist
|
|
343
|
+
</Button>
|
|
343
344
|
),
|
|
344
345
|
None: undefined,
|
|
345
346
|
},
|
|
346
347
|
table: { defaultValue: { summary: 'null' } },
|
|
347
348
|
},
|
|
349
|
+
rowTotalFooter: {
|
|
350
|
+
control: {
|
|
351
|
+
type: 'check',
|
|
352
|
+
labels: ['Super Offer Badge'],
|
|
353
|
+
},
|
|
354
|
+
description: 'Content displayed right below the total row price.',
|
|
355
|
+
options: ['SuperOffer'],
|
|
356
|
+
mapping: {
|
|
357
|
+
SuperOffer: (
|
|
358
|
+
<div style={{ color: 'var(--color-alert-800)', fontWeight: 'bold' }}>
|
|
359
|
+
Super offer
|
|
360
|
+
</div>
|
|
361
|
+
),
|
|
362
|
+
},
|
|
363
|
+
table: { defaultValue: { summary: 'null' } },
|
|
364
|
+
},
|
|
348
365
|
footer: {
|
|
349
366
|
control: {
|
|
350
367
|
type: 'check',
|
|
@@ -354,24 +371,24 @@ const meta: Meta<CartItemProps> = {
|
|
|
354
371
|
'Final Sales and Returns Policy',
|
|
355
372
|
],
|
|
356
373
|
},
|
|
357
|
-
description: 'Footer content.',
|
|
374
|
+
description: 'Footer content displayed at the bottom of the cart item.',
|
|
358
375
|
options: ['Promotions', 'Delivery', 'Returns'],
|
|
359
376
|
mapping: {
|
|
360
377
|
Promotions: <div>Extra 20% Off Clearance with Code: EXTRA20</div>,
|
|
361
378
|
Delivery: (
|
|
379
|
+
<div>
|
|
380
|
+
<div>Free Shipping</div>
|
|
362
381
|
<div>
|
|
363
|
-
|
|
364
|
-
<
|
|
365
|
-
Delivery Estimate
|
|
366
|
-
<p>Order now for delivery Aug 26 - Aug 28 to ZIP code: 80201</p>
|
|
367
|
-
</div>
|
|
382
|
+
Delivery Estimate
|
|
383
|
+
<p>Order now for delivery Aug 26 - Aug 28 to ZIP code: 80201</p>
|
|
368
384
|
</div>
|
|
385
|
+
</div>
|
|
369
386
|
),
|
|
370
387
|
Returns: (
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
388
|
+
<div>
|
|
389
|
+
Final-sale items, identified by a price ending in .99 or .97, cannot
|
|
390
|
+
be canceled or returned.
|
|
391
|
+
</div>
|
|
375
392
|
),
|
|
376
393
|
},
|
|
377
394
|
table: { defaultValue: { summary: 'null' } },
|
|
@@ -440,7 +457,6 @@ export const CartItem: Story = {
|
|
|
440
457
|
discount: 'none' as any,
|
|
441
458
|
savings: 'none' as any,
|
|
442
459
|
actions: 'Button' as any,
|
|
443
|
-
footer: null as any,
|
|
444
460
|
warning: 'none' as any,
|
|
445
461
|
alert: 'none' as any,
|
|
446
462
|
loading: false,
|
|
@@ -453,48 +469,48 @@ export const CartItem: Story = {
|
|
|
453
469
|
},
|
|
454
470
|
play: async () => {
|
|
455
471
|
const canvasElement = document.querySelector(
|
|
456
|
-
|
|
472
|
+
'#storybook-root'
|
|
457
473
|
) as HTMLElement;
|
|
458
474
|
const canvas = within(canvasElement);
|
|
459
475
|
|
|
460
476
|
const itemImage = document.querySelector(
|
|
461
|
-
|
|
477
|
+
'.dropin-cart-item__image'
|
|
462
478
|
) as HTMLElement;
|
|
463
479
|
expect(itemImage).toBeVisible();
|
|
464
480
|
const itemTitle = document.querySelector(
|
|
465
|
-
|
|
481
|
+
'.dropin-cart-item__title'
|
|
466
482
|
) as HTMLElement;
|
|
467
483
|
expect(itemTitle).toBeVisible();
|
|
468
484
|
const itemSku = document.querySelector(
|
|
469
|
-
|
|
485
|
+
'.dropin-cart-item__sku'
|
|
470
486
|
) as HTMLElement;
|
|
471
487
|
expect(itemSku).toBeVisible();
|
|
472
488
|
const itemConfigurations = document.querySelector(
|
|
473
|
-
|
|
489
|
+
'.dropin-cart-item__configurations'
|
|
474
490
|
) as HTMLElement;
|
|
475
491
|
expect(itemConfigurations).toBeVisible();
|
|
476
492
|
const itemPrice = document.querySelector(
|
|
477
|
-
|
|
493
|
+
'.dropin-cart-item__price'
|
|
478
494
|
) as HTMLElement;
|
|
479
495
|
expect(itemPrice).toBeVisible();
|
|
480
496
|
const quantityStepper = document.querySelector(
|
|
481
|
-
|
|
497
|
+
'.dropin-cart-item__quantity'
|
|
482
498
|
) as HTMLElement;
|
|
483
499
|
expect(quantityStepper).toBeVisible();
|
|
484
500
|
const itemTotal = document.querySelector(
|
|
485
|
-
|
|
501
|
+
'.dropin-cart-item__total'
|
|
486
502
|
) as HTMLElement;
|
|
487
503
|
expect(itemTotal).toBeVisible();
|
|
488
504
|
const actions = document.querySelector(
|
|
489
|
-
|
|
505
|
+
'.dropin-cart-item__buttons'
|
|
490
506
|
) as HTMLElement;
|
|
491
507
|
expect(actions).toBeVisible();
|
|
492
508
|
|
|
493
509
|
const increaseButton = document.querySelector(
|
|
494
|
-
|
|
510
|
+
'button[aria-label="Increase Quantity"]'
|
|
495
511
|
) as HTMLElement;
|
|
496
512
|
const decreaseButton = document.querySelector(
|
|
497
|
-
|
|
513
|
+
'button[aria-label="Decrease Quantity"]'
|
|
498
514
|
) as HTMLElement;
|
|
499
515
|
|
|
500
516
|
// Without this wait test failing intermittently as click event is triggering before even element fully loaded
|
|
@@ -542,7 +558,6 @@ export const ReadOnly: Story = {
|
|
|
542
558
|
attributes: 'none' as any,
|
|
543
559
|
quantity: 1,
|
|
544
560
|
description: 'Description' as any,
|
|
545
|
-
footer: null as any,
|
|
546
561
|
warning: 'none' as any,
|
|
547
562
|
alert: 'none' as any,
|
|
548
563
|
discount: 'none' as any,
|
|
@@ -608,7 +623,6 @@ export const DropdownQuantity: Story = {
|
|
|
608
623
|
description: 'Description' as any,
|
|
609
624
|
discount: 'none' as any,
|
|
610
625
|
savings: 'none' as any,
|
|
611
|
-
footer: null as any,
|
|
612
626
|
warning: 'none' as any,
|
|
613
627
|
alert: 'none' as any,
|
|
614
628
|
loading: false,
|
|
@@ -626,3 +640,34 @@ export const DropdownQuantity: Story = {
|
|
|
626
640
|
quantityType: 'dropdown',
|
|
627
641
|
},
|
|
628
642
|
};
|
|
643
|
+
|
|
644
|
+
export const WithPromotionalBadge: Story = {
|
|
645
|
+
args: {
|
|
646
|
+
ariaLabel: 'Short Name',
|
|
647
|
+
image: 'Image' as any,
|
|
648
|
+
title: 'Short' as any,
|
|
649
|
+
price: 'Price' as any,
|
|
650
|
+
rowTotalFooter: 'SuperOffer' as any,
|
|
651
|
+
total: 'Final' as any,
|
|
652
|
+
sku: 'Sku' as any,
|
|
653
|
+
quantity: 1,
|
|
654
|
+
warning: 'none' as any,
|
|
655
|
+
alert: 'none' as any,
|
|
656
|
+
discount: 'none' as any,
|
|
657
|
+
savings: 'none' as any,
|
|
658
|
+
loading: false,
|
|
659
|
+
updating: false,
|
|
660
|
+
configurations: {
|
|
661
|
+
Color: 'Blue',
|
|
662
|
+
Size: 'L',
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
play: async ({ canvasElement }) => {
|
|
666
|
+
// Verify row total footer is rendered below the total
|
|
667
|
+
const rowTotalFooter = canvasElement.querySelector(
|
|
668
|
+
'.dropin-cart-item__row-total-footer'
|
|
669
|
+
) as HTMLElement;
|
|
670
|
+
expect(rowTotalFooter).toBeTruthy();
|
|
671
|
+
expect(rowTotalFooter?.textContent).toContain('Super offer');
|
|
672
|
+
},
|
|
673
|
+
};
|
|
@@ -30,6 +30,7 @@ export interface CartItemProps
|
|
|
30
30
|
image?: VNode;
|
|
31
31
|
title?: VNode;
|
|
32
32
|
price?: VNode;
|
|
33
|
+
rowTotalFooter?: VNode;
|
|
33
34
|
taxIncluded?: boolean;
|
|
34
35
|
taxExcluded?: boolean;
|
|
35
36
|
total?: VNode;
|
|
@@ -62,6 +63,7 @@ export const CartItem: FunctionComponent<CartItemProps> = ({
|
|
|
62
63
|
image,
|
|
63
64
|
title,
|
|
64
65
|
price,
|
|
66
|
+
rowTotalFooter,
|
|
65
67
|
taxIncluded = false,
|
|
66
68
|
taxExcluded = false,
|
|
67
69
|
total,
|
|
@@ -434,6 +436,14 @@ export const CartItem: FunctionComponent<CartItemProps> = ({
|
|
|
434
436
|
className={classes(['dropin-cart-item__savings'])}
|
|
435
437
|
/>
|
|
436
438
|
)}
|
|
439
|
+
|
|
440
|
+
{/* Row Total Footer */}
|
|
441
|
+
{rowTotalFooter && (
|
|
442
|
+
<VComponent
|
|
443
|
+
node={rowTotalFooter}
|
|
444
|
+
className={classes(['dropin-cart-item__row-total-footer'])}
|
|
445
|
+
/>
|
|
446
|
+
)}
|
|
437
447
|
</div>
|
|
438
448
|
|
|
439
449
|
{/* Footer */}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
// https://storybook.js.org/docs/7.0/preact/writing-stories/introduction
|
|
@@ -67,6 +67,12 @@ export const CartList: Story = {
|
|
|
67
67
|
tags: ['skip'],
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
export const WithPromotionalBadges: Story = {
|
|
71
|
+
args: {
|
|
72
|
+
children: renderItemsWithPromos(3),
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
70
76
|
function renderItems(count: number) {
|
|
71
77
|
return Array.from({ length: count }, (_, i) => i + 1).map((key) => (
|
|
72
78
|
<CartItem
|
|
@@ -109,3 +115,60 @@ function renderItems(count: number) {
|
|
|
109
115
|
/>
|
|
110
116
|
));
|
|
111
117
|
}
|
|
118
|
+
|
|
119
|
+
function renderItemsWithPromos(count: number) {
|
|
120
|
+
return Array.from({ length: count }, (_, i) => i + 1).map((key) => {
|
|
121
|
+
// Show promo badge on first and third items as an example
|
|
122
|
+
const showPromo = key === 1 || key === 3;
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<CartItem
|
|
126
|
+
key={key}
|
|
127
|
+
image={
|
|
128
|
+
<Image
|
|
129
|
+
src="https://picsum.photos/132/184"
|
|
130
|
+
width="132"
|
|
131
|
+
height="184"
|
|
132
|
+
alt="Some alternative text"
|
|
133
|
+
loading="lazy"
|
|
134
|
+
/>
|
|
135
|
+
}
|
|
136
|
+
title={<div>Product Name {key}</div>}
|
|
137
|
+
description={
|
|
138
|
+
<div>
|
|
139
|
+
Secondary product information such as brand name, description, etc.
|
|
140
|
+
</div>
|
|
141
|
+
}
|
|
142
|
+
sku={<div>SKU: 59YK{key}</div>}
|
|
143
|
+
quantity={1}
|
|
144
|
+
price={
|
|
145
|
+
<span>
|
|
146
|
+
<Price
|
|
147
|
+
amount={53.99}
|
|
148
|
+
style={{ fontWeight: 'inherit', color: 'inherit' }}
|
|
149
|
+
/>
|
|
150
|
+
</span>
|
|
151
|
+
}
|
|
152
|
+
rowTotalFooter={
|
|
153
|
+
showPromo ? (
|
|
154
|
+
<div
|
|
155
|
+
style={{ color: 'var(--color-alert-800)', fontWeight: 'bold' }}
|
|
156
|
+
>
|
|
157
|
+
Super offer
|
|
158
|
+
</div>
|
|
159
|
+
) : undefined
|
|
160
|
+
}
|
|
161
|
+
total={
|
|
162
|
+
<span>
|
|
163
|
+
<Price amount={59.98} variant="strikethrough" />{' '}
|
|
164
|
+
<Price amount={55.95} sale />
|
|
165
|
+
</span>
|
|
166
|
+
}
|
|
167
|
+
configurations={{ Color: 'Blue', Size: 'L' }}
|
|
168
|
+
onRemove={() => {}}
|
|
169
|
+
onQuantity={() => {}}
|
|
170
|
+
taxIncluded={true}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
// https://storybook.js.org/docs/7.0/preact/writing-stories/introduction
|
|
@@ -316,3 +316,21 @@ export const MandatoryFieldFloatingLabelWithValue: Story = {
|
|
|
316
316
|
],
|
|
317
317
|
},
|
|
318
318
|
};
|
|
319
|
+
|
|
320
|
+
export const SingleOption: Story = {
|
|
321
|
+
name: 'Single option',
|
|
322
|
+
args: {
|
|
323
|
+
name: 'pickerField',
|
|
324
|
+
variant: 'primary',
|
|
325
|
+
defaultOption: {
|
|
326
|
+
value: 'option1',
|
|
327
|
+
text: 'Only Option',
|
|
328
|
+
},
|
|
329
|
+
options: [
|
|
330
|
+
{
|
|
331
|
+
value: 'option1',
|
|
332
|
+
text: 'Only Option',
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
},
|
|
336
|
+
};
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
import { Icon } from '@adobe-commerce/elsie/components';
|
|
@@ -74,6 +74,7 @@ export const Picker: FunctionComponent<PickerProps> = ({
|
|
|
74
74
|
}) => {
|
|
75
75
|
const uniqueId = id ?? name ?? `dropin-picker-${Math.random().toString(36)}`;
|
|
76
76
|
const isRequired = !!props?.required;
|
|
77
|
+
const isDisabled = disabled || options?.length === 1;
|
|
77
78
|
|
|
78
79
|
// find the first option that is not disabled
|
|
79
80
|
const firstAvailableOption = options?.find((option) => !option.disabled);
|
|
@@ -143,7 +144,7 @@ export const Picker: FunctionComponent<PickerProps> = ({
|
|
|
143
144
|
['dropin-picker__floating', !!floatingLabel],
|
|
144
145
|
['dropin-picker__selected', isSelected],
|
|
145
146
|
['dropin-picker__error', error],
|
|
146
|
-
['dropin-picker__disabled',
|
|
147
|
+
['dropin-picker__disabled', isDisabled],
|
|
147
148
|
['dropin-picker__icon', icon],
|
|
148
149
|
])}
|
|
149
150
|
>
|
|
@@ -165,7 +166,7 @@ export const Picker: FunctionComponent<PickerProps> = ({
|
|
|
165
166
|
])}
|
|
166
167
|
name={name}
|
|
167
168
|
aria-label={name}
|
|
168
|
-
disabled={
|
|
169
|
+
disabled={isDisabled}
|
|
169
170
|
onChange={handleOptionClick}
|
|
170
171
|
{...props}
|
|
171
172
|
>
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
/* https://cssguidelin.es/#bem-like-naming */
|
|
@@ -64,6 +64,11 @@
|
|
|
64
64
|
box-shadow: 0 0 0 var(--shape-icon-stroke-4) var(--color-neutral-400);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
.dropin-radio-button__icon {
|
|
68
|
+
margin-right: var(--spacing-xsmall);
|
|
69
|
+
flex-shrink: 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
67
72
|
.dropin-radio-button__description {
|
|
68
73
|
clear: both;
|
|
69
74
|
color: var(--color-neutral-700);
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
// https://storybook.js.org/docs/7.0/preact/writing-stories/introduction
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
RadioButtonProps,
|
|
15
15
|
} from '@adobe-commerce/elsie/components/RadioButton';
|
|
16
16
|
import { expect, userEvent, within } from '@storybook/test';
|
|
17
|
+
import { IconsList } from '@adobe-commerce/elsie/components/Icon/Icon.stories.helpers';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Use Radio Buttons to let users select one option from a set of mutually exclusive choices.
|
|
@@ -91,6 +92,15 @@ const meta: Meta<RadioButtonProps> = {
|
|
|
91
92
|
name: 'boolean',
|
|
92
93
|
},
|
|
93
94
|
},
|
|
95
|
+
icon: {
|
|
96
|
+
description:
|
|
97
|
+
'Optional icon to display before the label (SVG or img element)',
|
|
98
|
+
options: Object.keys(IconsList),
|
|
99
|
+
mapping: IconsList,
|
|
100
|
+
control: {
|
|
101
|
+
type: 'select',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
94
104
|
},
|
|
95
105
|
};
|
|
96
106
|
|
|
@@ -124,3 +134,27 @@ export const RadioButtonStory: Story = {
|
|
|
124
134
|
await expect(radioButton).toBeChecked();
|
|
125
135
|
},
|
|
126
136
|
};
|
|
137
|
+
|
|
138
|
+
export const RadioButtonWithIcon: Story = {
|
|
139
|
+
name: 'Radio button with icon',
|
|
140
|
+
args: {
|
|
141
|
+
name: 'shipping',
|
|
142
|
+
label: 'Free Shipping',
|
|
143
|
+
value: 'free-shipping',
|
|
144
|
+
description: 'Delivery in 5-7 business days',
|
|
145
|
+
size: 'medium',
|
|
146
|
+
disabled: false,
|
|
147
|
+
error: false,
|
|
148
|
+
// @ts-ignore - icon is mapped from IconsList
|
|
149
|
+
icon: 'Delivery',
|
|
150
|
+
},
|
|
151
|
+
play: async ({ canvasElement }) => {
|
|
152
|
+
const canvas = within(canvasElement);
|
|
153
|
+
const radioButton = await canvas.findByRole('radio');
|
|
154
|
+
const radioButtonContainer = radioButton.closest('.dropin-radio-button');
|
|
155
|
+
const icon = radioButtonContainer?.querySelector(
|
|
156
|
+
'.dropin-radio-button__icon'
|
|
157
|
+
);
|
|
158
|
+
await expect(icon).toBeInTheDocument();
|
|
159
|
+
},
|
|
160
|
+
};
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
import { FunctionComponent, VNode } from 'preact';
|
|
@@ -13,7 +13,7 @@ import { classes } from '@adobe-commerce/elsie/lib';
|
|
|
13
13
|
import '@adobe-commerce/elsie/components/RadioButton/RadioButton.css';
|
|
14
14
|
|
|
15
15
|
export interface RadioButtonProps
|
|
16
|
-
extends Omit<HTMLAttributes<HTMLInputElement>, 'size' | 'label'> {
|
|
16
|
+
extends Omit<HTMLAttributes<HTMLInputElement>, 'size' | 'label' | 'icon'> {
|
|
17
17
|
label: string | VNode<HTMLAttributes<HTMLElement>>;
|
|
18
18
|
name: string;
|
|
19
19
|
value: string;
|
|
@@ -23,6 +23,9 @@ export interface RadioButtonProps
|
|
|
23
23
|
error?: boolean;
|
|
24
24
|
description?: string;
|
|
25
25
|
busy?: boolean;
|
|
26
|
+
icon?:
|
|
27
|
+
| VNode<HTMLAttributes<SVGSVGElement>>
|
|
28
|
+
| VNode<HTMLAttributes<HTMLImageElement>>;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export const RadioButton: FunctionComponent<RadioButtonProps> = ({
|
|
@@ -35,6 +38,7 @@ export const RadioButton: FunctionComponent<RadioButtonProps> = ({
|
|
|
35
38
|
error = false,
|
|
36
39
|
description = '',
|
|
37
40
|
busy = false,
|
|
41
|
+
icon,
|
|
38
42
|
className,
|
|
39
43
|
children,
|
|
40
44
|
...props
|
|
@@ -70,6 +74,16 @@ export const RadioButton: FunctionComponent<RadioButtonProps> = ({
|
|
|
70
74
|
['dropin-radio-button__label--disabled', disabled],
|
|
71
75
|
])}
|
|
72
76
|
>
|
|
77
|
+
{icon && (
|
|
78
|
+
// @ts-ignore
|
|
79
|
+
<icon.type
|
|
80
|
+
{...icon?.props}
|
|
81
|
+
className={classes([
|
|
82
|
+
'dropin-radio-button__icon',
|
|
83
|
+
icon?.props?.className,
|
|
84
|
+
])}
|
|
85
|
+
/>
|
|
86
|
+
)}
|
|
73
87
|
{label}
|
|
74
88
|
</span>
|
|
75
89
|
<span
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
export * from '@adobe-commerce/elsie/components/RadioButton/RadioButton';
|
|
@@ -53,7 +53,7 @@ export function getPriceFormatter(
|
|
|
53
53
|
|
|
54
54
|
const params: Intl.NumberFormatOptions = {
|
|
55
55
|
style: 'currency',
|
|
56
|
-
currency: currency
|
|
56
|
+
currency: currency && currency !== 'NONE' ? currency : 'USD',
|
|
57
57
|
// These options are needed to round to whole numbers if that's what you want.
|
|
58
58
|
minimumFractionDigits: 2, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1)
|
|
59
59
|
maximumFractionDigits: 2, // (causes 2500.99 to be printed as $2,501)
|