@salesforce/b2c-dx-mcp 0.4.4 → 0.4.5
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/README.md +82 -370
- package/content/pwav3/components.md +400 -0
- package/content/pwav3/config.md +124 -0
- package/content/pwav3/data-fetching.md +213 -0
- package/content/pwav3/extensibility.md +167 -0
- package/content/pwav3/i18n.md +214 -0
- package/content/pwav3/quick-reference.md +169 -0
- package/content/pwav3/routing.md +107 -0
- package/content/pwav3/state-management.md +193 -0
- package/content/pwav3/styling.md +248 -0
- package/content/pwav3/testing.md +124 -0
- package/content/site-theming/theming-accessibility.md +126 -0
- package/content/site-theming/theming-questions.md +208 -0
- package/content/site-theming/theming-validation.md +174 -0
- package/dist/registry.js +1 -1
- package/dist/services.d.ts +10 -10
- package/dist/services.js +19 -12
- package/dist/tools/cartridges/index.js +1 -6
- package/dist/tools/index.d.ts +1 -4
- package/dist/tools/index.js +1 -4
- package/dist/tools/mrt/index.js +1 -6
- package/dist/tools/pwav3/index.d.ts +12 -3
- package/dist/tools/pwav3/index.js +5 -63
- package/dist/tools/pwav3/pwa-kit-development-guidelines.d.ts +9 -0
- package/dist/tools/pwav3/pwa-kit-development-guidelines.js +151 -0
- package/dist/tools/scapi/index.d.ts +1 -1
- package/dist/tools/scapi/index.js +6 -1
- package/dist/tools/scapi/scapi-custom-api-scaffold.d.ts +60 -0
- package/dist/tools/scapi/scapi-custom-api-scaffold.js +175 -0
- package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.d.ts +24 -0
- package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.js +53 -0
- package/dist/tools/storefrontnext/figma/figma-to-component/index.d.ts +42 -0
- package/dist/tools/storefrontnext/figma/figma-to-component/index.js +325 -0
- package/dist/tools/storefrontnext/figma/generate-component/decision.d.ts +40 -0
- package/dist/tools/storefrontnext/figma/generate-component/decision.js +312 -0
- package/dist/tools/storefrontnext/figma/generate-component/formatter.d.ts +9 -0
- package/dist/tools/storefrontnext/figma/generate-component/formatter.js +92 -0
- package/dist/tools/storefrontnext/figma/generate-component/index.d.ts +114 -0
- package/dist/tools/storefrontnext/figma/generate-component/index.js +98 -0
- package/dist/tools/storefrontnext/figma/map-tokens/css-parser.d.ts +71 -0
- package/dist/tools/storefrontnext/figma/map-tokens/css-parser.js +260 -0
- package/dist/tools/storefrontnext/figma/map-tokens/index.d.ts +61 -0
- package/dist/tools/storefrontnext/figma/map-tokens/index.js +234 -0
- package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.d.ts +65 -0
- package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.js +268 -0
- package/dist/tools/storefrontnext/index.d.ts +17 -0
- package/dist/tools/storefrontnext/index.js +10 -60
- package/dist/tools/storefrontnext/page-designer-decorator/analyzer.js +15 -0
- package/dist/tools/storefrontnext/page-designer-decorator/index.js +3 -3
- package/dist/tools/storefrontnext/{developer-guidelines.js → sfnext-development-guidelines.js} +3 -3
- package/dist/tools/storefrontnext/site-theming/color-contrast.d.ts +92 -0
- package/dist/tools/storefrontnext/site-theming/color-contrast.js +186 -0
- package/dist/tools/storefrontnext/site-theming/color-mapping.d.ts +16 -0
- package/dist/tools/storefrontnext/site-theming/color-mapping.js +131 -0
- package/dist/tools/storefrontnext/site-theming/guidance-merger.d.ts +11 -0
- package/dist/tools/storefrontnext/site-theming/guidance-merger.js +78 -0
- package/dist/tools/storefrontnext/site-theming/index.d.ts +14 -0
- package/dist/tools/storefrontnext/site-theming/index.js +122 -0
- package/dist/tools/storefrontnext/site-theming/response-builder.d.ts +16 -0
- package/dist/tools/storefrontnext/site-theming/response-builder.js +316 -0
- package/dist/tools/storefrontnext/site-theming/theming-store.d.ts +62 -0
- package/dist/tools/storefrontnext/site-theming/theming-store.js +410 -0
- package/dist/tools/storefrontnext/site-theming/types.d.ts +35 -0
- package/dist/tools/storefrontnext/site-theming/types.js +7 -0
- package/oclif.manifest.json +1 -1
- package/package.json +8 -5
- /package/content/{auth.md → sfnext/auth.md} +0 -0
- /package/content/{components.md → sfnext/components.md} +0 -0
- /package/content/{config.md → sfnext/config.md} +0 -0
- /package/content/{data-fetching.md → sfnext/data-fetching.md} +0 -0
- /package/content/{extensions.md → sfnext/extensions.md} +0 -0
- /package/content/{i18n.md → sfnext/i18n.md} +0 -0
- /package/content/{page-designer.md → sfnext/page-designer.md} +0 -0
- /package/content/{performance.md → sfnext/performance.md} +0 -0
- /package/content/{pitfalls.md → sfnext/pitfalls.md} +0 -0
- /package/content/{quick-reference.md → sfnext/quick-reference.md} +0 -0
- /package/content/{state-management.md → sfnext/state-management.md} +0 -0
- /package/content/{styling.md → sfnext/styling.md} +0 -0
- /package/content/{testing.md → sfnext/testing.md} +0 -0
- /package/dist/tools/storefrontnext/{developer-guidelines.d.ts → sfnext-development-guidelines.d.ts} +0 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# PWA Kit Development Quick Reference
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
PWA Kit (Progressive Web App Kit) is Salesforce's solution for building high-performance, React-based e-commerce storefronts for Commerce Cloud. It provides server-side rendering (SSR), React Router integration, and seamless Commerce API integration through the commerce-sdk-react library.
|
|
6
|
+
|
|
7
|
+
The Retail React App template serves as the foundation, demonstrating best practices for component architecture, data fetching, and state management using modern React patterns.
|
|
8
|
+
|
|
9
|
+
## Core Architecture
|
|
10
|
+
|
|
11
|
+
### Hybrid Rendering Model
|
|
12
|
+
- **Server-Side Rendering (SSR)**: Initial page loads render on the server for fast First Contentful Paint and SEO
|
|
13
|
+
- **Client-Side Rendering (CSR)**: After hydration, navigation happens client-side for fluid user interactions
|
|
14
|
+
- **Isomorphic Code**: All application code must work in both Node.js (server) and browser (client) environments
|
|
15
|
+
- **CDN Caching**: Server-rendered pages are cached at the edge for optimal performance
|
|
16
|
+
|
|
17
|
+
### Express.js + React Stack
|
|
18
|
+
- **Express.js**: Handles server-side routing and rendering
|
|
19
|
+
- **React Router**: Manages client-side navigation and route definitions
|
|
20
|
+
- **React 18**: UI library with Hooks, Suspense, and Concurrent Features
|
|
21
|
+
- **Managed Runtime**: Salesforce's hosting platform for PWA Kit applications
|
|
22
|
+
|
|
23
|
+
## Core Principles
|
|
24
|
+
|
|
25
|
+
### Project Understanding
|
|
26
|
+
- Thoroughly analyze requests and the existing project before implementation
|
|
27
|
+
- Examine the current codebase to identify similar solutions and reusable components
|
|
28
|
+
- Promptly clarify ambiguous requirements
|
|
29
|
+
|
|
30
|
+
### Development Workflow
|
|
31
|
+
Follow this workflow for all development tasks:
|
|
32
|
+
|
|
33
|
+
1. **Analyze Requirements** - Clearly define objectives and functionalities
|
|
34
|
+
2. **Review Existing Code** - Identify patterns and reusable components
|
|
35
|
+
3. **Understand Available Tools** - Familiarize with hooks and utilities from commerce-sdk-react and template-retail-react-app
|
|
36
|
+
4. **Plan Implementation** - Design component structure before coding
|
|
37
|
+
5. **Implement Incrementally** - Develop and test in small, manageable steps
|
|
38
|
+
6. **Test Thoroughly** - Ensure comprehensive testing with Jest and React Testing Library
|
|
39
|
+
|
|
40
|
+
## Technical Stack
|
|
41
|
+
|
|
42
|
+
### Core Technologies
|
|
43
|
+
- **React** - UI components and single-page application architecture
|
|
44
|
+
- **Express** - Server-side rendering and backend routing
|
|
45
|
+
- **@salesforce/commerce-sdk-react** - Commerce Cloud API integration with React hooks
|
|
46
|
+
- **PWA Kit** - SSR framework, routing, configuration, and Salesforce integration
|
|
47
|
+
- **Chakra UI V2** - UI component library and theming system
|
|
48
|
+
- **Emotion** - CSS-in-JS styling solution
|
|
49
|
+
- **React Router** - Client-side routing
|
|
50
|
+
- **React Intl** - Internationalization and localization
|
|
51
|
+
- **React Query** - Data fetching, caching, and state synchronization
|
|
52
|
+
- **Webpack** - Module bundling and code splitting
|
|
53
|
+
- **React Testing Library & Jest** - Testing frameworks
|
|
54
|
+
- **react-helmet** - HTML head tag management
|
|
55
|
+
- **framer-motion** - Animation library
|
|
56
|
+
- **ESLint & Prettier** - Code formatting and linting
|
|
57
|
+
|
|
58
|
+
## Critical Non-Negotiable Rules
|
|
59
|
+
|
|
60
|
+
### 1. Isomorphic Code Requirements
|
|
61
|
+
```javascript
|
|
62
|
+
import {useState, useEffect} from 'react';
|
|
63
|
+
|
|
64
|
+
// ✅ CORRECT: Isomorphic code that works in both environments
|
|
65
|
+
export const MyComponent = () => {
|
|
66
|
+
const [mounted, setMounted] = useState(false);
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
// Client-only logic safely in useEffect
|
|
70
|
+
setMounted(true);
|
|
71
|
+
if (window.analytics) {
|
|
72
|
+
window.analytics.track('page_view');
|
|
73
|
+
}
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
return <div>Component</div>;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// ❌ WRONG: Direct window access at module level
|
|
80
|
+
const userAgent = window.navigator.userAgent; // Crashes on server!
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2. No Secrets in Configuration
|
|
84
|
+
```javascript
|
|
85
|
+
// ❌ WRONG: Secrets in config files (serialized to client!)
|
|
86
|
+
export default {
|
|
87
|
+
apiKey: 'sk_live_abc123', // EXPOSED TO BROWSER!
|
|
88
|
+
secretToken: process.env.SECRET // Still serialized!
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// ✅ CORRECT: Use proxy requests for sensitive operations
|
|
92
|
+
// Keep secrets in environment variables on the server only
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 3. Proxy Pattern for API Requests
|
|
96
|
+
Always use the proxy pattern for external APIs to avoid CORS and improve performance:
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
// Proxy configuration in config/default.js (merge into module.exports)
|
|
100
|
+
module.exports = {
|
|
101
|
+
// ...other config
|
|
102
|
+
proxy: {
|
|
103
|
+
'external-api': {
|
|
104
|
+
host: 'https://api.example.com',
|
|
105
|
+
path: '/api'
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Request through proxy
|
|
111
|
+
fetch('/mobify/proxy/external-api/endpoint')
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 4. Component File Naming
|
|
115
|
+
- Use kebab-case for all file names: `product-tile.jsx`, `use-basket.js`
|
|
116
|
+
- Only prefix with underscore for special components: `_app.jsx`, `_app-config.jsx`, `_error.jsx`
|
|
117
|
+
|
|
118
|
+
## Quick Code Patterns
|
|
119
|
+
|
|
120
|
+
**Reference the template for these patterns:**
|
|
121
|
+
- **Fetch Commerce Data:** `app/pages/product-detail/index.jsx` (useProduct, useParams)
|
|
122
|
+
- **Custom Hook:** `app/hooks/use-current-customer.js` (useCustomer, useCustomerId, useCustomerType)
|
|
123
|
+
- **Routes:** `app/routes.jsx` (loadable, configureRoutes)
|
|
124
|
+
- **Chakra UI:** `app/components/product-tile`, `app/components/shared/ui`
|
|
125
|
+
|
|
126
|
+
## Quality Standards
|
|
127
|
+
|
|
128
|
+
- **Code Formatting**: Maintain consistent formatting using Prettier and ESLint
|
|
129
|
+
- **Test Coverage**: Write comprehensive tests for all business logic
|
|
130
|
+
- **Accessibility**: Ensure components are keyboard navigable and screen reader friendly
|
|
131
|
+
- **Mobile-First**: Design responsive layouts that work on all device sizes
|
|
132
|
+
- **Security**: Follow OWASP best practices, never expose secrets
|
|
133
|
+
- **Performance**: Monitor bundle sizes, use code splitting, optimize images
|
|
134
|
+
|
|
135
|
+
## Common Commands
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Start development server
|
|
139
|
+
npm start
|
|
140
|
+
|
|
141
|
+
# Run tests
|
|
142
|
+
npm test
|
|
143
|
+
|
|
144
|
+
# Build for production
|
|
145
|
+
npm run build
|
|
146
|
+
|
|
147
|
+
# Lint code
|
|
148
|
+
npm run lint
|
|
149
|
+
|
|
150
|
+
# Extract and compile translations
|
|
151
|
+
npm run build-translations
|
|
152
|
+
# Or individually: extract-default-translations, compile-translations, compile-translations:pseudo
|
|
153
|
+
|
|
154
|
+
# Run Lighthouse CI performance tests
|
|
155
|
+
npm run test:lighthouse
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Documentation References
|
|
159
|
+
|
|
160
|
+
For detailed information on specific topics, refer to the dedicated sections:
|
|
161
|
+
- **Components**: Component patterns, Chakra UI, special components, React Hooks
|
|
162
|
+
- **Data Fetching**: commerce-sdk-react hooks, custom APIs, React Query patterns
|
|
163
|
+
- **Routing**: Express.js integration, React Router, SSR/CSR navigation
|
|
164
|
+
- **Configuration**: Config files, environment variables, proxy setup, multi-site
|
|
165
|
+
- **State Management**: Context API, useReducer, Redux integration patterns
|
|
166
|
+
- **Extensibility**: Template extension, overrides directory, ccExtensibility configuration
|
|
167
|
+
- **Testing**: Jest, React Testing Library, MSW, test organization
|
|
168
|
+
- **Internationalization**: React Intl, translation workflows, multi-locale support
|
|
169
|
+
- **Styling**: Chakra UI theming, Emotion CSS-in-JS, responsive design
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Routing in PWA Kit
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
PWA Kit uses Express.js (server-side) and React Router (client-side) for routing. Routes are defined in `app/routes.jsx` and support both SSR and client-side navigation. The template uses `loadable` for code-splitting and `configureRoutes` to add site/locale path segments.
|
|
6
|
+
|
|
7
|
+
## Route Definition
|
|
8
|
+
|
|
9
|
+
**Reference:** See `app/routes.jsx` in the template for the full setup (loadable, configureRoutes, dynamic routes, catch-all).
|
|
10
|
+
|
|
11
|
+
To add a route: add to the `routes` array and ensure the default export passes them through `configureRoutes` with `fuzzyPathMatching: true`, then appends the catch-all `{ path: '*', component: PageNotFound }`.
|
|
12
|
+
|
|
13
|
+
## Route Parameters
|
|
14
|
+
|
|
15
|
+
```jsx
|
|
16
|
+
import {useParams} from 'react-router-dom';
|
|
17
|
+
|
|
18
|
+
const ProductDetail = () => {
|
|
19
|
+
const {productId} = useParams();
|
|
20
|
+
|
|
21
|
+
const {data: product} = useProduct({
|
|
22
|
+
parameters: {id: productId}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return <ProductView product={product} />;
|
|
26
|
+
};
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Data Loading with React Query
|
|
30
|
+
|
|
31
|
+
`withReactQuery` is applied at the **AppConfig** level (in `_app-config/index.jsx`), not per route. Route components use commerce-sdk-react hooks (e.g., `useProduct`) which integrate with React Query automatically. No need to wrap individual route components with `withReactQuery`.
|
|
32
|
+
|
|
33
|
+
## Legacy getProps Pattern
|
|
34
|
+
|
|
35
|
+
PWA Kit still supports `getProps` and `shouldGetProps` methods with long-term support. The template prefers commerce-sdk-react hooks for data fetching; use getProps when you need server-side prefetching.
|
|
36
|
+
|
|
37
|
+
```jsx
|
|
38
|
+
const ProductDetail = ({product}) => {
|
|
39
|
+
return <ProductView product={product} />;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// getProps receives {req, res, params, location} plus extraGetPropsArgs (buildUrl, site, locale)
|
|
43
|
+
ProductDetail.getProps = async ({params}) => {
|
|
44
|
+
const {productId} = params;
|
|
45
|
+
// Use Commerce SDK client or fetch via proxy to prefetch product data
|
|
46
|
+
const product = await fetchProduct(productId);
|
|
47
|
+
return {product};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
ProductDetail.shouldGetProps = ({previousParams, params}) => {
|
|
51
|
+
return previousParams?.productId !== params?.productId;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default ProductDetail;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Navigation
|
|
58
|
+
|
|
59
|
+
### Programmatic Navigation
|
|
60
|
+
|
|
61
|
+
```jsx
|
|
62
|
+
import {useHistory} from 'react-router-dom';
|
|
63
|
+
|
|
64
|
+
const ProductTile = ({product}) => {
|
|
65
|
+
const history = useHistory();
|
|
66
|
+
|
|
67
|
+
const navigateToProduct = () => {
|
|
68
|
+
history.push(`/product/${product.id}`);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return <Button onClick={navigateToProduct}>View Details</Button>;
|
|
72
|
+
};
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Link Component
|
|
76
|
+
|
|
77
|
+
```jsx
|
|
78
|
+
import {Link} from 'react-router-dom';
|
|
79
|
+
import {Button} from '@salesforce/retail-react-app/app/components/shared/ui';
|
|
80
|
+
|
|
81
|
+
const ProductTile = ({product}) => {
|
|
82
|
+
return (
|
|
83
|
+
<Button as={Link} to={`/product/${product.id}`}>
|
|
84
|
+
View Details
|
|
85
|
+
</Button>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## SSR vs CSR Navigation
|
|
91
|
+
|
|
92
|
+
### Server-Side Rendering (First Load)
|
|
93
|
+
- Initial page request goes to Express.js
|
|
94
|
+
- Server renders React components to HTML
|
|
95
|
+
- Data fetched on server
|
|
96
|
+
- HTML sent to browser
|
|
97
|
+
- Client-side JavaScript hydrates
|
|
98
|
+
|
|
99
|
+
### Client-Side Rendering (Subsequent)
|
|
100
|
+
- React Router handles routing
|
|
101
|
+
- No full page reload
|
|
102
|
+
- Data fetched via React Query
|
|
103
|
+
- Smooth transitions
|
|
104
|
+
|
|
105
|
+
## Lazy Loading Routes
|
|
106
|
+
|
|
107
|
+
Use `@loadable/component` with a Skeleton fallback. See `app/routes.jsx` for the pattern.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# State Management
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
PWA Kit applications support various state management approaches, from prop passing to global state using React Context API or Redux. The _app-config special component initializes state management systems.
|
|
6
|
+
|
|
7
|
+
## React Context API (Recommended)
|
|
8
|
+
|
|
9
|
+
The React Context API combined with useReducer provides simple, effective state management:
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
12
|
+
import {createContext, useContext, useReducer} from 'react';
|
|
13
|
+
|
|
14
|
+
// Create context
|
|
15
|
+
const CartContext = createContext();
|
|
16
|
+
|
|
17
|
+
// Define reducer
|
|
18
|
+
const cartReducer = (state, action) => {
|
|
19
|
+
switch (action.type) {
|
|
20
|
+
case 'ADD_ITEM':
|
|
21
|
+
return {...state, items: [...state.items, action.payload]};
|
|
22
|
+
case 'REMOVE_ITEM':
|
|
23
|
+
return {...state, items: state.items.filter(item => item.id !== action.payload)};
|
|
24
|
+
case 'CLEAR_CART':
|
|
25
|
+
return {...state, items: []};
|
|
26
|
+
default:
|
|
27
|
+
return state;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Provider component
|
|
32
|
+
export const CartProvider = ({children}) => {
|
|
33
|
+
const [state, dispatch] = useReducer(cartReducer, {
|
|
34
|
+
items: [],
|
|
35
|
+
total: 0
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<CartContext.Provider value={{state, dispatch}}>
|
|
40
|
+
{children}
|
|
41
|
+
</CartContext.Provider>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Custom hook
|
|
46
|
+
export const useCart = () => {
|
|
47
|
+
const context = useContext(CartContext);
|
|
48
|
+
if (!context) {
|
|
49
|
+
throw new Error('useCart must be used within CartProvider');
|
|
50
|
+
}
|
|
51
|
+
return context;
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Using Context in Components
|
|
56
|
+
|
|
57
|
+
```jsx
|
|
58
|
+
import {useCart} from '../contexts/cart-context';
|
|
59
|
+
|
|
60
|
+
const ProductDetail = ({product}) => {
|
|
61
|
+
const {state, dispatch} = useCart();
|
|
62
|
+
|
|
63
|
+
const addToCart = () => {
|
|
64
|
+
dispatch({type: 'ADD_ITEM', payload: product});
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Button onClick={addToCart}>
|
|
69
|
+
Add to Cart ({state.items.length} items)
|
|
70
|
+
</Button>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Integrating Context in _app-config
|
|
76
|
+
|
|
77
|
+
```jsx
|
|
78
|
+
// app/components/_app-config/index.jsx
|
|
79
|
+
import {CartProvider} from '../../contexts/cart-context';
|
|
80
|
+
import {MultiSiteProvider} from '../../contexts/multi-site-context';
|
|
81
|
+
|
|
82
|
+
const AppConfig = ({children}) => {
|
|
83
|
+
return (
|
|
84
|
+
<CommerceApiProvider>
|
|
85
|
+
<ChakraProvider>
|
|
86
|
+
<MultiSiteProvider>
|
|
87
|
+
<CartProvider>
|
|
88
|
+
{children}
|
|
89
|
+
</CartProvider>
|
|
90
|
+
</MultiSiteProvider>
|
|
91
|
+
</ChakraProvider>
|
|
92
|
+
</CommerceApiProvider>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export default AppConfig;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Redux Integration
|
|
100
|
+
|
|
101
|
+
PWA Kit supports Redux through AppConfig special methods:
|
|
102
|
+
|
|
103
|
+
```jsx
|
|
104
|
+
// app/components/_app-config/index.jsx
|
|
105
|
+
import {Provider} from 'react-redux';
|
|
106
|
+
import {createStore} from '../../store';
|
|
107
|
+
|
|
108
|
+
const AppConfig = ({children, locals = {}}) => {
|
|
109
|
+
const store = createStore(locals.initialState);
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<Provider store={store}>
|
|
113
|
+
{children}
|
|
114
|
+
</Provider>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Restore state from server
|
|
119
|
+
AppConfig.restore = (locals = {}) => {
|
|
120
|
+
const store = createStore(locals.initialState);
|
|
121
|
+
return {initialState: store.getState()};
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Serialize state for SSR
|
|
125
|
+
AppConfig.freeze = () => {
|
|
126
|
+
const store = window.__STORE__;
|
|
127
|
+
return {initialState: store.getState()};
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Provide extra arguments to getProps
|
|
131
|
+
AppConfig.extraGetPropsArgs = () => {
|
|
132
|
+
return {store: window.__STORE__};
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export default AppConfig;
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Local Component State
|
|
139
|
+
|
|
140
|
+
For simple, component-specific state:
|
|
141
|
+
|
|
142
|
+
```jsx
|
|
143
|
+
import {useState} from 'react';
|
|
144
|
+
|
|
145
|
+
const ProductImages = ({images}) => {
|
|
146
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<Box>
|
|
150
|
+
<Image src={images[selectedIndex]} />
|
|
151
|
+
<Thumbnails
|
|
152
|
+
images={images}
|
|
153
|
+
onSelect={setSelectedIndex}
|
|
154
|
+
/>
|
|
155
|
+
</Box>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Derived State with useMemo
|
|
161
|
+
|
|
162
|
+
```jsx
|
|
163
|
+
import {useMemo} from 'react';
|
|
164
|
+
|
|
165
|
+
const Cart = ({items}) => {
|
|
166
|
+
const subtotal = useMemo(() => {
|
|
167
|
+
return items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
|
|
168
|
+
}, [items]);
|
|
169
|
+
|
|
170
|
+
const tax = useMemo(() => subtotal * 0.08, [subtotal]);
|
|
171
|
+
const total = useMemo(() => subtotal + tax, [subtotal, tax]);
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<Box>
|
|
175
|
+
<Text>Subtotal: ${subtotal.toFixed(2)}</Text>
|
|
176
|
+
<Text>Tax: ${tax.toFixed(2)}</Text>
|
|
177
|
+
<Text>Total: ${total.toFixed(2)}</Text>
|
|
178
|
+
</Box>
|
|
179
|
+
);
|
|
180
|
+
};
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Best Practices
|
|
184
|
+
|
|
185
|
+
1. Keep state as local as possible
|
|
186
|
+
2. Use Context for global UI state
|
|
187
|
+
3. Use React Query for server state
|
|
188
|
+
4. Avoid prop drilling
|
|
189
|
+
5. Memoize expensive computations
|
|
190
|
+
6. Keep reducer logic pure
|
|
191
|
+
7. Type your state
|
|
192
|
+
8. Normalize complex state
|
|
193
|
+
9. Test reducers independently
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Styling with Chakra UI and Emotion
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
PWA Kit applications use Chakra UI V2 for component styling and theming, with Emotion as the underlying CSS-in-JS engine.
|
|
6
|
+
|
|
7
|
+
## Chakra UI Components
|
|
8
|
+
|
|
9
|
+
Import from the Retail React App shared/ui barrel for consistency with the template:
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
12
|
+
import {
|
|
13
|
+
Box,
|
|
14
|
+
Flex,
|
|
15
|
+
Stack,
|
|
16
|
+
Button,
|
|
17
|
+
Heading,
|
|
18
|
+
Text,
|
|
19
|
+
Image
|
|
20
|
+
} from '@salesforce/retail-react-app/app/components/shared/ui';
|
|
21
|
+
|
|
22
|
+
const ProductCard = ({product}) => {
|
|
23
|
+
return (
|
|
24
|
+
<Box borderWidth="1px" borderRadius="lg" p={4}>
|
|
25
|
+
<Image src={product.image} alt={product.name} />
|
|
26
|
+
<Heading size="md" mt={2}>{product.name}</Heading>
|
|
27
|
+
<Text color="gray.600">${product.price}</Text>
|
|
28
|
+
<Button colorScheme="blue" mt={4} width="full">
|
|
29
|
+
Add to Cart
|
|
30
|
+
</Button>
|
|
31
|
+
</Box>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Style Props
|
|
37
|
+
|
|
38
|
+
```jsx
|
|
39
|
+
<Box
|
|
40
|
+
// Layout
|
|
41
|
+
display="flex"
|
|
42
|
+
flexDirection="column"
|
|
43
|
+
w="100%"
|
|
44
|
+
maxW="container.lg"
|
|
45
|
+
|
|
46
|
+
// Colors
|
|
47
|
+
bg="gray.50"
|
|
48
|
+
color="gray.800"
|
|
49
|
+
|
|
50
|
+
// Spacing
|
|
51
|
+
p={8}
|
|
52
|
+
px={4}
|
|
53
|
+
m={4}
|
|
54
|
+
|
|
55
|
+
// Typography
|
|
56
|
+
fontSize="lg"
|
|
57
|
+
fontWeight="bold"
|
|
58
|
+
|
|
59
|
+
// Borders
|
|
60
|
+
borderWidth="1px"
|
|
61
|
+
borderRadius="md"
|
|
62
|
+
|
|
63
|
+
// Effects
|
|
64
|
+
shadow="lg"
|
|
65
|
+
|
|
66
|
+
// Pseudo selectors
|
|
67
|
+
_hover={{bg: 'gray.100'}}
|
|
68
|
+
/>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Responsive Design
|
|
72
|
+
|
|
73
|
+
```jsx
|
|
74
|
+
<Box
|
|
75
|
+
// Array syntax: [base, sm, md, lg]
|
|
76
|
+
fontSize={['sm', 'md', 'lg', 'xl']}
|
|
77
|
+
|
|
78
|
+
// Object syntax
|
|
79
|
+
display={{base: 'block', md: 'flex'}}
|
|
80
|
+
width={{base: '100%', md: '50%'}}
|
|
81
|
+
>
|
|
82
|
+
Content
|
|
83
|
+
</Box>
|
|
84
|
+
|
|
85
|
+
<SimpleGrid columns={{base: 1, sm: 2, md: 3, lg: 4}} spacing={6}>
|
|
86
|
+
{products.map(product => (
|
|
87
|
+
<ProductCard key={product.id} product={product} />
|
|
88
|
+
))}
|
|
89
|
+
</SimpleGrid>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Theme Customization
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
// app/theme/index.js (or overrides/app/theme/index.js)
|
|
96
|
+
import baseTheme from '@salesforce/retail-react-app/app/theme';
|
|
97
|
+
import {extendTheme} from '@salesforce/retail-react-app/app/components/shared/ui';
|
|
98
|
+
|
|
99
|
+
const theme = extendTheme(baseTheme, {
|
|
100
|
+
colors: {
|
|
101
|
+
brand: {
|
|
102
|
+
50: '#e6f2ff',
|
|
103
|
+
500: '#0073e6',
|
|
104
|
+
900: '#00111a'
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
fonts: {
|
|
108
|
+
heading: 'Montserrat, sans-serif',
|
|
109
|
+
body: 'Open Sans, sans-serif'
|
|
110
|
+
},
|
|
111
|
+
components: {
|
|
112
|
+
Button: {
|
|
113
|
+
baseStyle: {
|
|
114
|
+
fontWeight: 'bold'
|
|
115
|
+
},
|
|
116
|
+
variants: {
|
|
117
|
+
solid: {
|
|
118
|
+
bg: 'brand.500',
|
|
119
|
+
color: 'white',
|
|
120
|
+
_hover: {bg: 'brand.600'}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
export default theme;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Applying Theme
|
|
131
|
+
|
|
132
|
+
```jsx
|
|
133
|
+
// app/components/_app-config/index.jsx
|
|
134
|
+
import {ChakraProvider} from '@salesforce/retail-react-app/app/components/shared/ui';
|
|
135
|
+
import theme from '@salesforce/retail-react-app/app/theme';
|
|
136
|
+
|
|
137
|
+
const AppConfig = ({children}) => {
|
|
138
|
+
return (
|
|
139
|
+
<ChakraProvider theme={theme}>
|
|
140
|
+
{children}
|
|
141
|
+
</ChakraProvider>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Emotion CSS-in-JS
|
|
147
|
+
|
|
148
|
+
```jsx
|
|
149
|
+
import {css} from '@emotion/react';
|
|
150
|
+
|
|
151
|
+
const customStyles = css`
|
|
152
|
+
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
|
153
|
+
border-radius: 8px;
|
|
154
|
+
transition: transform 0.2s;
|
|
155
|
+
|
|
156
|
+
&:hover {
|
|
157
|
+
transform: scale(1.05);
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
const GradientBox = ({children}) => {
|
|
162
|
+
return <Box css={customStyles}>{children}</Box>;
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Layout Components
|
|
167
|
+
|
|
168
|
+
### Flex Layout
|
|
169
|
+
|
|
170
|
+
```jsx
|
|
171
|
+
<Flex direction="row" justify="space-between" align="center" gap={4}>
|
|
172
|
+
<Box>Item 1</Box>
|
|
173
|
+
<Box>Item 2</Box>
|
|
174
|
+
</Flex>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Grid Layout
|
|
178
|
+
|
|
179
|
+
```jsx
|
|
180
|
+
<Grid templateColumns="repeat(auto-fit, minmax(250px, 1fr))" gap={6}>
|
|
181
|
+
{products.map(product => (
|
|
182
|
+
<ProductCard key={product.id} product={product} />
|
|
183
|
+
))}
|
|
184
|
+
</Grid>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Stack Layout
|
|
188
|
+
|
|
189
|
+
```jsx
|
|
190
|
+
<Stack direction="column" spacing={4}>
|
|
191
|
+
<Heading>Title</Heading>
|
|
192
|
+
<Text>Description</Text>
|
|
193
|
+
<Button>Action</Button>
|
|
194
|
+
</Stack>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Pseudo Selectors
|
|
198
|
+
|
|
199
|
+
```jsx
|
|
200
|
+
<Button
|
|
201
|
+
_hover={{
|
|
202
|
+
bg: 'brand.600',
|
|
203
|
+
transform: 'translateY(-2px)'
|
|
204
|
+
}}
|
|
205
|
+
_active={{
|
|
206
|
+
transform: 'translateY(0)'
|
|
207
|
+
}}
|
|
208
|
+
_focus={{
|
|
209
|
+
outline: '2px solid',
|
|
210
|
+
outlineColor: 'brand.500'
|
|
211
|
+
}}
|
|
212
|
+
>
|
|
213
|
+
Click me
|
|
214
|
+
</Button>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Color Mode (Dark Mode)
|
|
218
|
+
|
|
219
|
+
```jsx
|
|
220
|
+
import {useColorMode, useColorModeValue} from '@chakra-ui/react';
|
|
221
|
+
|
|
222
|
+
const ThemedComponent = () => {
|
|
223
|
+
const {colorMode, toggleColorMode} = useColorMode();
|
|
224
|
+
const bg = useColorModeValue('white', 'gray.800');
|
|
225
|
+
const color = useColorModeValue('gray.800', 'white');
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<Box bg={bg} color={color}>
|
|
229
|
+
<Button onClick={toggleColorMode}>
|
|
230
|
+
Toggle {colorMode === 'light' ? 'Dark' : 'Light'}
|
|
231
|
+
</Button>
|
|
232
|
+
</Box>
|
|
233
|
+
);
|
|
234
|
+
};
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Best Practices
|
|
238
|
+
|
|
239
|
+
1. Use Chakra UI components
|
|
240
|
+
2. Leverage style props
|
|
241
|
+
3. Make designs responsive
|
|
242
|
+
4. Follow theme structure
|
|
243
|
+
5. Use semantic HTML with `as` prop
|
|
244
|
+
6. Ensure accessibility
|
|
245
|
+
7. Optimize images
|
|
246
|
+
8. Test in multiple browsers
|
|
247
|
+
9. Use theme tokens
|
|
248
|
+
10. Consider performance
|