diff --git a/packages/venia-concept/package.json b/packages/venia-concept/package.json
index 0512fc5fb6..41a8b7429d 100644
--- a/packages/venia-concept/package.json
+++ b/packages/venia-concept/package.json
@@ -58,9 +58,7 @@
"@magento/pagebuilder": "9.3.4-alpha9",
"@magento/peregrine": "15.5.1-alpha9",
"@magento/pwa-theme-venia": "~2.4.0",
- "@magento/recommendations-js-sdk": "~2.0.7",
- "@magento/upward-security-headers": "~1.0.14",
- "@magento/venia-data-collector": "~1.0.7",
+ "@magento/upward-security-headers": "1.1.18-alpha9",
"@magento/venia-ui": "11.7.0-alpha9",
"@pmmmwh/react-refresh-webpack-plugin": "0.4.1",
"@storybook/react": "~6.3.7",
diff --git a/packages/venia-concept/src/.storybook/config.js b/packages/venia-concept/src/.storybook/config.js
deleted file mode 100644
index 95b22f22d2..0000000000
--- a/packages/venia-concept/src/.storybook/config.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react';
-import { configure, addDecorator } from '@storybook/react';
-import Adapter from '@magento/venia-ui/lib/components/Adapter';
-import store from '../store';
-import '@magento/venia-ui/lib/index.module.css';
-import 'tailwindcss/tailwind.css';
-
-const loadStories = () => {
- // Load all stories from venia-ui
- const veniaContext = require.context(
- '../../node_modules/@magento/venia-ui/lib',
- true,
- /__stories__\/.+\.js$/
- );
- veniaContext.keys().forEach(veniaContext);
-
- // Load all custom defined stories in src
- const customContext = require.context('..', true, /__stories__\/.+\.js$/);
- customContext.keys().forEach(customContext);
-};
-
-const origin = process.env.MAGENTO_BACKEND_URL;
-
-addDecorator(storyFn => (
-
- {storyFn()}
-
-));
-
-configure(loadStories, module);
diff --git a/packages/venia-concept/src/.storybook/main.js b/packages/venia-concept/src/.storybook/main.js
new file mode 100644
index 0000000000..d110c14d4c
--- /dev/null
+++ b/packages/venia-concept/src/.storybook/main.js
@@ -0,0 +1,115 @@
+module.exports = {
+ stories: [
+ '../../node_modules/@magento/venia-ui/lib/**/__stories__/*.js',
+ '../**/__stories__/*.js'
+ ],
+ addons: [],
+
+ // Fix for Manager webpack build (Storybook UI)
+ managerWebpack: async (config, options) => {
+ // Add babel rule for react-draggable in manager build
+ config.module.rules.push({
+ test: /\.js$/,
+ include: /node_modules\/react-draggable/,
+ use: {
+ loader: 'babel-loader',
+ options: {
+ presets: [
+ [
+ '@babel/preset-env',
+ {
+ targets: {
+ browsers: ['last 2 versions', 'ie >= 11']
+ }
+ }
+ ]
+ ]
+ }
+ }
+ });
+
+ return config;
+ },
+
+ // Fix for Preview webpack build (Story iframe)
+ webpackFinal: async (config, { configType }) => {
+ // Import the existing webpack config logic
+ const path = require('path');
+ const {
+ graphQL: {
+ getPossibleTypes,
+ getStoreConfigData,
+ getAvailableStoresConfigData
+ },
+ Utilities: { loadEnvironment }
+ } = require('@magento/pwa-buildpack');
+ const baseWebpackConfig = require('../../webpack.config');
+ const { DefinePlugin, EnvironmentPlugin } = require('webpack');
+ const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
+
+ const projectConfig = await loadEnvironment(
+ path.resolve(__dirname, '../..')
+ );
+
+ if (projectConfig.error) {
+ throw projectConfig.error;
+ }
+
+ const possibleTypes = await getPossibleTypes();
+ const storeConfigData = await getStoreConfigData();
+ const { availableStores } = await getAvailableStoresConfigData();
+ global.LOCALE = storeConfigData.locale.replace('_', '-');
+
+ const [webpackConfig] = await baseWebpackConfig(configType);
+
+ config.module = webpackConfig.module;
+ config.resolve = webpackConfig.resolve;
+
+ // Add babel rule for react-draggable in preview build too
+ config.module.rules.push({
+ test: /\.js$/,
+ include: /node_modules\/react-draggable/,
+ use: {
+ loader: 'babel-loader',
+ options: {
+ presets: [
+ [
+ '@babel/preset-env',
+ {
+ targets: {
+ browsers: ['last 2 versions', 'ie >= 11']
+ }
+ }
+ ]
+ ]
+ }
+ }
+ });
+
+
+ // Make sure to provide any plugins that UI code may depend on.
+ config.plugins = [
+ ...config.plugins,
+ new DefinePlugin({
+ __fetchLocaleData__: async () => {
+ // no-op in storybook
+ },
+ POSSIBLE_TYPES: JSON.stringify(possibleTypes),
+ STORE_NAME: JSON.stringify('Storybook'),
+ STORE_VIEW_LOCALE: JSON.stringify(global.LOCALE),
+ STORE_VIEW_CODE: process.env.STORE_VIEW_CODE
+ ? JSON.stringify(process.env.STORE_VIEW_CODE)
+ : JSON.stringify(storeConfigData.code),
+ AVAILABLE_STORE_VIEWS: JSON.stringify(availableStores),
+ DEFAULT_LOCALE: JSON.stringify(global.LOCALE),
+ DEFAULT_COUNTRY_CODE: JSON.stringify(
+ process.env.DEFAULT_COUNTRY_CODE || 'US'
+ )
+ }),
+ new EnvironmentPlugin(projectConfig.env),
+ new ReactRefreshWebpackPlugin()
+ ];
+
+ return config;
+ }
+};
diff --git a/packages/venia-concept/src/.storybook/preview.js b/packages/venia-concept/src/.storybook/preview.js
new file mode 100644
index 0000000000..7f7a7febef
--- /dev/null
+++ b/packages/venia-concept/src/.storybook/preview.js
@@ -0,0 +1,168 @@
+import React from 'react';
+import { MemoryRouter } from 'react-router-dom';
+import Adapter from '@magento/venia-ui/lib/components/Adapter';
+import { Form } from 'informed';
+import store from '../store';
+import '@magento/venia-ui/lib/index.module.css';
+import 'tailwindcss/tailwind.css';
+
+// Mock browser APIs that components might expect
+if (typeof window !== 'undefined') {
+ // Ensure URLSearchParams is available with comprehensive methods
+ if (!window.URLSearchParams) {
+ window.URLSearchParams = class URLSearchParams {
+ constructor(search = '') {
+ this.params = new Map();
+ if (typeof search === 'string') {
+ if (search.startsWith('?')) search = search.slice(1);
+ if (search) {
+ search.split('&').forEach(param => {
+ const [key, value] = param.split('=');
+ if (key)
+ this.params.set(
+ decodeURIComponent(key),
+ decodeURIComponent(value || '')
+ );
+ });
+ }
+ }
+ }
+ get(key) {
+ return this.params.get(key);
+ }
+ set(key, value) {
+ this.params.set(key, value);
+ }
+ delete(key) {
+ this.params.delete(key);
+ }
+ has(key) {
+ return this.params.has(key);
+ }
+ append(key, value) {
+ this.params.set(key, value);
+ }
+ toString() {
+ const entries = Array.from(this.params.entries());
+ return entries
+ .map(
+ ([k, v]) =>
+ `${encodeURIComponent(k)}=${encodeURIComponent(v)}`
+ )
+ .join('&');
+ }
+ forEach(callback) {
+ this.params.forEach(callback);
+ }
+ keys() {
+ return this.params.keys();
+ }
+ values() {
+ return this.params.values();
+ }
+ entries() {
+ return this.params.entries();
+ }
+ };
+ }
+
+ // Create comprehensive URL object mock
+ if (!window.URL) {
+ window.URL = class URL {
+ constructor(url, base) {
+ this.href = url || 'http://localhost:9001/?page=1';
+ this.origin = 'http://localhost:9001';
+ this.protocol = 'http:';
+ this.host = 'localhost:9001';
+ this.hostname = 'localhost';
+ this.port = '9001';
+ this.pathname = '/';
+ this.search = '?page=1';
+ this.hash = '';
+ }
+ toString() {
+ return this.href;
+ }
+ };
+ }
+
+ // Mock location.search and other properties that might be undefined
+ try {
+ if (!window.location.search || window.location.search === '') {
+ Object.defineProperty(window.location, 'search', {
+ value: '?page=1',
+ writable: true,
+ configurable: true
+ });
+ }
+ if (!window.location.pathname || window.location.pathname === '') {
+ Object.defineProperty(window.location, 'pathname', {
+ value: '/',
+ writable: true,
+ configurable: true
+ });
+ }
+ } catch (e) {
+ // Some browsers don't allow modifying location
+ console.warn('Could not mock location properties:', e);
+ }
+
+ // Global string replacement protection
+ const originalStringReplace = String.prototype.replace;
+ String.prototype.replace = function(...args) {
+ if (this == null || this === undefined) {
+ console.warn('Attempted to call replace on undefined/null value');
+ return '';
+ }
+ return originalStringReplace.apply(this, args);
+ };
+}
+
+const origin =
+ process.env.MAGENTO_BACKEND_URL ||
+ 'https://master-7rqtwti-c5v7sxvquxwl4.eu-4.magentosite.cloud/';
+
+// Form wrapper for components that need form context
+const FormWrapper = ({ children }) => {
+ return
;
+};
+
+// Router wrapper for components that need routing context
+const RouterWrapper = ({ children }) => {
+ // Provide initial location with comprehensive data that components might expect
+ const initialEntries = [
+ {
+ pathname: '/',
+ search: '?page=1',
+ hash: '',
+ state: null,
+ key: 'default'
+ }
+ ];
+
+ return (
+ {children}
+ );
+};
+
+export const decorators = [
+ Story => (
+
+
+
+
+
+
+
+ )
+];
+
+export const parameters = {
+ actions: { argTypesRegex: '^on[A-Z].*' },
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/
+ }
+ }
+};
diff --git a/packages/venia-concept/src/.storybook/webpack.config.js b/packages/venia-concept/src/.storybook/webpack.config.js
deleted file mode 100644
index 0981879c70..0000000000
--- a/packages/venia-concept/src/.storybook/webpack.config.js
+++ /dev/null
@@ -1,61 +0,0 @@
-const path = require('path');
-const {
- graphQL: {
- getPossibleTypes,
- getStoreConfigData,
- getAvailableStoresConfigData
- },
- Utilities: { loadEnvironment }
-} = require('@magento/pwa-buildpack');
-const baseWebpackConfig = require('../../webpack.config');
-const { DefinePlugin, EnvironmentPlugin } = require('webpack');
-const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
-
-// Storybook 5.2.8 uses a different signature for webpack config than webpack
-// defines in the docs.
-// See https://storybook.js.org/docs/configurations/custom-webpack-config/#full-control-mode
-module.exports = async ({ config: storybookBaseConfig, mode }) => {
- const projectConfig = await loadEnvironment(
- // Load .env from root
- path.resolve(__dirname, '../..')
- );
-
- if (projectConfig.error) {
- throw projectConfig.error;
- }
-
- const possibleTypes = await getPossibleTypes();
- const storeConfigData = await getStoreConfigData();
- const { availableStores } = await getAvailableStoresConfigData();
- global.LOCALE = storeConfigData.locale.replace('_', '-');
-
- const [webpackConfig] = await baseWebpackConfig(mode);
-
- storybookBaseConfig.module = webpackConfig.module;
- storybookBaseConfig.resolve = webpackConfig.resolve;
-
- // Make sure to provide any plugins that UI code may depend on.
- storybookBaseConfig.plugins = [
- ...storybookBaseConfig.plugins,
- new DefinePlugin({
- __fetchLocaleData__: async () => {
- // no-op in storybook
- },
- POSSIBLE_TYPES: JSON.stringify(possibleTypes),
- STORE_NAME: JSON.stringify('Storybook'),
- STORE_VIEW_LOCALE: JSON.stringify(global.LOCALE),
- STORE_VIEW_CODE: process.env.STORE_VIEW_CODE
- ? JSON.stringify(process.env.STORE_VIEW_CODE)
- : JSON.stringify(storeConfigData.code),
- AVAILABLE_STORE_VIEWS: JSON.stringify(availableStores),
- DEFAULT_LOCALE: JSON.stringify(global.LOCALE),
- DEFAULT_COUNTRY_CODE: JSON.stringify(
- process.env.DEFAULT_COUNTRY_CODE || 'US'
- )
- }),
- new EnvironmentPlugin(projectConfig.env),
- new ReactRefreshWebpackPlugin()
- ];
-
- return storybookBaseConfig;
-};