@akinon/pz-theme 2.0.166-rc.2 → 2.0.166

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,73 +1,12 @@
1
1
  # @akinon/pz-theme
2
2
 
3
- ## 2.0.166-rc.2
3
+ ## 2.0.166
4
4
 
5
5
  ### Patch Changes
6
6
 
7
7
  - f6871947: ZERO-5509: The order-detail block can hand its whole page to a child group with role `order-detail-layout`. The group is drawn once over the order in place of the built-in markup: its blocks bind `item.*` to the order (number, date, status, totals, discounts, installments, invoice / cancel / tracking links, addresses, payment), already formatted with the block's money style, and a repeat over `item.lines` draws the line rows. Line rows (also in the existing row layout) now carry color, size, tracking, carrier, return code / method / carrier / pickup date, and are scoped as a one-line order so the rate & review action placed in a line rates that line only. Clones of a repeat inside a row now open a row scope for their entry (`Block.itemScopeRow`). Without a layout the block renders exactly as before.
8
- - @akinon/next@2.0.166-rc.2
9
- - @akinon/theme-contract@2.0.166-rc.2
10
-
11
- ## 2.0.166-rc.1
12
-
13
- ### Patch Changes
14
-
15
- - e3c2bab2: ZERO-5521: restore the text block's useValueLocale import and the /api/auth no-store header rule that the automatic RC sync dropped
16
- - Updated dependencies [e3c2bab2]
17
- - @akinon/next@2.0.166-rc.1
18
- - @akinon/theme-contract@2.0.166-rc.1
19
-
20
- ## 2.0.166-rc.0
21
-
22
- ### Patch Changes
23
-
24
- - 8e040731e: ZERO-5235: let brands hang actions on order detail lines
25
- - c761b9fd4: ZERO-5296: fall back to the storefront default language in the text block
26
- - 17a598dae: ZERO-5296: report the storefront default language to the editor
27
- - 3b05a407d: ZERO-5232: allow richtext settings on custom blocks
28
- - Updated dependencies [86187b1c2]
29
- - Updated dependencies [08390a87]
30
- - Updated dependencies [0cf9ea239]
31
- - Updated dependencies [324f97d55]
32
- - Updated dependencies [4c3465ae]
33
- - Updated dependencies [51ea06888]
34
- - Updated dependencies
35
- - Updated dependencies [549da545]
36
- - Updated dependencies [2fea41430]
37
- - Updated dependencies [b55acb768]
38
- - Updated dependencies [760258c1c]
39
- - Updated dependencies [143be2b9d]
40
- - Updated dependencies [7889b08fe]
41
- - Updated dependencies [9f8cd3bc5]
42
- - Updated dependencies [d635d345]
43
- - Updated dependencies [bfafa3f49]
44
- - Updated dependencies [d860c917]
45
- - Updated dependencies [ac6cada1]
46
- - Updated dependencies [e90c0e02]
47
- - Updated dependencies [dc7f9ca5]
48
- - Updated dependencies [57d7eb305]
49
- - Updated dependencies [0145dc3f]
50
- - Updated dependencies [d99a6a7d5]
51
- - Updated dependencies [92086b29]
52
- - Updated dependencies [227b7408]
53
- - Updated dependencies [9db81a714]
54
- - Updated dependencies [591e345e1]
55
- - Updated dependencies [4de5303c5]
56
- - Updated dependencies [95b139dc1]
57
- - Updated dependencies [1d00f2d06]
58
- - Updated dependencies [4ac7b2a1e]
59
- - Updated dependencies [4998a9631]
60
- - Updated dependencies [804d2bd6c]
61
- - Updated dependencies [3909d3224]
62
- - Updated dependencies [6a3d8a631]
63
- - Updated dependencies [e18836b20]
64
- - Updated dependencies [b1111d7b1]
65
- - Updated dependencies [1595d458e]
66
- - Updated dependencies [dca128112]
67
- - Updated dependencies [9a7b0937]
68
- - Updated dependencies [8853822c]
69
- - @akinon/next@2.0.166-rc.0
70
- - @akinon/theme-contract@2.0.166-rc.0
8
+ - @akinon/next@2.0.166
9
+ - @akinon/theme-contract@2.0.166
71
10
 
72
11
  ## 2.0.165
73
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.166-rc.2",
4
+ "version": "2.0.166",
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.166-rc.2",
11
+ "@akinon/next": "2.0.166",
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.166-rc.2",
16
+ "@akinon/theme-contract": "2.0.166",
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.166-rc.2",
24
+ "@akinon/next": "2.0.166",
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",
@@ -28,7 +28,6 @@ const SETTINGS_LOCALES = [
28
28
  const mockLocalization = {
29
29
  locale: 'tr',
30
30
  locales: SETTINGS_LOCALES as Array<Record<string, unknown>>,
31
- defaultLocaleValue: 'tr',
32
31
  setLocale: jest.fn()
33
32
  };
34
33
 
@@ -107,7 +106,6 @@ beforeEach(() => {
107
106
  postMessage.mockClear();
108
107
  mockLocalization.locale = 'tr';
109
108
  mockLocalization.locales = SETTINGS_LOCALES;
110
- mockLocalization.defaultLocaleValue = 'tr';
111
109
  mockLocalization.setLocale = jest.fn();
112
110
  delete (window as { __iframeReadySent?: boolean }).__iframeReadySent;
113
111
  });
@@ -146,8 +144,7 @@ describe('inside the editor', () => {
146
144
  { value: 'en', label: 'English' },
147
145
  { value: 'tr', label: 'Türkçe' },
148
146
  { value: 'de', label: 'de' }
149
- ],
150
- defaultLocale: 'tr'
147
+ ]
151
148
  }
152
149
  })
153
150
  ]);
@@ -157,34 +154,6 @@ describe('inside the editor', () => {
157
154
  expect(targetOrigin).toBe(EDITOR_ORIGIN);
158
155
  });
159
156
 
160
- it('posts the languages before the document reports ready', () => {
161
- // The editor holds every per-language write until IFRAME_READY and takes
162
- // "ready without languages" to mean they are never coming, so the order
163
- // of the two frames is part of the contract.
164
- render(placeholder('header'));
165
-
166
- const types = postMessage.mock.calls.map(([message]) => message?.type);
167
- expect(types).toContain('LOCALE_UPDATE');
168
- expect(types).toContain('IFRAME_READY');
169
- expect(types.indexOf('LOCALE_UPDATE')).toBeLessThan(
170
- types.indexOf('IFRAME_READY')
171
- );
172
- });
173
-
174
- it("posts the brand's default language, whichever it is", () => {
175
- // The editor's per-language text controls take the plain-string form to
176
- // mean this language; left to guess, the editor assumed English.
177
- mockLocalization.defaultLocaleValue = 'de';
178
-
179
- render(<EditorLocaleBridge />);
180
-
181
- expect(localeFrames()).toEqual([
182
- expect.objectContaining({
183
- data: expect.objectContaining({ locale: 'tr', defaultLocale: 'de' })
184
- })
185
- ]);
186
- });
187
-
188
157
  it("follows the editor's switch exactly once, with several placeholders mounted", () => {
189
158
  render(
190
159
  <>
@@ -2,31 +2,9 @@ import React from 'react';
2
2
  import { render } from '@testing-library/react';
3
3
  import TextBlock from '../text-block';
4
4
 
5
- // The page's language and the storefront's default, as the localization
6
- // provider reports them. Most cases run on an English-default store showing
7
- // English; the fallback case moves both.
8
- const mockLocalization = { locale: 'en-us', defaultLocaleValue: 'en-us' };
9
-
10
- jest.mock('@akinon/next/hooks', () => ({
11
- __esModule: true,
12
- useLocalization: () => ({
13
- t: (path: string) => path,
14
- setLocale: () => {},
15
- setCurrency: () => {},
16
- locale: mockLocalization.locale,
17
- currency: 'USD',
18
- locales: [{ value: 'en-us' }, { value: 'tr-tr' }],
19
- currencies: ['USD'],
20
- defaultLocaleValue: mockLocalization.defaultLocaleValue,
21
- defaultCurrencyCode: 'USD',
22
- localeUrlStrategy: 'hide-default-locale'
23
- })
24
- }));
25
-
26
- beforeEach(() => {
27
- mockLocalization.locale = 'en-us';
28
- mockLocalization.defaultLocaleValue = 'en-us';
29
- });
5
+ // The mocked useLocalization (jest/__mocks__/akinon-next-hooks.js) returns
6
+ // locale: 'en-us'. process.env.NEXT_PUBLIC_DEFAULT_LOCALE is unset in the
7
+ // jest env, so the component's defaultLocale falls back to 'en'.
30
8
 
31
9
  const renderBlock = (value: unknown, styles?: Record<string, unknown>) => {
32
10
  const block: any = { id: 'b1', type: 'text', value, styles };
@@ -56,16 +34,10 @@ describe('TextBlock value-shape resolution', () => {
56
34
  );
57
35
  });
58
36
 
59
- it("falls back to the storefront's default language when the active one is absent", () => {
60
- // A Turkish-default store showing German. The entry under `en` is what
61
- // the block used to show: its default was read from an env variable
62
- // that falls back to English, not from the storefront (ZERO-5296).
63
- mockLocalization.locale = 'de-de';
64
- mockLocalization.defaultLocaleValue = 'tr-tr';
65
-
66
- const { container } = renderBlock({ en: 'EN', 'tr-tr': 'TR' });
37
+ it('falls back to defaultLocale ("en") when active locale is absent', () => {
38
+ const { container } = renderBlock({ en: 'EN', fr: 'FR' });
67
39
  expect((container.querySelector('div') as HTMLDivElement).innerHTML).toBe(
68
- 'TR'
40
+ 'EN'
69
41
  );
70
42
  });
71
43
 
@@ -13,7 +13,6 @@ import {
13
13
  ITEM_TEMPLATE_ROLE,
14
14
  ItemTemplateList
15
15
  } from './item-template';
16
- import { renderOrderLineActions } from './order-line-actions';
17
16
  import { BlockRendererProps } from './block-renderer-registry';
18
17
  import type { Block } from '../theme-block';
19
18
  import {
@@ -30,7 +29,7 @@ import {
30
29
  useOrderActionRules
31
30
  } from '../utils/order-action-rules';
32
31
 
33
- type OrderItemLike = {
32
+ interface OrderItemLike {
34
33
  id?: number;
35
34
  price?: string;
36
35
  quantity?: string | number;
@@ -52,7 +51,7 @@ type OrderItemLike = {
52
51
  absolute_url?: string;
53
52
  attributes?: Record<string, unknown>;
54
53
  } | null;
55
- };
54
+ }
56
55
 
57
56
  interface AddressLike {
58
57
  first_name?: string;
@@ -431,7 +430,7 @@ const formatAddress = (address: AddressLike | null | undefined): string[] => {
431
430
  * currencyText, currencyPosition.
432
431
  * Parts: detail, header, number, date, header-total, status,
433
432
  * invoice-link, cancel-link, return-link, list, item, image, name, quantity,
434
- * color, size, item-status, item-tracking, item-return-code, item-actions,
433
+ * color, size, item-status, item-tracking, item-return-code,
435
434
  * item-return-method, item-return-carrier, item-pickup-date, price, shipment,
436
435
  * delivery, tracking, addresses, address, address-label, totals, total-row,
437
436
  * total-label, total-value, grand-total, payment-info, payment-info-label.
@@ -1132,21 +1131,6 @@ const OrderDetailBlock = ({
1132
1131
  showPickupDate && returnDetails.pickupDate
1133
1132
  ? formatPickupDate(returnDetails.pickupDate, locale)
1134
1133
  : '';
1135
- const lineActions = renderOrderLineActions({
1136
- order,
1137
- line,
1138
- isDesigner
1139
- });
1140
- const showLineMeta =
1141
- showShipmentStatus &&
1142
- Boolean(
1143
- item.status?.label ||
1144
- trackingValue ||
1145
- returnCodeValue ||
1146
- returnMethodValue ||
1147
- returnCarrierValue ||
1148
- pickupDateValue
1149
- );
1150
1134
 
1151
1135
  return (
1152
1136
  <li
@@ -1262,100 +1246,98 @@ const OrderDetailBlock = ({
1262
1246
  </div>
1263
1247
  </div>
1264
1248
 
1265
- {(showLineMeta || lineActions) && (
1266
- <div
1267
- style={{
1268
- display: 'flex',
1269
- flexDirection: 'column',
1270
- alignItems: isMobile ? 'flex-start' : 'flex-end',
1271
- textAlign: isMobile ? 'left' : 'right',
1272
- gap: '4px',
1273
- flex: isMobile ? '1 1 100%' : '0 0 auto'
1274
- }}
1275
- >
1276
- {lineActions && (
1277
- <div
1278
- data-order-detail-item-actions=""
1279
- {...partAttrs('item-actions')}
1280
- >
1281
- {lineActions}
1282
- </div>
1283
- )}
1284
- {showLineMeta && item.status?.label && (
1285
- <span
1286
- data-order-detail-item-status=""
1287
- data-order-status-value={item.status.value}
1288
- {...partAttrs('item-status')}
1289
- style={{ display: 'inline-block' }}
1290
- >
1291
- {item.status.label}
1292
- </span>
1293
- )}
1294
- {/* Label and value in their own spans, so a
1295
- composition can colour them apart. */}
1296
- {showLineMeta && returnMethodValue && (
1297
- <span
1298
- data-order-detail-item-return-method=""
1299
- {...partAttrs('item-return-method')}
1300
- style={{ opacity: 0.8 }}
1301
- >
1302
- <span data-label="">{returnMethodLabel}</span>{' '}
1303
- <span data-value="">{returnMethodValue}</span>
1304
- </span>
1305
- )}
1306
- {showLineMeta && returnCarrierValue && (
1307
- <span
1308
- data-order-detail-item-return-carrier=""
1309
- {...partAttrs('item-return-carrier')}
1310
- style={{ opacity: 0.8 }}
1311
- >
1312
- <span data-label="">{returnCarrierLabel}</span>{' '}
1313
- <span data-value="">{returnCarrierValue}</span>
1314
- </span>
1315
- )}
1316
- {showLineMeta && pickupDateValue && (
1317
- <span
1318
- data-order-detail-item-pickup-date=""
1319
- {...partAttrs('item-pickup-date')}
1320
- style={{ opacity: 0.8 }}
1321
- >
1322
- <span data-label="">{pickupDateLabel}</span>{' '}
1323
- <span data-value="">{pickupDateValue}</span>
1324
- </span>
1325
- )}
1326
- {showLineMeta && trackingValue && (
1327
- <span
1328
- data-order-detail-item-tracking=""
1329
- {...partAttrs('item-tracking')}
1330
- style={{ opacity: 0.8 }}
1331
- >
1332
- {lineTrackingLabel}{' '}
1333
- {item.tracking_url ? (
1334
- <a
1335
- href={isDesigner ? '#' : item.tracking_url}
1336
- onClick={guard}
1337
- target={isDesigner ? undefined : '_blank'}
1338
- rel="noreferrer"
1339
- style={{ color: 'inherit' }}
1340
- >
1341
- {item.tracking_number || item.tracking_url}
1342
- </a>
1343
- ) : (
1344
- trackingValue
1345
- )}
1346
- </span>
1347
- )}
1348
- {showLineMeta && returnCodeValue && (
1349
- <span
1350
- data-order-detail-item-return-code=""
1351
- {...partAttrs('item-return-code')}
1352
- style={{ opacity: 0.8 }}
1353
- >
1354
- {returnCodeLabel} {returnCodeValue}
1355
- </span>
1356
- )}
1357
- </div>
1358
- )}
1249
+ {showShipmentStatus &&
1250
+ (item.status?.label ||
1251
+ trackingValue ||
1252
+ returnCodeValue ||
1253
+ returnMethodValue ||
1254
+ returnCarrierValue ||
1255
+ pickupDateValue) && (
1256
+ <div
1257
+ style={{
1258
+ display: 'flex',
1259
+ flexDirection: 'column',
1260
+ alignItems: isMobile ? 'flex-start' : 'flex-end',
1261
+ textAlign: isMobile ? 'left' : 'right',
1262
+ gap: '4px',
1263
+ flex: isMobile ? '1 1 100%' : '0 0 auto'
1264
+ }}
1265
+ >
1266
+ {item.status?.label && (
1267
+ <span
1268
+ data-order-detail-item-status=""
1269
+ data-order-status-value={item.status.value}
1270
+ {...partAttrs('item-status')}
1271
+ style={{ display: 'inline-block' }}
1272
+ >
1273
+ {item.status.label}
1274
+ </span>
1275
+ )}
1276
+ {/* Label and value in their own spans, so a
1277
+ composition can colour them apart. */}
1278
+ {returnMethodValue && (
1279
+ <span
1280
+ data-order-detail-item-return-method=""
1281
+ {...partAttrs('item-return-method')}
1282
+ style={{ opacity: 0.8 }}
1283
+ >
1284
+ <span data-label="">{returnMethodLabel}</span>{' '}
1285
+ <span data-value="">{returnMethodValue}</span>
1286
+ </span>
1287
+ )}
1288
+ {returnCarrierValue && (
1289
+ <span
1290
+ data-order-detail-item-return-carrier=""
1291
+ {...partAttrs('item-return-carrier')}
1292
+ style={{ opacity: 0.8 }}
1293
+ >
1294
+ <span data-label="">{returnCarrierLabel}</span>{' '}
1295
+ <span data-value="">{returnCarrierValue}</span>
1296
+ </span>
1297
+ )}
1298
+ {pickupDateValue && (
1299
+ <span
1300
+ data-order-detail-item-pickup-date=""
1301
+ {...partAttrs('item-pickup-date')}
1302
+ style={{ opacity: 0.8 }}
1303
+ >
1304
+ <span data-label="">{pickupDateLabel}</span>{' '}
1305
+ <span data-value="">{pickupDateValue}</span>
1306
+ </span>
1307
+ )}
1308
+ {trackingValue && (
1309
+ <span
1310
+ data-order-detail-item-tracking=""
1311
+ {...partAttrs('item-tracking')}
1312
+ style={{ opacity: 0.8 }}
1313
+ >
1314
+ {lineTrackingLabel}{' '}
1315
+ {item.tracking_url ? (
1316
+ <a
1317
+ href={isDesigner ? '#' : item.tracking_url}
1318
+ onClick={guard}
1319
+ target={isDesigner ? undefined : '_blank'}
1320
+ rel="noreferrer"
1321
+ style={{ color: 'inherit' }}
1322
+ >
1323
+ {item.tracking_number || item.tracking_url}
1324
+ </a>
1325
+ ) : (
1326
+ trackingValue
1327
+ )}
1328
+ </span>
1329
+ )}
1330
+ {returnCodeValue && (
1331
+ <span
1332
+ data-order-detail-item-return-code=""
1333
+ {...partAttrs('item-return-code')}
1334
+ style={{ opacity: 0.8 }}
1335
+ >
1336
+ {returnCodeLabel} {returnCodeValue}
1337
+ </span>
1338
+ )}
1339
+ </div>
1340
+ )}
1359
1341
  </div>
1360
1342
  </li>
1361
1343
  );
@@ -7,7 +7,6 @@ import { getInnerRootInlineStyles, partAttrs } from '../utils';
7
7
  import { useThemeSettingsContext } from '../theme-settings-context';
8
8
  import { useLocalizeHref } from '../hooks/use-localize-href';
9
9
  import { localizeHtmlLinks } from '../utils/localize-href';
10
- import { useValueLocale } from '../utils/localized-value';
11
10
  // Rich-text element defaults from the theme settings.
12
11
  import {
13
12
  THEME_RICH_TEXT_ATTRIBUTE,
@@ -20,7 +19,7 @@ const TextBlock = ({
20
19
  }: BlockRendererProps) => {
21
20
  const { locale } = useLocalization();
22
21
  const localize = useLocalizeHref();
23
- const { defaultLocale } = useValueLocale();
22
+ const defaultLocale = process.env.NEXT_PUBLIC_DEFAULT_LOCALE || 'en';
24
23
  const themeSettings = useThemeSettingsContext();
25
24
 
26
25
  const getLocalizedContent = (): string => {
@@ -36,10 +35,7 @@ const TextBlock = ({
36
35
 
37
36
  if (typeof value === 'object' && value !== null) {
38
37
  return (
39
- value[locale] ||
40
- (defaultLocale && value[defaultLocale]) ||
41
- Object.values(value)[0] ||
42
- ''
38
+ value[locale] || value[defaultLocale] || Object.values(value)[0] || ''
43
39
  );
44
40
  }
45
41