Skip to content

Commit d9dc1eb

Browse files
committed
feat: pwa pd support lint
1 parent a71a90f commit d9dc1eb

File tree

14 files changed

+174
-632
lines changed

14 files changed

+174
-632
lines changed

packages/commerce-sdk-react/src/components/ShopperExperience/Component/index.test.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ describe('Component', () => {
9292
mockRegistry.getComponent.mockReturnValue(MockDynamicComponent)
9393
mockRegistry.getFallback.mockReturnValue(null)
9494

95-
render(<Component component={mockComponent} regionId="test-region" className="custom-class" />)
95+
render(
96+
<Component component={mockComponent} regionId="test-region" className="custom-class" />
97+
)
9698

9799
expect(receivedProps.title).toBe('Test Banner')
98100
expect(receivedProps.imageUrl).toBe('/test-image.jpg')
Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,48 @@
1-
/**
2-
* Copyright 2026 Salesforce, Inc.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
1+
/*
2+
* Copyright (c) 2026, Salesforce, Inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
156
*/
16-
import React, { type ReactElement, memo, Suspense } from 'react';
17-
import { registry } from '../registry';
18-
import type { ComponentDesignMetadata } from '@salesforce/storefront-next-runtime/design/react';
19-
import type { ComponentType } from '../types';
7+
import React, {type ReactElement, memo, Suspense} from 'react'
8+
import {registry} from '../registry'
9+
import type {ComponentDesignMetadata} from '@salesforce/storefront-next-runtime/design/react'
10+
import type {ComponentType} from '../types'
2011

2112
export interface ComponentProps {
22-
component: ComponentType;
23-
className?: string;
24-
regionId: string;
13+
component: ComponentType
14+
className?: string
15+
regionId: string
2516
}
2617

27-
28-
export const Component = memo(function Component({ component, className, regionId }: ComponentProps): ReactElement {
18+
export const Component = memo(function Component({
19+
component,
20+
className,
21+
regionId
22+
}: ComponentProps): ReactElement {
2923
// Get this component's data promise from context by its ID
30-
const FallbackComponent = registry.getFallback(component.typeId);
31-
const DynamicComponent = registry.getComponent(component.typeId);
24+
const FallbackComponent = registry.getFallback(component.typeId)
25+
const DynamicComponent = registry.getComponent(component.typeId)
3226

3327
if (!DynamicComponent) {
3428
// eslint-disable-next-line @typescript-eslint/only-throw-error
35-
throw registry.preload(component.typeId);
29+
throw registry.preload(component.typeId)
3630
}
3731

3832
const designMetadata: ComponentDesignMetadata = {
3933
name: component.designMetadata?.name,
4034
isFragment: false,
4135
isVisible: Boolean(component.visible),
4236
isLocalized: Boolean(component.localized),
43-
id: component.id,
44-
};
37+
id: component.id
38+
}
4539

4640
return (
47-
<Suspense fallback={FallbackComponent ? <FallbackComponent {...(component.data ?? {})} /> : <div />}>
41+
<Suspense
42+
fallback={
43+
FallbackComponent ? <FallbackComponent {...(component.data ?? {})} /> : <div />
44+
}
45+
>
4846
<DynamicComponent
4947
{...(component.data ?? {})}
5048
designMetadata={designMetadata}
@@ -54,5 +52,5 @@ export const Component = memo(function Component({ component, className, regionI
5452
regionId={regionId}
5553
/>
5654
</Suspense>
57-
);
58-
});
55+
)
56+
})

packages/commerce-sdk-react/src/components/ShopperExperience/Page/index.test.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ jest.mock('../Component', () => ({
2222
// Mock the RegionWrapper
2323
jest.mock('../Region/region-wrapper', () => ({
2424
RegionWrapper: ({children, className}: {children: React.ReactNode; className?: string}) => (
25-
<div className={`region ${className || ''}`}>
26-
{children}
27-
</div>
25+
<div className={`region ${className || ''}`}>{children}</div>
2826
)
2927
}))
3028

packages/commerce-sdk-react/src/components/ShopperExperience/Region/index.test.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ jest.mock('./region-wrapper', () => ({
2727
)
2828
}))
2929

30-
3130
describe('Region', () => {
3231
beforeEach(() => {
3332
jest.clearAllMocks()
@@ -108,7 +107,10 @@ describe('Region', () => {
108107
})
109108

110109
test('handles page with no regions', () => {
111-
const pageWithNoRegions = {id: 'test-page', regions: undefined} as unknown as PageWithDesignMetadata
110+
const pageWithNoRegions = {
111+
id: 'test-page',
112+
regions: undefined
113+
} as unknown as PageWithDesignMetadata
112114
const {container} = render(<Region page={pageWithNoRegions} regionId="main-region" />)
113115

114116
expect(container.firstChild).toBeNull()
@@ -150,9 +152,7 @@ describe('Region', () => {
150152
})
151153

152154
test('returns null when component region is not found and no errorElement', () => {
153-
const {container} = render(
154-
<Region component={mockComponent} regionId="non-existent" />
155-
)
155+
const {container} = render(<Region component={mockComponent} regionId="non-existent" />)
156156

157157
expect(container.firstChild).toBeNull()
158158
})
Lines changed: 70 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,81 @@
1-
/**
2-
* Copyright 2026 Salesforce, Inc.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
1+
/*
2+
* Copyright (c) 2026, Salesforce, Inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
156
*/
167
import React, {type ReactElement, type ReactNode, Suspense} from 'react'
17-
import { Component } from '../Component';
18-
import { RegionWrapper } from './region-wrapper';
19-
import type { ShopperExperience } from '@salesforce/storefront-next-runtime/scapi';
8+
import {Component} from '../Component'
9+
import {RegionWrapper} from './region-wrapper'
10+
import type {ShopperExperience} from '@salesforce/storefront-next-runtime/scapi'
2011
import {
2112
PageDesignerPageMetadataProvider,
22-
useRegionContext,
23-
} from '@salesforce/storefront-next-runtime/design/react/core';
13+
useRegionContext
14+
} from '@salesforce/storefront-next-runtime/design/react/core'
2415
import type {
2516
ComponentDecoratorProps,
26-
RegionDesignMetadata,
27-
} from '@salesforce/storefront-next-runtime/design/react';
17+
RegionDesignMetadata
18+
} from '@salesforce/storefront-next-runtime/design/react'
2819

29-
export type { RegionDesignMetadata };
20+
export type {RegionDesignMetadata}
3021

3122
export interface PageDesignMetadata {
32-
id: string;
33-
name: string;
34-
description?: string;
35-
archType?: 'controller' | 'headless';
36-
route?: string;
37-
supportedAspectTypes?: string[];
38-
regionDefinitions?: RegionDesignMetadata[];
23+
id: string
24+
name: string
25+
description?: string
26+
archType?: 'controller' | 'headless'
27+
route?: string
28+
supportedAspectTypes?: string[]
29+
regionDefinitions?: RegionDesignMetadata[]
3930
attributeDefinitionGroups?: {
40-
id: string;
41-
name?: string;
42-
description?: string;
43-
attributeDefinitions?: Record<string, unknown>[];
44-
}[];
31+
id: string
32+
name?: string
33+
description?: string
34+
attributeDefinitions?: Record<string, unknown>[]
35+
}[]
4536
}
4637

4738
export type PageDecoratorProps<TProps> = React.PropsWithChildren<
4839
{
49-
designMetadata?: PageDesignMetadata;
40+
designMetadata?: PageDesignMetadata
5041
} & TProps
51-
>;
42+
>
5243

5344
// Extended Page type with design metadata
5445
type PageWithDesignMetadata = PageDecoratorProps<ShopperExperience.schemas['Page']> & {
55-
componentData?: Record<string, Promise<unknown>>;
56-
};
46+
componentData?: Record<string, Promise<unknown>>
47+
}
5748

5849
// Props when rendering a page-level region
5950
interface PageRegionProps extends React.HTMLAttributes<HTMLDivElement> {
60-
page: PageWithDesignMetadata;
61-
component?: never;
62-
regionId: string;
63-
fallbackElement?: ReactNode;
64-
errorElement?: ReactNode;
51+
page: PageWithDesignMetadata
52+
component?: never
53+
regionId: string
54+
fallbackElement?: ReactNode
55+
errorElement?: ReactNode
6556
}
6657

67-
export type ComponentType = ComponentDecoratorProps<ShopperExperience.schemas['Component']>;
58+
export type ComponentType = ComponentDecoratorProps<ShopperExperience.schemas['Component']>
6859

6960
// Props when rendering a component-level region (nested)
7061
interface ComponentRegionProps extends React.HTMLAttributes<HTMLDivElement> {
71-
page?: never;
72-
component: ComponentType;
73-
regionId: string;
74-
fallbackElement?: ReactNode;
75-
errorElement?: ReactNode;
62+
page?: never
63+
component: ComponentType
64+
regionId: string
65+
fallbackElement?: ReactNode
66+
errorElement?: ReactNode
7667
}
7768

7869
// Discriminated union
79-
export type RegionProps = PageRegionProps | ComponentRegionProps;
70+
export type RegionProps = PageRegionProps | ComponentRegionProps
8071

8172
// Helper: Extract design metadata from region definition
8273
function getDesignMetadata(regionId: string, metadata?: RegionDesignMetadata) {
8374
return {
8475
id: regionId,
8576
componentTypeExclusions: metadata?.componentTypeExclusions ?? [],
86-
componentTypeInclusions: metadata?.componentTypeInclusions ?? [],
87-
};
77+
componentTypeInclusions: metadata?.componentTypeInclusions ?? []
78+
}
8879
}
8980

9081
// Helper: Render region wrapper with components
@@ -100,12 +91,20 @@ function renderRegionContent(
10091
region={region}
10192
className={className}
10293
designMetadata={getDesignMetadata(regionId, metadata)}
103-
{...rest}>
94+
{...rest}
95+
>
10496
{region.components?.map(
105-
(comp) => comp.id && <Component key={comp.id} component={comp as ComponentType} regionId={region.id} />
97+
(comp) =>
98+
comp.id && (
99+
<Component
100+
key={comp.id}
101+
component={comp as ComponentType}
102+
regionId={region.id}
103+
/>
104+
)
106105
)}
107106
</RegionWrapper>
108-
);
107+
)
109108
}
110109

111110
/**
@@ -142,39 +141,41 @@ function renderRegionContent(
142141
* regions that can contain multiple components managed through the Page Designer interface.
143142
*/
144143
export function Region(props: RegionProps): ReactElement | null {
145-
const { regionId, className = '', errorElement, fallbackElement = <div />, ...rest } = props;
146-
const regionContext = useRegionContext();
144+
const {regionId, className = '', errorElement, fallbackElement = <div />, ...rest} = props
145+
const regionContext = useRegionContext()
147146

148147
// COMPONENT MODE: Rendering a component-level region (nested)
149148
if (props.component !== undefined) {
150-
const region = props.component.regions?.find((r) => r.id === regionId);
149+
const region = props.component.regions?.find((r) => r.id === regionId)
151150
if (!region) {
152-
return errorElement ? <>{errorElement}</> : null;
151+
return errorElement ? <>{errorElement}</> : null
153152
}
154153

155-
const metadata = props.component.designMetadata?.regionDefinitions?.find((r) => r.id === regionId);
156-
return renderRegionContent(region, regionId, metadata, className, rest);
154+
const metadata = props.component.designMetadata?.regionDefinitions?.find(
155+
(r) => r.id === regionId
156+
)
157+
return renderRegionContent(region, regionId, metadata, className, rest)
157158
}
158159

159160
// PAGE MODE: Rendering a page-level region
160-
const page = props.page;
161-
const region = page?.regions?.find((r) => r.id === regionId);
161+
const page = props.page
162+
const region = page?.regions?.find((r) => r.id === regionId)
162163
if (!region) {
163-
return errorElement ? <>{errorElement}</> : null;
164+
return errorElement ? <>{errorElement}</> : null
164165
}
165166

166-
const metadata = page.designMetadata?.regionDefinitions?.find((r) => r.id === regionId);
167-
const { componentData: pageComponentData, ...pageData } = page;
167+
const metadata = page.designMetadata?.regionDefinitions?.find((r) => r.id === regionId)
168+
const {componentData: pageComponentData, ...pageData} = page
168169

169170
return (
170171
<Suspense fallback={fallbackElement}>
171172
{!regionContext && <PageDesignerPageMetadataProvider page={pageData} />}
172173
{renderRegionContent(region, regionId, metadata, className, rest)}
173174
</Suspense>
174-
);
175+
)
175176
}
176177

177178
export default Region
178179
// Re-export RegionWrapper for direct usage if needed
179-
export { RegionWrapper } from './region-wrapper';
180-
export type { RegionRendererProps } from './region-wrapper';
180+
export {RegionWrapper} from './region-wrapper'
181+
export type {RegionRendererProps} from './region-wrapper'

packages/commerce-sdk-react/src/components/ShopperExperience/Region/region-wrapper.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ describe('RegionWrapper', () => {
198198
<RegionWrapper
199199
region={mockRegion}
200200
data-custom="custom-value"
201-
aria-label="Test region">
201+
aria-label="Test region"
202+
>
202203
<div data-testid="child-content">Content</div>
203204
</RegionWrapper>
204205
)

0 commit comments

Comments
 (0)