@akinon/pz-theme 2.0.125-rc.0 → 2.0.125
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,40 +1,12 @@
|
|
|
1
1
|
# @akinon/pz-theme
|
|
2
2
|
|
|
3
|
-
## 2.0.125
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
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 [1595d458]
|
|
35
|
-
- Updated dependencies [dca12811]
|
|
36
|
-
- @akinon/next@2.0.125-rc.0
|
|
37
|
-
- @akinon/theme-contract@2.0.125-rc.0
|
|
3
|
+
## 2.0.125
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- aaa4433: ZERO-5122: resolve search box texts per storefront language
|
|
8
|
+
- @akinon/next@2.0.125
|
|
9
|
+
- @akinon/theme-contract@2.0.125
|
|
38
10
|
|
|
39
11
|
## 2.0.124
|
|
40
12
|
|
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.125
|
|
4
|
+
"version": "2.0.125",
|
|
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.125
|
|
11
|
+
"@akinon/next": "2.0.125",
|
|
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.125
|
|
16
|
+
"@akinon/theme-contract": "2.0.125",
|
|
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.125
|
|
24
|
+
"@akinon/next": "2.0.125",
|
|
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",
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { fireEvent, render } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
let localizationValue: Record<string, unknown> = {};
|
|
5
|
+
|
|
6
|
+
jest.mock('@akinon/next/hooks', () => ({
|
|
7
|
+
__esModule: true,
|
|
8
|
+
useLocalization: () => localizationValue
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
jest.mock('next/navigation', () => ({
|
|
12
|
+
useRouter: () => ({ push: jest.fn() }),
|
|
13
|
+
usePathname: () => '/'
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
import SearchBoxBlock from '../search-box-block';
|
|
17
|
+
import type { BlockRendererProps } from '../block-renderer-registry';
|
|
18
|
+
|
|
19
|
+
const setLocalization = (
|
|
20
|
+
locale: string,
|
|
21
|
+
locales: string[] = ['tr', 'en'],
|
|
22
|
+
defaultLocaleValue = 'tr'
|
|
23
|
+
) => {
|
|
24
|
+
localizationValue = {
|
|
25
|
+
locale,
|
|
26
|
+
locales: locales.map((value) => ({ value, label: value.toUpperCase() })),
|
|
27
|
+
defaultLocaleValue,
|
|
28
|
+
localeUrlStrategy: 'hide_default_locale'
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const TR = 'Ürün, kategori, renk arayabilirsiniz';
|
|
33
|
+
const EN = 'You can search for products, categories, or colors.';
|
|
34
|
+
|
|
35
|
+
const mount = (
|
|
36
|
+
properties: Record<string, unknown>,
|
|
37
|
+
extra: Partial<BlockRendererProps> = {}
|
|
38
|
+
) =>
|
|
39
|
+
render(
|
|
40
|
+
<SearchBoxBlock
|
|
41
|
+
{...({
|
|
42
|
+
block: {
|
|
43
|
+
id: 'search',
|
|
44
|
+
type: 'search-box',
|
|
45
|
+
properties: { mode: 'icon', ...properties }
|
|
46
|
+
},
|
|
47
|
+
placeholderId: 'p',
|
|
48
|
+
sectionId: 's'
|
|
49
|
+
} as unknown as BlockRendererProps)}
|
|
50
|
+
{...extra}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const open = (container: HTMLElement) => {
|
|
55
|
+
fireEvent.click(container.querySelector('[data-search-box]') as HTMLElement);
|
|
56
|
+
return container;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const placeholderOf = (properties: Record<string, unknown>) =>
|
|
60
|
+
open(mount(properties).container)
|
|
61
|
+
.querySelector('[data-search-input]')
|
|
62
|
+
?.getAttribute('placeholder');
|
|
63
|
+
|
|
64
|
+
beforeEach(() => setLocalization('tr'));
|
|
65
|
+
|
|
66
|
+
describe('placeholder', () => {
|
|
67
|
+
it('keeps reading a plain string as it always has', () => {
|
|
68
|
+
expect(placeholderOf({ placeholder: TR })).toBe(TR);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('picks the entry for the page’s language from a per-language map', () => {
|
|
72
|
+
expect(placeholderOf({ placeholder: { tr: TR, en: EN } })).toBe(TR);
|
|
73
|
+
|
|
74
|
+
setLocalization('en');
|
|
75
|
+
expect(placeholderOf({ placeholder: { tr: TR, en: EN } })).toBe(EN);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('reads a per-language map typed into the field as JSON', () => {
|
|
79
|
+
setLocalization('en');
|
|
80
|
+
expect(
|
|
81
|
+
placeholderOf({ placeholder: JSON.stringify({ tr: TR, en: EN }) })
|
|
82
|
+
).toBe(EN);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('reads the JSON map the editor stores under a breakpoint', () => {
|
|
86
|
+
setLocalization('en');
|
|
87
|
+
expect(
|
|
88
|
+
placeholderOf({
|
|
89
|
+
placeholder: { desktop: JSON.stringify({ tr: TR, en: EN }) }
|
|
90
|
+
})
|
|
91
|
+
).toBe(EN);
|
|
92
|
+
|
|
93
|
+
setLocalization('tr');
|
|
94
|
+
expect(
|
|
95
|
+
placeholderOf({
|
|
96
|
+
placeholder: { desktop: JSON.stringify({ tr: TR, en: EN }) }
|
|
97
|
+
})
|
|
98
|
+
).toBe(TR);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('still honours a per-breakpoint value, with or without languages inside', () => {
|
|
102
|
+
expect(placeholderOf({ placeholder: { desktop: EN } })).toBe(EN);
|
|
103
|
+
expect(
|
|
104
|
+
placeholderOf({ placeholder: { desktop: { tr: TR, en: EN } } })
|
|
105
|
+
).toBe(TR);
|
|
106
|
+
expect(
|
|
107
|
+
placeholderOf({
|
|
108
|
+
placeholder: { tr: { desktop: TR }, en: { desktop: EN } }
|
|
109
|
+
})
|
|
110
|
+
).toBe(TR);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('falls back to the default language, then to the block’s own wording', () => {
|
|
114
|
+
setLocalization('en');
|
|
115
|
+
expect(placeholderOf({ placeholder: { tr: TR, en: '' } })).toBe(TR);
|
|
116
|
+
expect(placeholderOf({ placeholder: { tr: '', en: '' } })).toBe('Search');
|
|
117
|
+
expect(placeholderOf({})).toBe('Search');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('does not treat a locale-looking map as languages the store does not have', () => {
|
|
121
|
+
setLocalization('de', ['de']);
|
|
122
|
+
expect(placeholderOf({ placeholder: { tr: TR, en: EN } })).toBe('Search');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('the other shopper-facing texts', () => {
|
|
127
|
+
it('resolve the trigger, close and submit labels per language', () => {
|
|
128
|
+
setLocalization('en');
|
|
129
|
+
const { container } = mount({
|
|
130
|
+
triggerLabel: { tr: 'Ara', en: 'Search' },
|
|
131
|
+
closeLabel: { tr: 'Aramayı kapat', en: 'Close search' },
|
|
132
|
+
submitLabel: { tr: 'Ara', en: 'Go' },
|
|
133
|
+
showSubmit: true,
|
|
134
|
+
showClose: true
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(
|
|
138
|
+
container.querySelector('[data-search-box]')?.getAttribute('aria-label')
|
|
139
|
+
).toBe('Search');
|
|
140
|
+
|
|
141
|
+
open(container);
|
|
142
|
+
|
|
143
|
+
expect(
|
|
144
|
+
container.querySelector('[data-search-close]')?.getAttribute('aria-label')
|
|
145
|
+
).toBe('Close search');
|
|
146
|
+
expect(container.querySelector('button[type="submit"]')?.textContent).toBe(
|
|
147
|
+
'Go'
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('lets the trigger keep announcing the placeholder when no label is set', () => {
|
|
152
|
+
const { container } = mount({ placeholder: { tr: TR, en: EN } });
|
|
153
|
+
|
|
154
|
+
expect(
|
|
155
|
+
container.querySelector('[data-search-box]')?.getAttribute('aria-label')
|
|
156
|
+
).toBe(TR);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('leaves the non-text settings alone', () => {
|
|
160
|
+
const { container } = mount({ mode: { tr: 'inline', en: 'icon' } });
|
|
161
|
+
|
|
162
|
+
expect(container.querySelector('[data-search-box]')).not.toBeNull();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -7,6 +7,10 @@ import { emitCommerceEvent } from '../utils/analytics';
|
|
|
7
7
|
import { useLocalizeHref } from '../hooks/use-localize-href';
|
|
8
8
|
import { applySearchTermTransform } from '../utils/search-transform';
|
|
9
9
|
import { useOverlay } from '../hooks/use-overlay';
|
|
10
|
+
import {
|
|
11
|
+
resolveLocalizedValue,
|
|
12
|
+
useValueLocale
|
|
13
|
+
} from '../utils/localized-value';
|
|
10
14
|
import ThemeBlock from '../theme-block';
|
|
11
15
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
12
16
|
import { DesignerPlaceholder, readToggle } from './product-block-utils';
|
|
@@ -61,21 +65,35 @@ const SearchBoxBlock = ({
|
|
|
61
65
|
isDesigner && resolveForceOpen(block.styles, currentBreakpoint);
|
|
62
66
|
const panelOpen = open || forcedOpen;
|
|
63
67
|
|
|
68
|
+
const valueLocale = useValueLocale();
|
|
64
69
|
const prop = (key: string, fallback: string) =>
|
|
65
70
|
String(
|
|
66
71
|
getResponsiveValue(block.properties?.[key], currentBreakpoint, fallback)
|
|
67
72
|
);
|
|
73
|
+
const text = (key: string, fallback: string) => {
|
|
74
|
+
const localized = resolveLocalizedValue(
|
|
75
|
+
block.properties?.[key],
|
|
76
|
+
valueLocale
|
|
77
|
+
);
|
|
78
|
+
const value = resolveLocalizedValue(
|
|
79
|
+
getResponsiveValue(localized, currentBreakpoint, ''),
|
|
80
|
+
valueLocale
|
|
81
|
+
);
|
|
82
|
+
return value === undefined || value === null || value === ''
|
|
83
|
+
? fallback
|
|
84
|
+
: String(value);
|
|
85
|
+
};
|
|
68
86
|
|
|
69
87
|
const mode = prop('mode', 'icon');
|
|
70
|
-
const placeholder =
|
|
88
|
+
const placeholder = text('placeholder', 'Search');
|
|
71
89
|
const searchUrl = prop('searchUrl', '/list');
|
|
72
90
|
const queryParam = prop('queryParam', 'search_text');
|
|
73
|
-
const closeLabel =
|
|
74
|
-
const submitLabel =
|
|
91
|
+
const closeLabel = text('closeLabel', 'Close search');
|
|
92
|
+
const submitLabel = text('submitLabel', 'Search');
|
|
75
93
|
const submitStyle = prop('submitStyle', 'label');
|
|
76
94
|
// Empty on purpose: the trigger has always announced itself with the
|
|
77
95
|
// placeholder, so an unset label must keep doing exactly that.
|
|
78
|
-
const triggerLabel =
|
|
96
|
+
const triggerLabel = text('triggerLabel', '');
|
|
79
97
|
const showIcon = readToggle(
|
|
80
98
|
block.properties?.showIcon,
|
|
81
99
|
currentBreakpoint,
|