@akinon/pz-theme 2.0.149-beta.0 → 2.0.149-rc.0

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,13 +1,40 @@
1
1
  # @akinon/pz-theme
2
2
 
3
- ## 2.0.149-beta.0
4
-
5
- ### Patch Changes
6
-
7
- - 99eb4f1: ZERO-5221: hide slider block arrows at the widths they are turned off
8
- - Updated dependencies [cbbbfd757]
9
- - @akinon/next@2.0.149-beta.0
10
- - @akinon/theme-contract@2.0.149-beta.0
3
+ ## 2.0.149-rc.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [86187b1c2]
8
+ - Updated dependencies [0cf9ea239]
9
+ - Updated dependencies [324f97d55]
10
+ - Updated dependencies [51ea06888]
11
+ - Updated dependencies
12
+ - Updated dependencies [2fea41430]
13
+ - Updated dependencies [b55acb768]
14
+ - Updated dependencies [760258c1c]
15
+ - Updated dependencies [143be2b9d]
16
+ - Updated dependencies [7889b08fe]
17
+ - Updated dependencies [9f8cd3bc5]
18
+ - Updated dependencies [bfafa3f49]
19
+ - Updated dependencies [57d7eb305]
20
+ - Updated dependencies [d99a6a7d5]
21
+ - Updated dependencies [9db81a714]
22
+ - Updated dependencies [591e345e1]
23
+ - Updated dependencies [4de5303c5]
24
+ - Updated dependencies [95b139dc1]
25
+ - Updated dependencies [1d00f2d06]
26
+ - Updated dependencies [4ac7b2a1e]
27
+ - Updated dependencies [e9598c71d]
28
+ - Updated dependencies [4998a9631]
29
+ - Updated dependencies [804d2bd6c]
30
+ - Updated dependencies [3909d3224]
31
+ - Updated dependencies [6a3d8a631]
32
+ - Updated dependencies [e18836b20]
33
+ - Updated dependencies [b1111d7b1]
34
+ - Updated dependencies [1595d458e]
35
+ - Updated dependencies [dca128112]
36
+ - @akinon/next@2.0.149-rc.0
37
+ - @akinon/theme-contract@2.0.149-rc.0
11
38
 
12
39
  ## 2.0.148
13
40
 
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@akinon/pz-theme",
3
3
  "description": "Theme package for Project Zero Next — ThemePlaceholder system and theme editor infrastructure",
4
- "version": "2.0.149-beta.0",
4
+ "version": "2.0.149-rc.0",
5
5
  "license": "MIT",
6
6
  "main": "src/index.ts",
7
7
  "scripts": {
8
8
  "test": "jest"
9
9
  },
10
10
  "peerDependencies": {
11
- "@akinon/next": "2.0.149-beta.0",
11
+ "@akinon/next": "2.0.149-rc.0",
12
12
  "react": "^18.0.0 || ^19.0.0",
13
13
  "react-dom": "^18.0.0 || ^19.0.0"
14
14
  },
15
15
  "dependencies": {
16
- "@akinon/theme-contract": "2.0.149-beta.0",
16
+ "@akinon/theme-contract": "2.0.149-rc.0",
17
17
  "clsx": "2.1.1",
18
18
  "lottie-web": "^5.13.0",
19
19
  "react-multi-carousel": "2.8.4",
@@ -21,7 +21,7 @@
21
21
  "zod": "4.4.1"
22
22
  },
23
23
  "devDependencies": {
24
- "@akinon/next": "2.0.149-beta.0",
24
+ "@akinon/next": "2.0.149-rc.0",
25
25
  "@testing-library/jest-dom": "5.16.4",
26
26
  "@testing-library/react": "16.3.2",
27
27
  "@testing-library/user-event": "14.1.1",
@@ -15,7 +15,6 @@ import {
15
15
  } from '../utils';
16
16
  import {
17
17
  DESKTOP_MIN_WIDTH,
18
- TABLET_MIN_WIDTH,
19
18
  isNarrowBreakpoint,
20
19
  setAtAnyBreakpoint
21
20
  } from '../utils/breakpoints';
@@ -326,30 +325,13 @@ const SliderBlock: React.FC<BlockRendererProps> = ({
326
325
  const canOverflow = visibleCount > 1;
327
326
  const renderArrows = arrowsAnywhere && canOverflow;
328
327
 
329
- const arrowBands = {
330
- desktop: carouselResponsive.desktop,
331
- tablet: {
332
- breakpoint: { max: DESKTOP_MIN_WIDTH - 1, min: TABLET_MIN_WIDTH },
333
- items: mobileItems
334
- },
335
- mobile: {
336
- breakpoint: { max: TABLET_MIN_WIDTH - 1, min: 0 },
337
- items: mobileItems
338
- }
339
- };
340
- const arrowOwnerId = carouselSsrScopeId(block.styleSourceId || block.id);
341
- const arrowCss =
342
- isDesigner || !arrowOwnerId
343
- ? ''
344
- : buildCarouselArrowCss(
345
- carouselSsrScopeId(block.id) || '',
346
- arrowBands,
347
- (band) =>
348
- getResponsiveValue(arrowsConfigured, band, true) === true &&
349
- visibleCount >
350
- Number(arrowBands[band as keyof typeof arrowBands]?.items || 1),
351
- `[data-block-id="${arrowOwnerId}"]`
352
- );
328
+ const arrowCss = buildCarouselArrowCss(
329
+ carouselSsrScopeId(block.id) || '',
330
+ carouselResponsive,
331
+ (band) =>
332
+ getResponsiveValue(arrowsConfigured, band, true) === true &&
333
+ visibleCount > Number(carouselResponsive?.[band]?.items || 1)
334
+ );
353
335
 
354
336
  const showDots = getResponsiveValue(
355
337
  getConfigEntry('show-dots'),
@@ -1158,7 +1140,6 @@ const SliderBlock: React.FC<BlockRendererProps> = ({
1158
1140
  * section arrows in `utils/slider-arrows.tsx` already carry this.
1159
1141
  */
1160
1142
  data-block-id={iconBlock?.id}
1161
- data-slider-arrow=""
1162
1143
  onClick={onActivate}
1163
1144
  className={arrowPlacementClass(
1164
1145
  plateStyle,
@@ -1419,7 +1400,8 @@ const SliderBlock: React.FC<BlockRendererProps> = ({
1419
1400
  })}
1420
1401
  </Carousel>
1421
1402
 
1422
- {(isDesigner ? showArrows : arrowsOutside && arrowsAnywhere) &&
1403
+ {(isDesigner || arrowsOutside) &&
1404
+ showArrows &&
1423
1405
  sortedBlocks.length > 0 &&
1424
1406
  leftArrowIconBlock &&
1425
1407
  rightArrowIconBlock ? (
@@ -1,223 +0,0 @@
1
- import React from 'react';
2
- import { render } from '@testing-library/react';
3
-
4
- jest.mock('react-multi-carousel', () => ({
5
- __esModule: true,
6
- default: (carouselProps: any) => (
7
- <div data-testid="carousel" className={carouselProps.containerClass}>
8
- {carouselProps.customLeftArrow}
9
- {carouselProps.children}
10
- {carouselProps.customRightArrow}
11
- </div>
12
- )
13
- }));
14
-
15
- jest.mock('../../theme-block', () => ({
16
- __esModule: true,
17
- default: ({ block }: { block: { id: string } }) => (
18
- <div data-testid="tb" data-id={block.id} />
19
- )
20
- }));
21
-
22
- import SliderBlock from '../slider-block';
23
-
24
- const child = (id: string, over: Record<string, unknown> = {}): any => ({
25
- id,
26
- type: 'text',
27
- label: id,
28
- value: '',
29
- properties: {},
30
- styles: {},
31
- order: 0,
32
- hidden: false,
33
- ...over
34
- });
35
-
36
- const slides = (count: number) =>
37
- Array.from({ length: count }, (_unused, index) =>
38
- child(`slide-${index}`, { order: index })
39
- );
40
-
41
- const arrowIcon = (label: string, styles: Record<string, unknown> = {}) =>
42
- child(label, { type: 'icon', label, value: { url: '/a.svg' }, styles });
43
-
44
- const sliderBlock = (
45
- showArrows: unknown,
46
- arrowStyles: Record<string, unknown> = {},
47
- over: Record<string, unknown> = {}
48
- ): any => ({
49
- id: 'slider-1',
50
- type: 'slider',
51
- label: 'slider',
52
- value: '',
53
- properties: {
54
- 'show-arrows': showArrows,
55
- 'items-per-slide': { desktop: 1, mobile: 1 }
56
- },
57
- styles: {},
58
- order: 0,
59
- hidden: false,
60
- blocks: [
61
- arrowIcon('Left Arrow Icon', arrowStyles),
62
- ...slides(3),
63
- arrowIcon('Right Arrow Icon', arrowStyles)
64
- ],
65
- ...over
66
- });
67
-
68
- const renderOnStorefront = (block: any, isDesigner = false) =>
69
- render(
70
- <div data-block-id={block.styleSourceId || block.id}>
71
- <SliderBlock
72
- {...({
73
- block,
74
- placeholderId: 'home-body',
75
- sectionId: 'section-1',
76
- isDesigner
77
- } as any)}
78
- />
79
- </div>
80
- );
81
-
82
- const arrowRules = (container: HTMLElement) =>
83
- Array.from(container.querySelectorAll('style'))
84
- .map((style) => style.textContent || '')
85
- .filter((css) => css.includes('[data-slider-arrow]'));
86
-
87
- const hiddenRulesOf = (css: string) =>
88
- Array.from(
89
- css.matchAll(/(@media[^{]+)\{([^{]+)\{display:none !important;\}\}/g)
90
- ).map((match) => ({ query: match[1], selectors: match[2] }));
91
-
92
- const hiddenSelectorsOf = (css: string) =>
93
- hiddenRulesOf(css).map((rule) => rule.selectors);
94
-
95
- describe('slider block arrows at the widths they are turned off', () => {
96
- it('marks every arrow the block draws inside the track', () => {
97
- const { container } = renderOnStorefront(
98
- sliderBlock({ desktop: true, mobile: false })
99
- );
100
-
101
- expect(container.querySelectorAll('[data-slider-arrow]')).toHaveLength(2);
102
- });
103
-
104
- it('hides below desktop when only mobile is off, and the rules reach both arrows', () => {
105
- const { container } = renderOnStorefront(
106
- sliderBlock({ desktop: true, mobile: false })
107
- );
108
- const [css] = arrowRules(container);
109
- const rules = hiddenRulesOf(css);
110
-
111
- expect(rules.map((rule) => rule.query)).toEqual([
112
- '@media (min-width:768px) and (max-width:1023px)',
113
- '@media (max-width:767px)'
114
- ]);
115
- rules.forEach((rule) => {
116
- expect(container.querySelectorAll(rule.selectors)).toHaveLength(2);
117
- });
118
- });
119
-
120
- it('honours a tablet value of its own', () => {
121
- const { container } = renderOnStorefront(
122
- sliderBlock({ desktop: true, tablet: false, mobile: true })
123
- );
124
- const rules = hiddenRulesOf(arrowRules(container)[0]);
125
-
126
- expect(rules.map((rule) => rule.query)).toEqual([
127
- '@media (min-width:768px) and (max-width:1023px)'
128
- ]);
129
- expect(container.querySelectorAll(rules[0].selectors)).toHaveLength(2);
130
- });
131
-
132
- it('keeps the tablet on when only the phone is turned off', () => {
133
- const { container } = renderOnStorefront(
134
- sliderBlock({ desktop: true, tablet: true, mobile: false })
135
- );
136
- const rules = hiddenRulesOf(arrowRules(container)[0]);
137
-
138
- expect(rules.map((rule) => rule.query)).toEqual([
139
- '@media (max-width:767px)'
140
- ]);
141
- });
142
-
143
- it('reaches arrows placed outside the track just the same', () => {
144
- const { container } = renderOnStorefront(
145
- sliderBlock({ desktop: true, mobile: false }, { top: '0px' })
146
- );
147
- const [css] = arrowRules(container);
148
- const [selectors] = hiddenSelectorsOf(css);
149
-
150
- expect(
151
- container.querySelector('[data-testid="carousel"] [data-slider-arrow]')
152
- ).toBeNull();
153
- expect(container.querySelectorAll(selectors)).toHaveLength(2);
154
- });
155
-
156
- it('hides the desktop band instead when that is the one turned off', () => {
157
- const { container } = renderOnStorefront(
158
- sliderBlock({ desktop: false, mobile: true })
159
- );
160
- const rules = hiddenRulesOf(arrowRules(container)[0]);
161
-
162
- expect(rules.map((rule) => rule.query)).toEqual([
163
- '@media (min-width:1024px) and (max-width:4000px)'
164
- ]);
165
- expect(container.querySelectorAll(rules[0].selectors)).toHaveLength(2);
166
- });
167
-
168
- it('ships arrows placed outside the track even when the desktop band is off', () => {
169
- const { container } = renderOnStorefront(
170
- sliderBlock({ desktop: false, mobile: true }, { top: '0px' })
171
- );
172
- const [css] = arrowRules(container);
173
-
174
- expect(container.querySelectorAll('[data-slider-arrow]')).toHaveLength(2);
175
- expect(css).toContain('(min-width:');
176
- expect(container.querySelectorAll(hiddenSelectorsOf(css)[0])).toHaveLength(
177
- 2
178
- );
179
- });
180
-
181
- it('draws no arrows at all when they are off at every width', () => {
182
- const { container } = renderOnStorefront(
183
- sliderBlock({ desktop: false, mobile: false }, { top: '0px' })
184
- );
185
-
186
- expect(container.querySelectorAll('[data-slider-arrow]')).toHaveLength(0);
187
- });
188
-
189
- it('writes no hiding rule while the arrows are on everywhere', () => {
190
- const { container } = renderOnStorefront(sliderBlock(true));
191
-
192
- expect(arrowRules(container)).toHaveLength(0);
193
- expect(container.querySelectorAll('[data-slider-arrow]')).toHaveLength(2);
194
- });
195
-
196
- it('follows the wrapper of a row clone, which carries the template id', () => {
197
- const { container } = renderOnStorefront(
198
- sliderBlock(
199
- { desktop: true, mobile: false },
200
- {},
201
- {
202
- id: 'slider-1-row-2',
203
- styleSourceId: 'slider-1'
204
- }
205
- )
206
- );
207
- const [css] = arrowRules(container);
208
-
209
- expect(css).toContain('[data-block-id="slider-1"] [data-slider-arrow]');
210
- expect(container.querySelectorAll(hiddenSelectorsOf(css)[0])).toHaveLength(
211
- 2
212
- );
213
- });
214
-
215
- it('leaves the designer canvas to resolve the breakpoint itself', () => {
216
- const { container } = renderOnStorefront(
217
- sliderBlock({ desktop: true, mobile: false }),
218
- true
219
- );
220
-
221
- expect(arrowRules(container)).toHaveLength(0);
222
- });
223
- });
@@ -1,113 +0,0 @@
1
- import React from 'react';
2
- import { render } from '@testing-library/react';
3
-
4
- jest.mock('../../theme-block', () => ({
5
- __esModule: true,
6
- default: ({ block }: { block: { id: string } }) => (
7
- <div data-testid="slide" data-id={block.id} />
8
- )
9
- }));
10
-
11
- jest.mock('react-multi-carousel', () => ({
12
- __esModule: true,
13
- default: ({
14
- children,
15
- customLeftArrow,
16
- customRightArrow
17
- }: {
18
- children?: React.ReactNode;
19
- customLeftArrow?: React.ReactNode;
20
- customRightArrow?: React.ReactNode;
21
- }) => (
22
- <div data-testid="carousel">
23
- {customLeftArrow}
24
- {customRightArrow}
25
- {children}
26
- </div>
27
- )
28
- }));
29
-
30
- import PostsSliderSection from '../posts-slider-section';
31
- import {
32
- LEFT_ARROW_ICON_LABEL,
33
- RIGHT_ARROW_ICON_LABEL
34
- } from '../../utils/slider-arrows';
35
-
36
- const slide = (id: string) => ({
37
- id,
38
- type: 'group',
39
- label: id,
40
- styles: {},
41
- order: 0,
42
- blocks: []
43
- });
44
-
45
- const arrow = (label: string) => ({
46
- id: `arrow-${label === LEFT_ARROW_ICON_LABEL ? 'left' : 'right'}`,
47
- type: 'icon',
48
- label,
49
- value: { url: '', alt: label },
50
- properties: {},
51
- styles: {},
52
- order: 9
53
- });
54
-
55
- const renderSection = (showArrows: unknown) =>
56
- render(
57
- <section data-section-id="sec">
58
- <PostsSliderSection
59
- {...({
60
- section: {
61
- id: 'sec',
62
- type: 'flash-deals-slider',
63
- label: 'sec',
64
- properties: { 'items-per-slide': 1, 'show-arrows': showArrows },
65
- styles: {},
66
- blocks: [
67
- arrow(LEFT_ARROW_ICON_LABEL),
68
- slide('a'),
69
- slide('b'),
70
- arrow(RIGHT_ARROW_ICON_LABEL)
71
- ]
72
- },
73
- currentBreakpoint: 'desktop',
74
- placeholderId: 'ph',
75
- isDesigner: false,
76
- selectedBlockId: null
77
- } as any)}
78
- />
79
- </section>
80
- );
81
-
82
- const arrowRules = (container: HTMLElement) =>
83
- Array.from(container.querySelectorAll('style'))
84
- .map((style) => style.textContent || '')
85
- .filter((css) => css.includes('[data-slider-arrow]'));
86
-
87
- const hiddenSelectorsOf = (css: string) =>
88
- Array.from(
89
- css.matchAll(/@media[^{]+\{([^{]+)\{display:none !important;\}\}/g)
90
- ).map((match) => match[1]);
91
-
92
- describe('posts slider arrows at the widths they are turned off', () => {
93
- it('hides only below desktop, and every rule reaches both arrows', () => {
94
- const { container } = renderSection({ desktop: true, mobile: false });
95
- const [css] = arrowRules(container);
96
-
97
- const selectors = hiddenSelectorsOf(css);
98
-
99
- expect(css).toBeDefined();
100
- expect(selectors.length).toBeGreaterThan(0);
101
- expect(css).not.toMatch(/@media \(min-width:\d+px\)\{/);
102
- selectors.forEach((selector) => {
103
- expect(container.querySelectorAll(selector)).toHaveLength(2);
104
- });
105
- });
106
-
107
- it('writes no hiding rule while the arrows are on everywhere', () => {
108
- const { container } = renderSection(true);
109
-
110
- expect(arrowRules(container)).toHaveLength(0);
111
- expect(container.querySelectorAll('[data-slider-arrow]')).toHaveLength(2);
112
- });
113
- });