@adobe-commerce/elsie 1.5.1-alpha003 → 1.6.0-alpha1

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.
Files changed (31) hide show
  1. package/bin/builders/serve/index.js +3 -1
  2. package/config/jest.js +3 -3
  3. package/config/vite.mjs +13 -8
  4. package/package.json +3 -3
  5. package/src/components/Button/Button.tsx +2 -0
  6. package/src/components/Incrementer/Incrementer.css +6 -0
  7. package/src/components/Incrementer/Incrementer.stories.tsx +18 -0
  8. package/src/components/Incrementer/Incrementer.tsx +66 -59
  9. package/src/components/MultiSelect/MultiSelect.css +273 -0
  10. package/src/components/MultiSelect/MultiSelect.stories.tsx +457 -0
  11. package/src/components/MultiSelect/MultiSelect.tsx +763 -0
  12. package/src/components/MultiSelect/index.ts +11 -0
  13. package/src/components/Picker/Picker.tsx +5 -3
  14. package/src/components/ProductItemCard/ProductItemCard.css +1 -39
  15. package/src/components/ProductItemCard/ProductItemCard.stories.tsx +7 -10
  16. package/src/components/ProductItemCard/ProductItemCard.tsx +4 -21
  17. package/src/components/Table/Table.css +110 -0
  18. package/src/components/Table/Table.stories.tsx +761 -0
  19. package/src/components/Table/Table.tsx +249 -0
  20. package/src/components/Table/index.ts +11 -0
  21. package/src/components/TextSwatch/TextSwatch.tsx +5 -2
  22. package/src/components/ToggleButton/ToggleButton.css +13 -1
  23. package/src/components/ToggleButton/ToggleButton.stories.tsx +13 -6
  24. package/src/components/ToggleButton/ToggleButton.tsx +4 -0
  25. package/src/components/index.ts +5 -3
  26. package/src/docs/API/render.mdx +16 -0
  27. package/src/docs/slots.mdx +9 -1
  28. package/src/i18n/en_US.json +38 -0
  29. package/src/lib/aem/configs.ts +7 -4
  30. package/src/lib/render.tsx +21 -7
  31. package/src/lib/slot.tsx +99 -30
@@ -4,7 +4,9 @@ const path = require('path');
4
4
  module.exports = async function generateResourceBuilder({ argv }) {
5
5
  const { build, preview } = await import('vite');
6
6
 
7
- const configFile = argv?.config ?? path.resolve(__dirname, '../../../config/vite.mjs');
7
+ const configFile =
8
+ argv?.config ??
9
+ path.resolve(...[__dirname, '..', '..', '..', 'config', 'vite.mjs']);
8
10
 
9
11
  let built = false;
10
12
 
package/config/jest.js CHANGED
@@ -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
  const path = require('path');
package/config/vite.mjs CHANGED
@@ -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 { glob } from 'glob';
@@ -25,7 +25,12 @@ import banner from 'vite-plugin-banner';
25
25
  const env = loadEnv('', process.cwd());
26
26
 
27
27
  // Load Elsie Config
28
- const elsieConfig = await import(path.resolve(process.cwd(), './.elsie.js')).then((m) => m.default);
28
+ const elsieConfigPath = path.resolve(process.cwd(), './.elsie.js');
29
+ // Convert Windows paths to file:// URLs for ES module imports
30
+ const elsieConfigUrl = elsieConfigPath.startsWith('file://')
31
+ ? elsieConfigPath
32
+ : `file://${elsieConfigPath.replace(/\\/g, '/')}`;
33
+ const elsieConfig = await import(elsieConfigUrl).then((m) => m.default);
29
34
 
30
35
  // Read package.json using createRequire (compatible with Node 20 and 22)
31
36
  const require = createRequire(import.meta.url);
@@ -293,19 +298,19 @@ export default {
293
298
  generateBundle(options, bundle) {
294
299
  for (const fileName in bundle) {
295
300
  const chunk = bundle[fileName];
296
-
301
+
297
302
  // Process both .map files and JS/TS files with sourcemaps
298
- if ((chunk.type === 'asset' && fileName.endsWith('.map')) ||
303
+ if ((chunk.type === 'asset' && fileName.endsWith('.map')) ||
299
304
  (chunk.type === 'chunk' && chunk.map)) {
300
305
  try {
301
306
  // Get the sourcemap object - either from the asset source or the chunk's map
302
307
  const map = chunk.type === 'asset' ? JSON.parse(chunk.source) : chunk.map;
303
-
308
+
304
309
  if (map.sources) {
305
310
  map.sources = map.sources.map((input) => {
306
311
  return input.replace(/(?:\.\.?\/)+src\//, `/${packageJSON.name}/src/`);
307
312
  });
308
-
313
+
309
314
  // Update the sourcemap in the appropriate place
310
315
  if (chunk.type === 'asset') {
311
316
  chunk.source = JSON.stringify(map);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe-commerce/elsie",
3
- "version": "1.5.1-alpha003",
3
+ "version": "1.6.0-alpha1",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "description": "Domain Package SDK",
6
6
  "engines": {
@@ -27,8 +27,8 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@adobe-commerce/event-bus": "~1.0.1",
30
- "@adobe-commerce/fetch-graphql": "~1.1.0",
31
- "@adobe-commerce/recaptcha": "~1.0.1",
30
+ "@adobe-commerce/fetch-graphql": "1.2.0-beta1",
31
+ "@adobe-commerce/recaptcha": "1.0.2-beta1",
32
32
  "@adobe-commerce/storefront-design": "~1.0.0",
33
33
  "@dropins/build-tools": "~1.0.1",
34
34
  "preact": "~10.22.1",
@@ -25,6 +25,8 @@ export interface ButtonProps
25
25
  active?: boolean;
26
26
  activeChildren?: ComponentChildren;
27
27
  activeIcon?: VNode<HTMLAttributes<SVGSVGElement>>;
28
+ href?: string;
29
+ type?: 'button' | 'submit' | 'reset';
28
30
  }
29
31
 
30
32
  export const Button: FunctionComponent<ButtonProps> = ({
@@ -19,6 +19,12 @@
19
19
  opacity: 1;
20
20
  }
21
21
 
22
+ .dropin-incrementer__content--no-buttons {
23
+ grid-template-columns: max-content;
24
+ width: fit-content;
25
+ margin-inline: auto;
26
+ }
27
+
22
28
  .dropin-incrementer__content--disabled {
23
29
  background: var(--color-neutral-300);
24
30
  border-radius: var(--shape-border-radius-1);
@@ -79,6 +79,10 @@ const meta: Meta<IncrementerProps> = {
79
79
  description: 'Maximum length of the input field',
80
80
  type: 'number',
81
81
  },
82
+ showButtons: {
83
+ description: 'Show increase/decrease buttons',
84
+ control: 'boolean',
85
+ },
82
86
  },
83
87
  };
84
88
 
@@ -170,3 +174,17 @@ export const WithError = {
170
174
  await expect(error).toHaveTextContent('Maximum quantity is 100');
171
175
  },
172
176
  };
177
+
178
+ export const WithoutButtons: Story = {
179
+ args: {
180
+ size: 'medium',
181
+ onValue: action('onValue'),
182
+ name: 'incrementerField',
183
+ value: '1',
184
+ min: 1,
185
+ max: 100,
186
+ disabled: false,
187
+ 'aria-label': 'Quantity',
188
+ showButtons: false,
189
+ },
190
+ };
@@ -28,6 +28,7 @@ export interface IncrementerProps
28
28
  max?: number;
29
29
  disabled?: boolean;
30
30
  maxLength?: number;
31
+ showButtons?: boolean;
31
32
  }
32
33
 
33
34
  export const Incrementer: FunctionComponent<IncrementerProps> = ({
@@ -42,6 +43,7 @@ export const Incrementer: FunctionComponent<IncrementerProps> = ({
42
43
  onValue,
43
44
  onUpdateError,
44
45
  size = 'medium',
46
+ showButtons = true,
45
47
  ...props
46
48
  }) => {
47
49
  const [currentValue, setCurrentValue] = useState<number>(Number(value));
@@ -99,41 +101,44 @@ export const Incrementer: FunctionComponent<IncrementerProps> = ({
99
101
  className={classes([
100
102
  'dropin-incrementer__content',
101
103
  `dropin-incrementer__content--${size}`,
104
+ ['dropin-incrementer__content--no-buttons', !showButtons],
102
105
  [`dropin-incrementer__content--error`, isInvalid],
103
106
  [`dropin-incrementer__content--success`, success],
104
107
  [`dropin-incrementer__content--disabled`, disabled],
105
108
  ])}
106
109
  >
107
110
  {/* Minus Button */}
108
- <div
109
- className={classes([
110
- 'dropin-incrementer__button-container',
111
- [`dropin-incrementer__button-container--disabled`, disabled],
112
- ])}
113
- >
114
- <Localizer>
115
- <button
116
- type="button"
117
- className={classes([
118
- 'dropin-incrementer__decrease-button',
119
- [`dropin-incrementer__decrease-button--disabled`, disabled],
120
- ])}
121
- onClick={() => handleIncrementer(currentValue - 1)}
122
- disabled={disabled || currentValue < minValue + 1}
123
- aria-label={
124
- (<Text id="Dropin.Incrementer.decreaseLabel" />) as any
125
- }
126
- >
127
- <Icon
128
- source={Minus}
129
- size="16"
130
- stroke="1"
131
- viewBox="4 2 20 20"
132
- className="dropin-incrementer__down"
133
- />
134
- </button>
135
- </Localizer>
136
- </div>
111
+ {showButtons && (
112
+ <div
113
+ className={classes([
114
+ 'dropin-incrementer__button-container',
115
+ [`dropin-incrementer__button-container--disabled`, disabled],
116
+ ])}
117
+ >
118
+ <Localizer>
119
+ <button
120
+ type="button"
121
+ className={classes([
122
+ 'dropin-incrementer__decrease-button',
123
+ [`dropin-incrementer__decrease-button--disabled`, disabled],
124
+ ])}
125
+ onClick={() => handleIncrementer(currentValue - 1)}
126
+ disabled={disabled || currentValue < minValue + 1}
127
+ aria-label={
128
+ (<Text id="Dropin.Incrementer.decreaseLabel" />) as any
129
+ }
130
+ >
131
+ <Icon
132
+ source={Minus}
133
+ size="16"
134
+ stroke="1"
135
+ viewBox="4 2 20 20"
136
+ className="dropin-incrementer__down"
137
+ />
138
+ </button>
139
+ </Localizer>
140
+ </div>
141
+ )}
137
142
 
138
143
  {/* Input Field */}
139
144
  <input
@@ -157,36 +162,38 @@ export const Incrementer: FunctionComponent<IncrementerProps> = ({
157
162
  {...props}
158
163
  />
159
164
 
160
- <div
161
- className={classes([
162
- 'dropin-incrementer__button-container',
163
- [`dropin-incrementer__button-container--disabled`, disabled],
164
- ])}
165
- >
166
- {/* Plus/Add button */}
167
- <Localizer>
168
- <button
169
- type="button"
170
- className={classes([
171
- 'dropin-incrementer__increase-button',
172
- [`dropin-incrementer__increase-button--disabled`, disabled],
173
- ])}
174
- onClick={() => handleIncrementer(currentValue + 1)}
175
- disabled={disabled || currentValue > maxValue - 1}
176
- aria-label={
177
- (<Text id="Dropin.Incrementer.increaseLabel" />) as any
178
- }
179
- >
180
- <Icon
181
- source={Add}
182
- size="16"
183
- stroke="1"
184
- viewBox="4 2 20 20"
185
- className="dropin-incrementer__add"
186
- />
187
- </button>
188
- </Localizer>
189
- </div>
165
+ {showButtons && (
166
+ <div
167
+ className={classes([
168
+ 'dropin-incrementer__button-container',
169
+ [`dropin-incrementer__button-container--disabled`, disabled],
170
+ ])}
171
+ >
172
+ {/* Plus/Add button */}
173
+ <Localizer>
174
+ <button
175
+ type="button"
176
+ className={classes([
177
+ 'dropin-incrementer__increase-button',
178
+ [`dropin-incrementer__increase-button--disabled`, disabled],
179
+ ])}
180
+ onClick={() => handleIncrementer(currentValue + 1)}
181
+ disabled={disabled || currentValue > maxValue - 1}
182
+ aria-label={
183
+ (<Text id="Dropin.Incrementer.increaseLabel" />) as any
184
+ }
185
+ >
186
+ <Icon
187
+ source={Add}
188
+ size="16"
189
+ stroke="1"
190
+ viewBox="4 2 20 20"
191
+ className="dropin-incrementer__add"
192
+ />
193
+ </button>
194
+ </Localizer>
195
+ </div>
196
+ )}
190
197
  </div>
191
198
  {isInvalid && (
192
199
  <p className="dropin-incrementer__content--error-message">
@@ -0,0 +1,273 @@
1
+ /********************************************************************
2
+ * Copyright 2025 Adobe
3
+ * All Rights Reserved.
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.
8
+ *******************************************************************/
9
+
10
+ .dropin-multi-select {
11
+ position: relative;
12
+ min-width: 300px;
13
+ }
14
+
15
+ .dropin-multi-select__input-hidden {
16
+ display: none;
17
+ }
18
+
19
+ .dropin-multi-select__container {
20
+ min-height: 56px;
21
+ box-sizing: border-box;
22
+ border: var(--shape-border-width-1) solid var(--color-neutral-600);
23
+ border-radius: var(--shape-border-radius-1);
24
+ padding: var(--spacing-xsmall) var(--spacing-small);
25
+ cursor: text;
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: space-between;
29
+ gap: var(--spacing-xsmall);
30
+ transition: all 0.2s ease;
31
+ background-color: var(--color-neutral-50);
32
+ }
33
+
34
+ .dropin-multi-select__container--with-floating-label {
35
+ padding: var(--spacing-small) var(--spacing-small) var(--spacing-xsmall)
36
+ var(--spacing-small);
37
+ }
38
+
39
+ .dropin-multi-select__container:hover {
40
+ border-color: var(--color-neutral-700);
41
+ }
42
+
43
+ .dropin-multi-select__container--open {
44
+ border-color: var(--color-neutral-700);
45
+ }
46
+
47
+ .dropin-multi-select__container--disabled {
48
+ background-color: var(--color-neutral-200);
49
+ cursor: not-allowed;
50
+ opacity: 0.6;
51
+ }
52
+
53
+ .dropin-multi-select__container--error {
54
+ border: var(--shape-border-width-2) solid var(--color-alert-500);
55
+ }
56
+
57
+ .dropin-multi-select__container--success {
58
+ border: var(--shape-border-width-2) solid var(--color-positive-500);
59
+ }
60
+
61
+ .dropin-multi-select__tags-area {
62
+ flex: 1;
63
+ display: flex;
64
+ flex-wrap: wrap;
65
+ align-items: center;
66
+ gap: var(--spacing-xsmall);
67
+ max-width: calc(100% - 30px);
68
+ overflow: hidden;
69
+ }
70
+
71
+ .dropin-multi-select__tag {
72
+ padding: 2px 16px;
73
+ }
74
+
75
+ .dropin-multi-select__floating-label {
76
+ position: absolute;
77
+ top: 50%;
78
+ left: 12px;
79
+ transform: translateY(-50%);
80
+ cursor: text;
81
+ background-color: var(--color-neutral-50);
82
+ padding: 0 var(--spacing-xxsmall);
83
+ font: var(--type-body-1-default-font);
84
+ letter-spacing: var(--type-body-1-default-letter-spacing);
85
+ color: var(--color-neutral-500);
86
+ pointer-events: none;
87
+ transition: all 0.2s ease;
88
+ z-index: 1;
89
+ }
90
+
91
+ .dropin-multi-select__container--with-floating-label.dropin-multi-select__container--has-value,
92
+ .dropin-multi-select__container--with-floating-label.dropin-multi-select__container--open,
93
+ .dropin-multi-select__container--with-floating-label:focus-within {
94
+ padding-top: var(--spacing-big);
95
+ }
96
+
97
+ .dropin-multi-select__container--with-floating-label.dropin-multi-select__container--has-value
98
+ .dropin-multi-select__floating-label,
99
+ .dropin-multi-select__container--with-floating-label.dropin-multi-select__container--open
100
+ .dropin-multi-select__floating-label,
101
+ .dropin-multi-select__container--with-floating-label:focus-within
102
+ .dropin-multi-select__floating-label {
103
+ top: 12px;
104
+ left: 12px;
105
+ transform: translateY(0);
106
+ font-size: 12px;
107
+ color: var(--color-neutral-800);
108
+ font: var(--type-details-caption-1-font);
109
+ letter-spacing: var(--type-details-caption-1-letter-spacing);
110
+ background-color: var(--color-neutral-50);
111
+ z-index: 2;
112
+ }
113
+
114
+ .dropin-multi-select__tag-remove {
115
+ background: none;
116
+ border: none;
117
+ cursor: pointer;
118
+ padding: 0;
119
+ display: flex;
120
+ align-items: center;
121
+ transition: color 0.2s ease;
122
+ color: var(--color-neutral-600);
123
+ }
124
+
125
+ .dropin-multi-select__search {
126
+ outline: none;
127
+ background: transparent;
128
+ border: none;
129
+ font: var(--type-body-1-default-font);
130
+ letter-spacing: var(--type-body-1-default-letter-spacing);
131
+ padding: 0;
132
+ }
133
+
134
+ .dropin-multi-select__search:empty {
135
+ width: 100%;
136
+ }
137
+
138
+ .dropin-multi-select__search::placeholder {
139
+ font: var(--type-body-1-default-font);
140
+ }
141
+
142
+ .dropin-multi-select__container--with-floating-label
143
+ .dropin-multi-select__search::placeholder {
144
+ opacity: 0;
145
+ }
146
+
147
+ .dropin-multi-select__container--with-floating-label.dropin-multi-select__container--open
148
+ .dropin-multi-select__search::placeholder {
149
+ opacity: 1;
150
+ }
151
+
152
+ .dropin-multi-select__tags-area--has-values .dropin-multi-select__search {
153
+ width: 30px;
154
+ }
155
+
156
+ .dropin-multi-select__chevron {
157
+ transition: transform 0.2s ease;
158
+ flex-shrink: 0;
159
+ position: absolute;
160
+ top: 50%;
161
+ right: 12px;
162
+ transform: translateY(-50%);
163
+ }
164
+
165
+ .dropin-multi-select__chevron--open {
166
+ transform: translateY(-50%) rotate(180deg);
167
+ }
168
+
169
+ .dropin-multi-select__dropdown {
170
+ position: absolute;
171
+ overflow: hidden;
172
+ z-index: 50;
173
+ width: 100%;
174
+ margin-top: var(--spacing-xxsmall);
175
+ background-color: var(--color-neutral-50);
176
+ border: var(--shape-border-width-1) solid var(--color-neutral-700);
177
+ border-radius: var(--shape-border-radius-1);
178
+ box-shadow: var(--shape-shadow-1);
179
+ box-sizing: border-box;
180
+ }
181
+
182
+ .dropin-multi-select__controls {
183
+ position: sticky;
184
+ top: 0;
185
+ z-index: 10;
186
+ display: flex;
187
+ justify-content: space-between;
188
+ align-items: center;
189
+ gap: var(--spacing-xsmall);
190
+ border-bottom: var(--shape-border-width-1) solid var(--color-neutral-300);
191
+ background-color: var(--color-neutral-200);
192
+ }
193
+
194
+ .dropin-multi-select__button {
195
+ flex: 1;
196
+ font: var(--type-body-1-default-font);
197
+ letter-spacing: var(--type-body-1-default-letter-spacing);
198
+ cursor: pointer;
199
+ }
200
+
201
+ .dropin-multi-select__button--deselect-all:disabled {
202
+ opacity: 0.5;
203
+ cursor: not-allowed;
204
+ }
205
+
206
+ .dropin-multi-select__button--deselect-all:disabled:hover {
207
+ background-color: var(--color-neutral-200);
208
+ }
209
+
210
+ .dropin-multi-select__button--select-all,
211
+ .dropin-multi-select__button--deselect-all {
212
+ padding-left: var(--spacing-xsmall);
213
+ padding-right: var(--spacing-xsmall);
214
+ }
215
+
216
+ .dropin-multi-select__options {
217
+ overflow: auto;
218
+ }
219
+
220
+ .dropin-multi-select__list {
221
+ list-style: none;
222
+ margin: 0;
223
+ padding: var(--spacing-xxsmall) 0;
224
+ }
225
+
226
+ .dropin-multi-select__option {
227
+ padding: var(--spacing-xsmall) 12px;
228
+ cursor: pointer;
229
+ display: flex;
230
+ align-items: center;
231
+ justify-content: space-between;
232
+ transition: background-color 0.15s ease;
233
+ font: var(--type-body-1-default-font);
234
+ letter-spacing: var(--type-body-1-default-letter-spacing);
235
+ }
236
+
237
+ .dropin-multi-select__option:hover {
238
+ background-color: var(--color-neutral-300);
239
+ }
240
+
241
+ .dropin-multi-select__option--focused {
242
+ background-color: var(--color-neutral-200);
243
+ }
244
+
245
+ .dropin-multi-select__option--selected {
246
+ color: var(--color-brand-700);
247
+ font: var(--type-body-1-emphasized-font);
248
+ letter-spacing: var(--type-body-1-emphasized-letter-spacing);
249
+ }
250
+
251
+ .dropin-multi-select__option-label {
252
+ flex: 1;
253
+ }
254
+
255
+ .dropin-multi-select__option-label--disabled {
256
+ color: var(--color-neutral-200);
257
+ }
258
+
259
+ .dropin-multi-select__no-results {
260
+ padding: var(--spacing-small);
261
+ text-align: center;
262
+ font: var(--type-body-1-default-font);
263
+ color: var(--color-neutral-500);
264
+ }
265
+
266
+ .dropin-multi-select__sr-only {
267
+ position: absolute;
268
+ left: -10000px;
269
+ top: auto;
270
+ width: 1px;
271
+ height: 1px;
272
+ overflow: hidden;
273
+ }