@adobe-commerce/elsie 1.9.0-beta.3 → 1.9.1-alpha-20260702101934

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/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @adobe-commerce/elsie
2
2
 
3
+ ## 1.9.1-alpha-20260702101934
4
+
5
+ ### Patch Changes
6
+
7
+ - fa16e7d: fix(Picker): auto-select and emit the sole option when the control auto-disables, so single-option narrowing on configurable PDPs no longer leaves the value unselected and Add to Cart permanently disabled
8
+
9
+ ## 1.9.0
10
+
11
+ ### Minor Changes
12
+
13
+ - af62897: Update minimum Node.js requirement to 22 LTS
14
+
15
+ Packages are now built with Node.js 22. `elsie` requires `>=22`; browser-only packages (`fetch-graphql`, `event-bus`, `recaptcha`, `storefront-design`, `build-tools`) do not declare an `engines` field as they do not run in Node.js.
16
+
17
+ - 62adf1c: Reduce HTTP requests on page load through three bundling optimizations. The preact runtime is isolated in its own vendor chunk so it no longer co-locates into other chunks. Dropin API and internal component modules are consolidated into `chunks/api.js` and `chunks/components.js` respectively, replacing the previous pattern of one chunk file per function or component. All SVG icons are consolidated into a single `chunks/icons.js` chunk instead of one chunk per icon.
18
+
19
+ Drop-ins must be rebuilt against this release to get the reduced request footprint. No source changes are required.
20
+
21
+ ### Patch Changes
22
+
23
+ - d2aacc7: Fix: GraphQL fragment source files are no longer incorrectly bundled into `chunks/api.js`. The `manualChunks` function now walks the full importer graph (with cycle protection) to determine whether an api-directory module is owned by the fragments barrel, so fragment files stay in the fragments output chunk even when accessed through intermediate sub-barrels.
24
+ - 5c64620: Implement a new `fragment-import-redirect` build plugin that automatically detects and redirects any dropin source file that directly imports a fragment source file (bypassing the barrel). The import is silently redirected to the fragments barrel at build time and a warning is emitted identifying the file so it can be corrected in source. This ensures fragment constants always appear as local declarations in `fragments.js` regardless of how dropin source code references them.
25
+
3
26
  ## 1.9.0-beta.3
4
27
 
5
28
  ### Patch Changes
@@ -24,10 +47,6 @@
24
47
 
25
48
  ### Minor Changes
26
49
 
27
- - f55a79c: Migrate to Node.js 24 LTS
28
-
29
- Minimum required Node.js version is now 24. Updated `engines.node` from `>=16`/`>=18` to `>=24` across all packages. Regenerated lockfile under Node 24. Updated CI workflows to use `storefront-workflows@v6` with Node 24 support.
30
-
31
50
  - 62adf1c: Reduce HTTP requests on page load through three bundling optimizations. The preact runtime is isolated in its own vendor chunk so it no longer co-locates into other chunks. Dropin API and internal component modules are consolidated into `chunks/api.js` and `chunks/components.js` respectively, replacing the previous pattern of one chunk file per function or component. All SVG icons are consolidated into a single `chunks/icons.js` chunk instead of one chunk per icon.
32
51
 
33
52
  Drop-ins must be rebuilt against this release to get the reduced request footprint. No source changes are required.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe-commerce/elsie",
3
- "version": "1.9.0-beta.3",
3
+ "version": "1.9.1-alpha-20260702101934",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "description": "Domain Package SDK",
6
6
  "engines": {
@@ -28,11 +28,11 @@
28
28
  "postpublish": "node ./scripts/publish-tools.mjs"
29
29
  },
30
30
  "devDependencies": {
31
- "@adobe-commerce/event-bus": "~1.1.0-beta.1",
32
- "@adobe-commerce/fetch-graphql": "~1.3.0-beta.1",
33
- "@adobe-commerce/recaptcha": "1.2.0-beta.1",
34
- "@adobe-commerce/storefront-design": "~1.1.0-beta.1",
35
- "@dropins/build-tools": "~1.2.0-beta.1",
31
+ "@adobe-commerce/event-bus": "~1.1.0",
32
+ "@adobe-commerce/fetch-graphql": "~1.3.0",
33
+ "@adobe-commerce/recaptcha": "1.2.0",
34
+ "@adobe-commerce/storefront-design": "~1.1.0",
35
+ "@dropins/build-tools": "~1.2.0",
36
36
  "preact": "~10.22.1",
37
37
  "vite-plugin-banner": "^0.8.0"
38
38
  },
@@ -11,7 +11,7 @@ import { Icon } from '@adobe-commerce/elsie/components';
11
11
  import { ChevronDown } from '@adobe-commerce/elsie/icons';
12
12
  import { classes } from '@adobe-commerce/elsie/lib';
13
13
  import { FunctionComponent, VNode } from 'preact';
14
- import { HTMLAttributes, useEffect, useState } from 'preact/compat';
14
+ import { HTMLAttributes, useEffect, useRef, useState } from 'preact/compat';
15
15
 
16
16
  import '@adobe-commerce/elsie/components/Picker/Picker.css';
17
17
 
@@ -46,10 +46,13 @@ function findSelectedValue(
46
46
  defaultOption?: PickerOption,
47
47
  placeholder?: string,
48
48
  floatingLabel?: string,
49
- firstAvailableOption?: PickerOption
49
+ firstAvailableOption?: PickerOption,
50
+ singleOption?: PickerOption
50
51
  ) {
51
52
  if (value) return value;
52
53
  if (defaultOption) return defaultOption.value;
54
+ // sole option wins over the placeholder so it stays selected, not blank
55
+ if (singleOption) return singleOption.value;
53
56
  if (placeholder || floatingLabel) return '';
54
57
  if (firstAvailableOption) return firstAvailableOption.value;
55
58
  return null;
@@ -75,17 +78,26 @@ export const Picker: FunctionComponent<PickerProps> = ({
75
78
  const uniqueId = id ?? name ?? `dropin-picker-${Math.random().toString(36)}`;
76
79
  const isRequired = !!props?.required;
77
80
  const isDisabled = disabled || options?.length === 1;
81
+ const selectRef = useRef<HTMLSelectElement>(null);
78
82
 
79
83
  // find the first option that is not disabled
80
84
  const firstAvailableOption = options?.find((option) => !option.disabled);
81
85
 
86
+ // the only selectable option when the picker auto-disables; a disabled
87
+ // <select> never fires change, so we must emit it ourselves (see below)
88
+ const singleOption =
89
+ !disabled && options?.length === 1 && !options[0]?.disabled
90
+ ? options[0]
91
+ : undefined;
92
+
82
93
  const [selectedValue, setSelectedValue] = useState<PickerValue>(() => {
83
94
  return findSelectedValue(
84
95
  value,
85
96
  defaultOption,
86
97
  placeholder,
87
98
  floatingLabel,
88
- firstAvailableOption
99
+ firstAvailableOption,
100
+ singleOption
89
101
  );
90
102
  });
91
103
 
@@ -96,10 +108,32 @@ export const Picker: FunctionComponent<PickerProps> = ({
96
108
  defaultOption,
97
109
  placeholder,
98
110
  floatingLabel,
99
- firstAvailableOption
111
+ firstAvailableOption,
112
+ singleOption
100
113
  )
101
114
  );
102
- }, [value, defaultOption, placeholder, floatingLabel, firstAvailableOption]);
115
+ }, [
116
+ value,
117
+ defaultOption,
118
+ placeholder,
119
+ floatingLabel,
120
+ firstAvailableOption,
121
+ singleOption,
122
+ ]);
123
+
124
+ // commit the sole option to the consumer; skip once value has come back in.
125
+ // dispatch a real change event so handleOptionClick emits it: consumers get
126
+ // a genuine Event (some call preventDefault) identical to a user selection.
127
+ useEffect(() => {
128
+ if (!singleOption || value === singleOption.value) return;
129
+
130
+ const select = selectRef.current;
131
+ if (!select) return;
132
+
133
+ select.value = singleOption.value as string;
134
+ select.dispatchEvent(new Event('change', { bubbles: true }));
135
+ // eslint-disable-next-line react-hooks/exhaustive-deps
136
+ }, [singleOption?.value, value]);
103
137
 
104
138
  const handleOptionClick = (event: Event) => {
105
139
  const { options, value } = event.target as HTMLSelectElement;
@@ -157,6 +191,7 @@ export const Picker: FunctionComponent<PickerProps> = ({
157
191
  )}
158
192
 
159
193
  <select
194
+ ref={selectRef}
160
195
  id={uniqueId}
161
196
  className={classes([
162
197
  'dropin-picker__select',