@pixelated-tech/components 3.2.11 → 3.2.13
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.COMPONENTS.md +5 -3
- package/README.md +111 -94
- package/dist/components/cms/cloudinary.image.js +4 -4
- package/dist/components/cms/wordpress.components.js +1 -1
- package/dist/components/cms/wordpress.css +7 -0
- package/dist/components/cms/wordpress.functions.js +30 -1
- package/dist/components/menu/menu-expando.css +16 -11
- package/dist/components/menu/menu-expando.js +2 -2
- package/dist/components/pagebuilder/components/ComponentPropertiesForm.js +1 -1
- package/dist/components/pagebuilder/form/form.js +2 -1
- package/dist/types/components/cms/wordpress.functions.d.ts +6 -0
- package/dist/types/components/cms/wordpress.functions.d.ts.map +1 -1
- package/dist/types/components/pagebuilder/components/ComponentPropertiesForm.d.ts +1 -1
- package/dist/types/components/pagebuilder/form/form.d.ts.map +1 -1
- package/dist/types/tests/component-properties-form.test.d.ts +2 -0
- package/dist/types/tests/component-properties-form.test.d.ts.map +1 -0
- package/dist/types/tests/component-selector.test.d.ts +2 -0
- package/dist/types/tests/component-selector.test.d.ts.map +1 -0
- package/dist/types/tests/component-tree.test.d.ts +2 -0
- package/dist/types/tests/component-tree.test.d.ts.map +1 -0
- package/dist/types/tests/page-builder-ui.test.d.ts +2 -0
- package/dist/types/tests/page-builder-ui.test.d.ts.map +1 -0
- package/dist/types/tests/page-engine.test.d.ts +2 -0
- package/dist/types/tests/page-engine.test.d.ts.map +1 -0
- package/dist/types/tests/save-load-section.test.d.ts +2 -0
- package/dist/types/tests/save-load-section.test.d.ts.map +1 -0
- package/dist/types/tests/wordpress.functions.test.d.ts +2 -0
- package/dist/types/tests/wordpress.functions.test.d.ts.map +1 -0
- package/package.json +5 -5
package/README.COMPONENTS.md
CHANGED
|
@@ -315,9 +315,11 @@ import { SmartImage } from '@pixelated-tech/components';
|
|
|
315
315
|
|
|
316
316
|
### WordPress Components
|
|
317
317
|
|
|
318
|
+
WordPress integration components with automatic Photon CDN URL processing for optimized image delivery.
|
|
319
|
+
|
|
318
320
|
#### BlogPostList
|
|
319
321
|
|
|
320
|
-
Displays a list of WordPress blog posts with pagination support.
|
|
322
|
+
Displays a list of WordPress blog posts with pagination support. Automatically converts WordPress Photon CDN URLs to direct image URLs for better Next.js optimization.
|
|
321
323
|
|
|
322
324
|
```tsx
|
|
323
325
|
import { BlogPostList } from '@pixelated-tech/components';
|
|
@@ -332,7 +334,7 @@ import { BlogPostList } from '@pixelated-tech/components';
|
|
|
332
334
|
|
|
333
335
|
| Prop | Type | Default | Description |
|
|
334
336
|
|------|------|---------|-------------|
|
|
335
|
-
| `site` | `string` | - | WordPress site identifier (e.g., 'your-blog.wordpress.com') |
|
|
337
|
+
| `site` | `string` | - | WordPress site identifier (e.g., 'blog.pixelated.tech' or 'your-blog.wordpress.com') |
|
|
336
338
|
| `count` | `number` | - | Number of posts to fetch (undefined = all) |
|
|
337
339
|
| `posts` | `BlogPostType[]` | - | Pre-fetched posts array |
|
|
338
340
|
| `showCategories` | `boolean` | `true` | Whether to display post categories |
|
|
@@ -1427,4 +1429,4 @@ When adding new components, please:
|
|
|
1427
1429
|
|
|
1428
1430
|
---
|
|
1429
1431
|
|
|
1430
|
-
*This documentation is automatically updated when components are modified. Last updated:
|
|
1432
|
+
*This documentation is automatically updated when components are modified. Last updated: December 17, 2025*
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<h3 align="center">Pixelated Components</h3>
|
|
19
19
|
|
|
20
20
|
<p align="center">
|
|
21
|
-
|
|
21
|
+
A comprehensive React component library for modern web development, featuring CMS integrations, UI components, and SEO optimization tools.
|
|
22
22
|
<br />
|
|
23
23
|
<a href="https://github.com/brianwhaley/pixelated-components"><strong>Explore the docs »</strong></a>
|
|
24
24
|
<br />
|
|
@@ -107,7 +107,7 @@ Reusable UI components for common patterns:
|
|
|
107
107
|
|
|
108
108
|
### CMS Integration
|
|
109
109
|
Headless CMS and content management components:
|
|
110
|
-
- **WordPress** - Blog post integration and display
|
|
110
|
+
- **WordPress** - Blog post integration and display with automatic Photon CDN URL processing
|
|
111
111
|
- **Contentful** - Headless CMS components and utilities
|
|
112
112
|
- **PageBuilder** - Dynamic page construction from JSON
|
|
113
113
|
- **PageEngine** - Advanced page rendering with Contentful integration
|
|
@@ -168,100 +168,11 @@ npm run storybook
|
|
|
168
168
|
**Access locally at:** `http://localhost:6006`
|
|
169
169
|
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
## 🧪 Testing
|
|
173
|
-
|
|
174
|
-
### Overview
|
|
175
|
-
|
|
176
|
-
**Current Status**: ✅ 2,037 tests passing across 59 test files (all tests passing)
|
|
177
|
-
|
|
178
|
-
| Metric | Value |
|
|
179
|
-
|--------|-------|
|
|
180
|
-
| Test Files | 58 |
|
|
181
|
-
| Total Tests | 2,038 |
|
|
182
|
-
| Components Tested | 52/52 (100%) |
|
|
183
|
-
| Utility Modules Tested | 2/2 (100%) |
|
|
184
|
-
| Coverage (Statements) | 66.81% |
|
|
185
|
-
| Coverage (Lines) | 70.31% |
|
|
186
|
-
| Coverage (Functions) | 74.59% |
|
|
187
|
-
| Coverage (Branches) | 57.62% |
|
|
188
|
-
| Test Framework | Vitest 4.x |
|
|
189
|
-
| Testing Library | @testing-library/react + jsdom |
|
|
190
|
-
|
|
191
|
-
### Quick Start
|
|
192
|
-
|
|
193
|
-
```bash
|
|
194
|
-
npm run test # Watch mode
|
|
195
|
-
npm run test:ui # Interactive UI dashboard
|
|
196
|
-
npm run test:coverage # Generate coverage reports
|
|
197
|
-
npm run test:run # Single run (for CI)
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### Component Coverage
|
|
201
|
-
|
|
202
|
-
**50 of 50 Frontend Components + 1 Utility Module Fully Tested (100%)**
|
|
203
|
-
|
|
204
|
-
#### Component Coverage (Sorted by Statement Coverage)
|
|
205
|
-
- **sitemap.ts**: 100% statements
|
|
206
|
-
- **google.reviews.functions.ts**: 100% statements
|
|
207
|
-
- **googlesearch.tsx**: 100% statements
|
|
208
|
-
- **formvalidations.tsx**: 100% statements (↑ 92.69 points)
|
|
209
|
-
- **tiles.tsx**: 100% statements
|
|
210
|
-
- **markdown.tsx**: 100% statements
|
|
211
|
-
- **buzzwordbingo.tsx**: 100% statements
|
|
212
|
-
- **timeline.tsx**: 100% statements
|
|
213
|
-
- **config.server.tsx**: 100% statements
|
|
214
|
-
- **modal.tsx**: 100% statements
|
|
215
|
-
- **google.reviews.components.tsx**: 100% statements
|
|
216
|
-
- **recipe.tsx**: 98.8% statements
|
|
217
|
-
- **sidepanel.tsx**: 97.5% statements
|
|
218
|
-
- **resume.tsx**: 94.38% statements
|
|
219
|
-
- **callout.tsx**: 93.75% statements
|
|
220
|
-
- **contentful.delivery.ts**: 92.5% statements (↑ 45 points)
|
|
221
|
-
- **css.tsx**: 91.42% statements
|
|
222
|
-
- **functions.ts**: 90.9% statements
|
|
223
|
-
- **config.client.tsx**: 90% statements
|
|
224
|
-
- **api.ts**: 87.5% statements
|
|
225
|
-
- **loading.tsx**: 85.71% statements
|
|
226
|
-
- **table.tsx**: 84.48% statements (↑ 60.35 points)
|
|
227
|
-
- **cloudinary.ts**: 83.33% statements (↑ 58.33 points)
|
|
228
|
-
- **shoppingcart.functions.ts**: 81.69% statements
|
|
229
|
-
- **nerdjoke.tsx**: 70.58% statements
|
|
230
|
-
- **menu-accordion.tsx**: 68.13% statements
|
|
231
|
-
- **carousel.tsx**: 58.49% statements
|
|
232
|
-
- **config.ts**: 55.17% statements
|
|
233
|
-
|
|
234
|
-
### Test Configuration
|
|
235
|
-
|
|
236
|
-
**Coverage Targets** (Updated - Focus on Statement Coverage):
|
|
237
|
-
- **Statements**: 66.81% ✅ ACHIEVED (Target: 70%)
|
|
238
|
-
- **Lines**: 70.31% ✅ ACHIEVED
|
|
239
|
-
- **Functions**: 74.59% ✅ ACHIEVED
|
|
240
|
-
- **Branches**: 57.62% (Focus area for future)
|
|
241
|
-
|
|
242
|
-
**Coverage Thresholds in vitest.config.ts**:
|
|
243
|
-
- Lines: 70% threshold
|
|
244
|
-
- Functions: 70% threshold
|
|
245
|
-
- Branches: 60% threshold
|
|
246
|
-
- Statements: 70% threshold
|
|
247
|
-
|
|
248
|
-
**Test Environment**: jsdom with @testing-library/react
|
|
249
|
-
**Test Pattern**: Data-focused validation + behavioral testing
|
|
250
|
-
|
|
251
|
-
### Tools & Dependencies
|
|
252
|
-
|
|
253
|
-
| Tool | Purpose |
|
|
254
|
-
|------|---------|
|
|
255
|
-
| Vitest 4.x | Test runner |
|
|
256
|
-
| @testing-library/react | Component testing utilities |
|
|
257
|
-
| jsdom | DOM environment for tests |
|
|
258
|
-
| v8 | Coverage reporting |
|
|
259
|
-
|
|
260
|
-
|
|
261
171
|
<!-- ROADMAP -->
|
|
262
172
|
## Roadmap
|
|
263
173
|
|
|
264
174
|
### New Components
|
|
175
|
+
- [ ] **IN PROGRESS** - Testimonial Block (Nextdoor/Yelp/Google): ingest review feeds + render carousel/grid.
|
|
265
176
|
- [ ] **ON HOLD** LinkedIn Recommendations Integration (Not possible with current LinkedIn API)
|
|
266
177
|
- [ ] **ON HOLD** eBay Feedback Integration - requires user OAuth login
|
|
267
178
|
- [ ] **ON HOLD** Yelp Recommendations integration (Cost Prohibitive)
|
|
@@ -271,7 +182,6 @@ npm run test:run # Single run (for CI)
|
|
|
271
182
|
- [ ] Buffer Integration (or Sendible, Sprout Social, Hootsuite)
|
|
272
183
|
- [ ] Zapier Integration
|
|
273
184
|
- [ ] Hero Banner: headline, subtext, CTA, background image/video, overlay.
|
|
274
|
-
- [ ] **IN PROGRESS** - Testimonial Block (Nextdoor/Yelp/Google): ingest review feeds + render carousel/grid.
|
|
275
185
|
|
|
276
186
|
### CI / CD Improvements
|
|
277
187
|
- [ ] Add CI workflow to run tests and lints on pull requests.
|
|
@@ -338,7 +248,7 @@ Distributed under the MIT License. See `LICENSE.txt` for more information.
|
|
|
338
248
|
<!-- CONTACT -->
|
|
339
249
|
## Contact
|
|
340
250
|
|
|
341
|
-
|
|
251
|
+
Brian Whaley - [@brianwhaley](https://twitter.com/@brianwhaley) - brian.whaley@gmail.com
|
|
342
252
|
|
|
343
253
|
Project Link: [https://github.com/brianwhaley/pixelated-components](https://github.com/brianwhaley/pixelated-components)
|
|
344
254
|
|
|
@@ -347,6 +257,113 @@ Project Link: [https://github.com/brianwhaley/pixelated-components](https://gith
|
|
|
347
257
|
|
|
348
258
|
|
|
349
259
|
|
|
260
|
+
## 🧪 Testing
|
|
261
|
+
|
|
262
|
+
### Overview
|
|
263
|
+
|
|
264
|
+
**Current Status**: ✅ 2,184 tests passing across 59 test files
|
|
265
|
+
|
|
266
|
+
| Metric | Value |
|
|
267
|
+
|--------|-------|
|
|
268
|
+
| Test Files | 59 |
|
|
269
|
+
| Total Tests | 2,184 |
|
|
270
|
+
| Coverage (Statements) | 79.27% |
|
|
271
|
+
| Coverage (Lines) | 82.74% |
|
|
272
|
+
| Coverage (Functions) | 84.74% |
|
|
273
|
+
| Coverage (Branches) | 67.19% |
|
|
274
|
+
| Test Framework | Vitest 4.x |
|
|
275
|
+
| Testing Library | @testing-library/react + jsdom |
|
|
276
|
+
|
|
277
|
+
### Quick Start
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
npm run test # Watch mode
|
|
281
|
+
npm run test:ui # Interactive UI dashboard
|
|
282
|
+
npm run test:coverage # Generate coverage reports
|
|
283
|
+
npm run test:run # Single run (for CI)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Component Coverage
|
|
287
|
+
|
|
288
|
+
**Component Coverage Summary**
|
|
289
|
+
|
|
290
|
+
#### Component Coverage (Sorted by Statement Coverage)
|
|
291
|
+
- **google.reviews.functions.ts**: 100% statements
|
|
292
|
+
- **googlesearch.tsx**: 100% statements
|
|
293
|
+
- **formvalidations.tsx**: 100% statements
|
|
294
|
+
- **tiles.tsx**: 100% statements
|
|
295
|
+
- **markdown.tsx**: 100% statements
|
|
296
|
+
- **buzzwordbingo.tsx**: 100% statements
|
|
297
|
+
- **timeline.tsx**: 100% statements
|
|
298
|
+
- **config.server.tsx**: 100% statements
|
|
299
|
+
- **modal.tsx**: 100% statements
|
|
300
|
+
- **recipe.tsx**: 98.8% statements
|
|
301
|
+
- **sidepanel.tsx**: 97.5% statements
|
|
302
|
+
- **google.reviews.components.tsx**: 95.83% statements
|
|
303
|
+
- **resume.tsx**: 94.38% statements
|
|
304
|
+
- **contentful.delivery.ts**: 92.5% statements
|
|
305
|
+
- **css.tsx**: 91.42% statements
|
|
306
|
+
- **functions.ts**: 90.9% statements
|
|
307
|
+
- **config.client.tsx**: 90% statements
|
|
308
|
+
- **api.ts**: 87.5% statements
|
|
309
|
+
- **loading.tsx**: 85.71% statements
|
|
310
|
+
- **table.tsx**: 84.48% statements
|
|
311
|
+
- **cloudinary.ts**: 83.33% statements
|
|
312
|
+
- **shoppingcart.functions.ts**: 81.69% statements
|
|
313
|
+
- **callout.tsx**: 80.00% statements
|
|
314
|
+
- **sitemap.ts**: 76.38% statements
|
|
315
|
+
- **carousel.tsx**: 71.70% statements
|
|
316
|
+
- **nerdjoke.tsx**: 70.58% statements
|
|
317
|
+
- **menu-accordion.tsx**: 68.13% statements
|
|
318
|
+
- **semantic.tsx**: 60.81% statements
|
|
319
|
+
- **config.ts**: 55.17% statements
|
|
320
|
+
- **socialcard.tsx**: 29.5% statements
|
|
321
|
+
- **ComponentPropertiesForm.tsx**: 0% statements (no tests)
|
|
322
|
+
- **ComponentSelector.tsx**: 0% statements (no tests)
|
|
323
|
+
- **ComponentTree.tsx**: 0% statements (no tests)
|
|
324
|
+
- **PageBuilderUI.tsx**: 0% statements (no tests)
|
|
325
|
+
- **PageEngine.tsx**: 0% statements (no tests)
|
|
326
|
+
- **SaveLoadSection.tsx**: 0% statements (no tests)
|
|
327
|
+
|
|
328
|
+
### Testing Next Steps
|
|
329
|
+
|
|
330
|
+
#### Integration Testing Gaps
|
|
331
|
+
- [ ] **Cross-component interactions** - Test how components work together (e.g., forms with validation, carousels with loading states)
|
|
332
|
+
- [ ] **Form validation edge cases** - Test URL validation, required fields, and complex validation rules under various conditions
|
|
333
|
+
- [ ] **CMS API integrations** - Test API failures, rate limiting, authentication errors, and network timeouts
|
|
334
|
+
- [ ] **Responsive design breakpoints** - Test component behavior across different screen sizes and device types
|
|
335
|
+
- [ ] **Accessibility (a11y) compliance** - Test keyboard navigation, screen reader compatibility, and ARIA attributes
|
|
336
|
+
|
|
337
|
+
### Test Configuration
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
**Coverage Targets** (configured in `vitest.config.ts`):
|
|
341
|
+
- **Statements**: 70% threshold
|
|
342
|
+
- **Lines**: 70% threshold
|
|
343
|
+
- **Functions**: 70% threshold
|
|
344
|
+
- **Branches**: 60% threshold
|
|
345
|
+
|
|
346
|
+
**Coverage Thresholds in vitest.config.ts**:
|
|
347
|
+
- Lines: 70% threshold
|
|
348
|
+
- Functions: 70% threshold
|
|
349
|
+
- Branches: 60% threshold
|
|
350
|
+
- Statements: 70% threshold
|
|
351
|
+
|
|
352
|
+
**Test Environment**: jsdom with @testing-library/react
|
|
353
|
+
**Test Pattern**: Data-focused validation + behavioral testing
|
|
354
|
+
|
|
355
|
+
### Tools & Dependencies
|
|
356
|
+
|
|
357
|
+
| Tool | Purpose |
|
|
358
|
+
|------|---------|
|
|
359
|
+
| Vitest 4.x | Test runner |
|
|
360
|
+
| @testing-library/react | Component testing utilities |
|
|
361
|
+
| jsdom | DOM environment for tests |
|
|
362
|
+
| v8 | Coverage reporting |
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
350
367
|
|
|
351
368
|
<!-- MARKDOWN LINKS & IMAGES -->
|
|
352
369
|
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
|
|
@@ -88,20 +88,20 @@ export function SmartImage(props) {
|
|
|
88
88
|
// if(Array.isArray(src))
|
|
89
89
|
const finalSrc = newProps.cloudinaryEnv
|
|
90
90
|
? buildCloudinaryUrl({
|
|
91
|
-
src: src,
|
|
91
|
+
src: newProps.src,
|
|
92
92
|
productEnv: newProps.cloudinaryEnv,
|
|
93
93
|
cloudinaryDomain: newProps.cloudinaryDomain,
|
|
94
94
|
quality,
|
|
95
95
|
width: newProps.width ?? undefined,
|
|
96
96
|
transforms: newProps.cloudinaryTransforms ?? undefined
|
|
97
97
|
})
|
|
98
|
-
: String(src);
|
|
98
|
+
: String(newProps.src);
|
|
99
99
|
let responsiveSrcSet;
|
|
100
100
|
let responsiveSizes;
|
|
101
101
|
if (newProps.cloudinaryEnv) {
|
|
102
102
|
if (newProps.width) {
|
|
103
103
|
const widths = [Math.ceil(newProps.width * 0.5), newProps.width, Math.ceil(newProps.width * 1.5), Math.ceil(newProps.width * 2)];
|
|
104
|
-
responsiveSrcSet = generateSrcSet(String(src), newProps.cloudinaryEnv, widths, {
|
|
104
|
+
responsiveSrcSet = generateSrcSet(String(newProps.src), newProps.cloudinaryEnv, widths, {
|
|
105
105
|
quality,
|
|
106
106
|
transforms: newProps.cloudinaryTransforms ?? undefined,
|
|
107
107
|
cloudinaryDomain: newProps.cloudinaryDomain
|
|
@@ -110,7 +110,7 @@ export function SmartImage(props) {
|
|
|
110
110
|
}
|
|
111
111
|
else {
|
|
112
112
|
const breakpoints = [320, 640, 768, 1024, 1280, 1536];
|
|
113
|
-
responsiveSrcSet = generateSrcSet(String(src), newProps.cloudinaryEnv, breakpoints, {
|
|
113
|
+
responsiveSrcSet = generateSrcSet(String(newProps.src), newProps.cloudinaryEnv, breakpoints, {
|
|
114
114
|
quality,
|
|
115
115
|
transforms: newProps.cloudinaryTransforms ?? undefined,
|
|
116
116
|
cloudinaryDomain: newProps.cloudinaryDomain
|
|
@@ -45,7 +45,7 @@ export function BlogPostSummary(props) {
|
|
|
45
45
|
const myCategoryImages = Object.entries(props.categories).map(([category, index]) => [category.trim().toLowerCase().replace(/[ /]+/g, '-'), index]).sort();
|
|
46
46
|
const config = usePixelatedConfig();
|
|
47
47
|
const myExcerpt = decodeString(props.excerpt).replace(/\[…\]/g, '<a href="' + props.URL + '" target="_blank" rel="noopener noreferrer">[…]</a>');
|
|
48
|
-
return (_jsx("div", { className: "blog-post-summary", children: _jsxs("article", { className: "h-entry", children: [_jsx("h2", { className: "p-name", children: _jsx("a", { className: "u-url blog-post-url", href: props.URL, target: "_blank", rel: "noopener noreferrer", children: decodeString(props.title) }) }), _jsxs("div", { className: "dt-published", children: ["Published: ", new Date(props.date).toLocaleDateString()] }), props.featured_image ? (_jsxs("div", { className: "article-body row-12col", children: [_jsx("div", { className: "article-featured-image grid-s1-e4", children: _jsx(SmartImage, { className: "u-photo", src: props.featured_image, alt: decodeString(props.title), title: decodeString(props.title), style: {
|
|
48
|
+
return (_jsx("div", { className: "blog-post-summary", children: _jsxs("article", { className: "h-entry", children: [_jsx("h2", { className: "p-name", children: _jsx("a", { className: "u-url blog-post-url", href: props.URL, target: "_blank", rel: "noopener noreferrer", children: decodeString(props.title) }) }), _jsxs("div", { className: "dt-published", children: ["Published: ", new Date(props.date).toLocaleDateString()] }), props.featured_image ? (_jsxs("div", { className: "article-body row-12col", children: [_jsx("div", { className: "article-featured-image grid-s1-e4", children: _jsx(SmartImage, { className: "u-photo", src: props.featured_image, alt: decodeString(props.title), title: decodeString(props.title), style: {}, cloudinaryEnv: config?.cloudinary?.product_env ?? undefined, cloudinaryDomain: config?.cloudinary?.baseUrl ?? undefined, cloudinaryTransforms: config?.cloudinary?.transforms ?? undefined }) }), _jsx("div", { className: "article-excerpt grid-s4-e13", children: _jsx("div", { className: "p-summary", dangerouslySetInnerHTML: { __html: myExcerpt } }) })] })) :
|
|
49
49
|
_jsx("div", { className: "article-excerpt grid-s1-e13", children: _jsx("div", { className: "p-summary", dangerouslySetInnerHTML: { __html: myExcerpt } }) }), props.showCategories !== false && (_jsxs("div", { children: ["Categories:", myCategoryImages.map(([categoryImg, index]) => (_jsx("span", { className: "p-category", children: _jsx(SmartImage, { src: `/images/icons/${categoryImg}.png`, title: String(categoryImg), alt: String(categoryImg), cloudinaryEnv: config?.cloudinary?.product_env ?? undefined, cloudinaryDomain: config?.cloudinary?.baseUrl ?? undefined, cloudinaryTransforms: config?.cloudinary?.transforms ?? undefined }) }, categoryImg + "-" + index)))] }))] }) }, props.ID));
|
|
50
50
|
}
|
|
51
51
|
export function BlogPostCategories(props) {
|
|
@@ -25,7 +25,12 @@ export async function getWordPressItems(props) {
|
|
|
25
25
|
if (batch.length === 0) {
|
|
26
26
|
break; // no more posts
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
// Process Photon URLs in featured images
|
|
29
|
+
const processedBatch = batch.map(post => ({
|
|
30
|
+
...post,
|
|
31
|
+
featured_image: post.featured_image ? photonToOriginalUrl(post.featured_image) : post.featured_image
|
|
32
|
+
}));
|
|
33
|
+
posts.push(...processedBatch);
|
|
29
34
|
if (requested && posts.length >= requested) {
|
|
30
35
|
break; // collected enough
|
|
31
36
|
}
|
|
@@ -103,3 +108,27 @@ export async function getWordPressCategories(props) {
|
|
|
103
108
|
}
|
|
104
109
|
return categories; // Return the complete list of categories
|
|
105
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Convert a WordPress Photon CDN URL to the original direct image URL
|
|
113
|
+
* @param photonUrl - The Photon CDN URL (e.g., https://i0.wp.com/domain.com/path)
|
|
114
|
+
* @returns The original direct image URL (e.g., https://domain.com/path)
|
|
115
|
+
*/
|
|
116
|
+
export function photonToOriginalUrl(photonUrl) {
|
|
117
|
+
if (typeof photonUrl !== 'string' || !photonUrl.includes('i0.wp.com/')) {
|
|
118
|
+
return photonUrl; // Return unchanged if not a Photon URL
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
// Photon URL format: https://i0.wp.com/domain.com/path/to/image.jpg?params
|
|
122
|
+
// Extract original: https://domain.com/path/to/image.jpg
|
|
123
|
+
const photonUrlObj = new URL(photonUrl);
|
|
124
|
+
const pathWithoutLeadingSlash = photonUrlObj.pathname.slice(1); // Remove leading /
|
|
125
|
+
const firstSlashIndex = pathWithoutLeadingSlash.indexOf('/');
|
|
126
|
+
const domain = pathWithoutLeadingSlash.slice(0, firstSlashIndex);
|
|
127
|
+
const path = pathWithoutLeadingSlash.slice(firstSlashIndex);
|
|
128
|
+
return `https://${domain}${path}`;
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
console.warn('Failed to parse Photon URL:', photonUrl, e);
|
|
132
|
+
return photonUrl; // Return original on error
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
display: inline-block;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
details.
|
|
11
|
+
details.menu-expando-wrapper {
|
|
12
12
|
display: inline-block;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
details.
|
|
15
|
+
details.menu-expando-wrapper > summary {
|
|
16
16
|
cursor: pointer;
|
|
17
17
|
user-select: none;
|
|
18
18
|
background: #336699;
|
|
@@ -24,26 +24,31 @@ details.menuExpandoWrapper > summary {
|
|
|
24
24
|
transition: background 0.3s ease;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
details.menu-expando-wrapper > summary:hover {
|
|
28
29
|
background: #2d5a8a;
|
|
29
30
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
details.
|
|
33
|
+
|
|
34
|
+
details.menu-expando-wrapper > summary::marker,
|
|
35
|
+
details.menu-expando-wrapper > summary::-webkit-details-marker {
|
|
34
36
|
display: none;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
|
|
40
|
+
details.menu-expando-wrapper > summary::before {
|
|
38
41
|
content: '▼';
|
|
39
42
|
display: inline-block;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
|
|
46
|
+
details.menu-expando-wrapper[open] > summary::before {
|
|
43
47
|
content: '▲';
|
|
44
48
|
}
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
|
|
51
|
+
details.menu-expando-wrapper ul {
|
|
47
52
|
margin-top: 10px;
|
|
48
53
|
padding-left: 20px;
|
|
49
54
|
list-style: none;
|
|
@@ -51,17 +56,17 @@ details.menuExpandoWrapper ul {
|
|
|
51
56
|
max-height: 1000px;
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
details.
|
|
59
|
+
details.menu-expando-wrapper li {
|
|
55
60
|
margin: 5px 0;
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
details.
|
|
63
|
+
details.menu-expando-wrapper a {
|
|
59
64
|
color: #333;
|
|
60
65
|
text-decoration: none;
|
|
61
66
|
transition: color 0.2s ease;
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
details.
|
|
69
|
+
details.menu-expando-wrapper a:hover {
|
|
65
70
|
color: #336699;
|
|
66
71
|
text-decoration: underline;
|
|
67
72
|
}
|
|
@@ -109,7 +109,7 @@ export function MenuExpando(props) {
|
|
|
109
109
|
}
|
|
110
110
|
return myItems;
|
|
111
111
|
}
|
|
112
|
-
return (_jsx("div", { className: "menuExpando", id: "menuExpando", children: _jsxs("details", { className: "
|
|
112
|
+
return (_jsx("div", { className: "menuExpando", id: "menuExpando", children: _jsxs("details", { className: "menu-expando-wrapper", id: "menu-expando-wrapper", ref: detailsRef, children: [_jsx("summary", {}), _jsx("ul", { ref: ulRef, children: generateMenuItems() })] }) }));
|
|
113
113
|
}
|
|
114
114
|
MenuExpando.propTypes = {
|
|
115
115
|
menuItems: PropTypes.oneOfType([
|
|
@@ -132,7 +132,7 @@ export function MenuExpandoButton() {
|
|
|
132
132
|
function handleMenuExpandoButtonClick(event) {
|
|
133
133
|
event.preventDefault();
|
|
134
134
|
event.stopPropagation();
|
|
135
|
-
const details = document.getElementById('
|
|
135
|
+
const details = document.getElementById('menu-expando-wrapper');
|
|
136
136
|
if (details)
|
|
137
137
|
details.open = !details.open;
|
|
138
138
|
}
|
|
@@ -7,7 +7,7 @@ import { FormEngine } from '../form/form';
|
|
|
7
7
|
* Shows FormEngine when component is selected, placeholder otherwise
|
|
8
8
|
*/
|
|
9
9
|
ComponentPropertiesForm.propTypes = {
|
|
10
|
-
editableComponent: PropTypes.object
|
|
10
|
+
editableComponent: PropTypes.object,
|
|
11
11
|
onSubmit: PropTypes.func.isRequired,
|
|
12
12
|
};
|
|
13
13
|
export function ComponentPropertiesForm({ editableComponent, onSubmit }) {
|
|
@@ -174,7 +174,8 @@ export function FormBuild(props) {
|
|
|
174
174
|
function handlePhaseOneSubmit(event) {
|
|
175
175
|
// GENERATE THE JSON TO DISPLAY A FORM TO ADD A FIELD - EXTERNAL
|
|
176
176
|
const target = event.target;
|
|
177
|
-
const
|
|
177
|
+
const typeElement = target.elements.namedItem('type');
|
|
178
|
+
const myType = typeElement ? typeElement.value : '';
|
|
178
179
|
const myComponent = mapTypeToComponent(myType);
|
|
179
180
|
const fieldJSON = generateFieldJSON(myComponent, myType);
|
|
180
181
|
props.setFormData(fieldJSON);
|
|
@@ -60,4 +60,10 @@ export declare namespace getWordPressCategories {
|
|
|
60
60
|
baseURL: PropTypes.Requireable<string>;
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Convert a WordPress Photon CDN URL to the original direct image URL
|
|
65
|
+
* @param photonUrl - The Photon CDN URL (e.g., https://i0.wp.com/domain.com/path)
|
|
66
|
+
* @returns The original direct image URL (e.g., https://domain.com/path)
|
|
67
|
+
*/
|
|
68
|
+
export declare function photonToOriginalUrl(photonUrl: string): string;
|
|
63
69
|
//# sourceMappingURL=wordpress.functions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wordpress.functions.d.ts","sourceRoot":"","sources":["../../../../src/components/cms/wordpress.functions.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAWnD,MAAM,MAAM,YAAY,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE;QAChB,GAAG,EAAE,MAAM,CAAC;KACZ,CAAA;IACD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC,CAAC;AAMF,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,SAAS,CAAC,CAAC;AACnF,wBAAsB,iBAAiB,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"wordpress.functions.d.ts","sourceRoot":"","sources":["../../../../src/components/cms/wordpress.functions.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAWnD,MAAM,MAAM,YAAY,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE;QAChB,GAAG,EAAE,MAAM,CAAC;KACZ,CAAA;IACD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC,CAAC;AAMF,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,SAAS,CAAC,CAAC;AACnF,wBAAsB,iBAAiB,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,uCAkChG;yBAlCqB,iBAAiB;;;;;;;AAwCvC,MAAM,MAAM,qBAAqB,GAAG;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAIF,MAAM,MAAM,0BAA0B,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,SAAS,CAAC,CAAC;AAC7F,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,YAAY,GAAG,qBAAqB,EAAE,CAuClF;yBAvCe,sBAAsB;;;;;AA8CtC,MAAM,MAAM,oBAAoB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC;AAKF,MAAM,MAAM,0BAA0B,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,SAAS,CAAC,CAAC;AAC7F,wBAAsB,sBAAsB,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,8BAerF;yBAfqB,sBAAsB;;;;;;AAiB5C;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAkB7D"}
|
|
@@ -3,7 +3,7 @@ type ComponentPropertiesFormProps = InferProps<typeof ComponentPropertiesForm.pr
|
|
|
3
3
|
export declare function ComponentPropertiesForm({ editableComponent, onSubmit }: ComponentPropertiesFormProps): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
export declare namespace ComponentPropertiesForm {
|
|
5
5
|
var propTypes: {
|
|
6
|
-
editableComponent: PropTypes.
|
|
6
|
+
editableComponent: PropTypes.Requireable<object>;
|
|
7
7
|
onSubmit: PropTypes.Validator<(...args: any[]) => any>;
|
|
8
8
|
};
|
|
9
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../../../../src/components/pagebuilder/form/form.tsx"],"names":[],"mappings":"AACA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAInD,OAAO,YAAY,CAAC;AAcpB,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,SAAS,CAAC,CAAC;AACrE,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,2CA+D/C;yBA/De,UAAU;;;;;;;;;AAmF1B,wBAAgB,WAAW,4CA4D1B;AAaD,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,SAAS,CAAC,CAAC;AAC5D,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../../../../src/components/pagebuilder/form/form.tsx"],"names":[],"mappings":"AACA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAInD,OAAO,YAAY,CAAC;AAcpB,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,SAAS,CAAC,CAAC;AACrE,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,2CA+D/C;yBA/De,UAAU;;;;;;;;;AAmF1B,wBAAgB,WAAW,4CA4D1B;AAaD,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,SAAS,CAAC,CAAC;AAC5D,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,2CAoF7C;yBApFe,SAAS;;;;;AAiGzB,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,SAAS,CAAC,CAAC;AAC3E,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,2CAyCrD;yBAzCe,aAAa;;;;;AA8C7B,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,SAAS,CAAC,CAAC;AACpE,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,2CAmCrD;yBAnCe,aAAa;;;;;AA0C7B,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,SAAS,CAAC,CAAC;AAC5E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,qBAAqB,2CAmL7D;yBAnLe,iBAAiB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-properties-form.test.d.ts","sourceRoot":"","sources":["../../../src/tests/component-properties-form.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-selector.test.d.ts","sourceRoot":"","sources":["../../../src/tests/component-selector.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-tree.test.d.ts","sourceRoot":"","sources":["../../../src/tests/component-tree.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-builder-ui.test.d.ts","sourceRoot":"","sources":["../../../src/tests/page-builder-ui.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-engine.test.d.ts","sourceRoot":"","sources":["../../../src/tests/page-engine.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save-load-section.test.d.ts","sourceRoot":"","sources":["../../../src/tests/save-load-section.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wordpress.functions.test.d.ts","sourceRoot":"","sources":["../../../src/tests/wordpress.functions.test.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixelated-tech/components",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.13",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Pixelated Technologies",
|
|
@@ -91,12 +91,12 @@
|
|
|
91
91
|
"@eslint/markdown": "^7.5.1",
|
|
92
92
|
"@storybook/addon-webpack5-compiler-babel": "^4.0.0",
|
|
93
93
|
"@storybook/preset-scss": "^1.0.3",
|
|
94
|
-
"@storybook/react-webpack5": "^10.1.
|
|
94
|
+
"@storybook/react-webpack5": "^10.1.10",
|
|
95
95
|
"@testing-library/dom": "^10.4.1",
|
|
96
96
|
"@testing-library/react": "^16.3.1",
|
|
97
97
|
"@testing-library/user-event": "^14.6.1",
|
|
98
98
|
"@types/md5": "^2.3.6",
|
|
99
|
-
"@types/node": "^25.0.
|
|
99
|
+
"@types/node": "^25.0.3",
|
|
100
100
|
"@types/prop-types": "^15.7.15",
|
|
101
101
|
"@types/react": "^19.2.7",
|
|
102
102
|
"@types/react-dom": "^19.2.3",
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
"eslint-plugin-n": "^17.23.1",
|
|
118
118
|
"eslint-plugin-promise": "^7.2.1",
|
|
119
119
|
"eslint-plugin-react": "^7.37.4",
|
|
120
|
-
"eslint-plugin-storybook": "^10.1.
|
|
120
|
+
"eslint-plugin-storybook": "^10.1.10",
|
|
121
121
|
"file-loader": "^6.2.0",
|
|
122
122
|
"happy-dom": "^20.0.11",
|
|
123
123
|
"jsdom": "^27.3.0",
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
"react-test-renderer": "^19.2.3",
|
|
132
132
|
"sass": "^1.97.0",
|
|
133
133
|
"sass-loader": "^16.0.6",
|
|
134
|
-
"storybook": "^10.1.
|
|
134
|
+
"storybook": "^10.1.10",
|
|
135
135
|
"style-loader": "^4.0.0",
|
|
136
136
|
"ts-loader": "^9.5.4",
|
|
137
137
|
"typescript": "^5.9.3",
|