Skip to content

Commit 683e469

Browse files
committed
feat(design): add component registry
feat(design): move design components out of sdk feat(design): add messaging api fix types upgrade typescript to support esm modules cleanup messenger on disconnect make scrolling isomorphic feat(design): add hover bounds events (#220) feat: refine connection logic for design layer feat(pd): add design context and wrapper @W-19432252 (#222) * feat(pd): add design context and wrapper feat(design): add more interactions and cleanup (#223) feat: drag and drop from palette @W-19284238 (#226) * feature(design): add drag and drop support for regions * rework drag and drop * add local dragging logic * add drop target styles * fix styles * fix decorator render order * feat: add unit test + build fixes * fix: styling * fix: nodeToTargetMap being empty + update tests * fix: add back region green border * fix: test + bad css merge * fix: region decorator tests --------- Co-authored-by: Steven Sojka <[email protected]> Feature/drag and drop move (#237) * feature(design): add drag and drop support for regions * rework drag and drop * add local dragging logic * add drop target styles * fix styles * fix decorator render order * polish move * get parent id from context update styles and add design tests
1 parent aef0edf commit 683e469

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+5679
-26
lines changed

.eslintrc.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,12 @@
8989
"plugin:@typescript-eslint/recommended-requiring-type-checking"
9090
],
9191
"rules": {
92-
"@typescript-eslint/explicit-module-boundary-types": "error"
92+
"@typescript-eslint/explicit-module-boundary-types": "error",
93+
"react/jsx-filename-extension": [1, { "extensions": [".tsx"] }],
94+
"no-use-before-define": "off",
95+
"import/prefer-default-export": "off",
96+
"@typescript-eslint/no-use-before-define": "error",
97+
"import/no-unresolved": "off"
9398
}
9499
},
95100
{

.github/workflows/test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ jobs:
3030
if: ${{ steps.cache-nodemodules.outputs.cache-hit != 'true' }}
3131
- run: yarn run renderTemplates
3232
- run: yarn build:lib
33+
- run: yarn build:design
3334
- run: yarn test
35+
- run: yarn test:design

babel.config.jest.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module.exports = {
1616
},
1717
],
1818
'@babel/typescript',
19+
'@babel/preset-react',
1920
],
2021
ignore: ['node_modules/**'],
2122
};

package.json

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,27 @@
109109
"import": "./lib/version.js",
110110
"require": "./lib/version.cjs.js"
111111
},
112-
"./package.json": "./package.json"
112+
"./package.json": "./package.json",
113+
"./design": {
114+
"import": {
115+
"default": "./lib/design/esm/index.js",
116+
"types": "./lib/design/esm/index.d.ts"
117+
},
118+
"require": {
119+
"default": "./lib/design/cjs/index.js",
120+
"types": "./lib/design/cjs/index.d.ts"
121+
}
122+
},
123+
"./design/react": {
124+
"import": {
125+
"default": "./lib/design/esm/react/index.js",
126+
"types": "./lib/design/esm/react/index.d.ts"
127+
},
128+
"require": {
129+
"default": "./lib/design/cjs/react/index.js",
130+
"types": "./lib/design/cjs/react/index.d.ts"
131+
}
132+
}
113133
},
114134
"main": "lib/index.cjs.js",
115135
"module": "lib/index.esm.js",
@@ -122,8 +142,10 @@
122142
"scripts": {
123143
"build": "react-scripts build",
124144
"build:lib": "ts-node --compiler-options '{\"module\": \"commonjs\", \"target\": \"ES6\" }' ./scripts/generateFileList.ts && rollup -c",
145+
"build:design": "tsc -p tsconfig.design.json && tsc -p tsconfig.design.json --module commonjs --outDir lib/design/cjs && yarn build:design:copy",
146+
"build:design:copy": "cpx 'src/design/**/*.css' lib/design/cjs && cpx 'src/design/**/*.css' lib/design/esm",
125147
"check:size": "npm-pack-all --output commerce-sdk-isomorphic-with-deps.tgz && bundlesize",
126-
"check:types": "tsc --noEmit",
148+
"check:types": "tsc -p tsconfig.sdk.json --noEmit",
127149
"ci": "rm -rf node_modules && yarn install",
128150
"clean": "rm -rf build lib src/lib commerce-sdk-isomorphic-with-deps.tgz",
129151
"depcheck": "depcheck",
@@ -135,14 +157,15 @@
135157
"fix:style": "yarn run lint:style -- --fix",
136158
"generateVersionTable": "ts-node --compiler-options '{\"module\": \"commonjs\", \"target\": \"ES6\" }' ./scripts/generateVersionTable.ts",
137159
"lint": "eslint --ext js,jsx,ts,tsx .",
138-
"lint:style": "stylelint ./src/",
160+
"lint:style": "stylelint ./src/**/*.css",
139161
"prepare": "snyk protect",
140162
"renderTemplates": "PACKAGE_VERSION=$(node -p \"require('./package.json').version\") ts-node --compiler-options '{\"module\": \"commonjs\", \"target\": \"ES6\" }' ./scripts/generate-oas.ts",
141163
"start": "HTTPS=true react-scripts start",
142164
"pretest": "yarn run lint && yarn run lint:style && depcheck && yarn run check:size",
143165
"test": "yarn run check:types && yarn run test:unit && CI=true yarn run test:react",
166+
"test:design": "jest --config src/design/jest.config.js src/design",
144167
"test:react": "react-scripts test --env=jest-environment-jsdom-sixteen src/environment --setupFilesAfterEnv ./src/test/setupTests.ts",
145-
"test:unit": "jest --coverage --testPathIgnorePatterns node_modules src/environment --silent",
168+
"test:unit": "jest --coverage --testPathIgnorePatterns node_modules src/design src/environment --silent",
146169
"updateApis": "ts-node --compiler-options '{\"module\": \"commonjs\", \"target\": \"ES6\" }' ./scripts/updateApis.ts && yarn diffApis"
147170
},
148171
"husky": {
@@ -203,6 +226,7 @@
203226
"@typescript-eslint/parser": "^4.33.0",
204227
"autoprefixer": "9.8.8",
205228
"bundlesize2": "^0.0.31",
229+
"cpx": "^1.5.0",
206230
"depcheck": "^1.4.3",
207231
"dotenv": "^16.0.3",
208232
"eslint": "^7.32.0",
@@ -242,6 +266,7 @@
242266
"stylelint": "13.13.1",
243267
"stylelint-config-standard": "20.0.0",
244268
"stylelint-order": "4.1.0",
269+
"ts-jest": "26.x.x",
245270
"ts-node": "9.1.1",
246271
"typedoc": "^0.17.7",
247272
"typedoc-plugin-external-module-map": "1.2.1",

rollup.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ const sharedPlugins = [
5353
ts({
5454
transpiler: 'babel',
5555
// Setting noEmit directly in the tsconfig triggers a react testing bug so we override it here
56-
tsconfig: resolvedConfig => ({...resolvedConfig, noEmit: false}),
56+
tsconfig: {
57+
fileName: 'tsconfig.sdk.json',
58+
hook: resolvedConfig => ({...resolvedConfig, noEmit: false}),
59+
},
5760
exclude: 'node_modules/**',
5861
}),
5962
babel({
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (c) 2025, salesforce.com, 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
6+
*/
7+
import {ComponentRegistry} from './componentRegistry';
8+
import {isDesignModeActive} from './modeDetection';
9+
10+
type TestComponent = {name: string};
11+
12+
jest.mock('./modeDetection', () => ({
13+
isDesignModeActive: jest.fn().mockReturnValue(true),
14+
}));
15+
16+
describe('design/componentRegistry', () => {
17+
let designMode = false;
18+
let registry: ComponentRegistry<TestComponent>;
19+
20+
beforeEach(() => {
21+
designMode = false;
22+
23+
(isDesignModeActive as jest.Mock).mockImplementation(() => designMode);
24+
25+
registry = new ComponentRegistry<TestComponent>({
26+
designDecorator: component => ({
27+
...component,
28+
name: `${component.name}-design`,
29+
}),
30+
});
31+
});
32+
33+
describe('when registering a component', () => {
34+
it('should set the component in the registry', () => {
35+
registry.registerComponent('test', {name: 'test'});
36+
expect(registry.getComponent('test')).toEqual({name: 'test'});
37+
});
38+
39+
describe('when getting a component in design mode', () => {
40+
beforeEach(() => {
41+
designMode = true;
42+
});
43+
44+
it('should get the decorated component', () => {
45+
registry.registerComponent('test', {name: 'test'});
46+
expect(registry.getComponent('test')).toEqual({
47+
name: 'test-design',
48+
});
49+
});
50+
});
51+
});
52+
53+
describe("when getting a component that doesn't exist in the registry", () => {
54+
it('should return null', () => {
55+
expect(registry.getComponent('blorg')).toBeNull();
56+
});
57+
});
58+
});

src/design/componentRegistry.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) 2025, salesforce.com, 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
6+
*/
7+
8+
import {isDesignModeActive} from './modeDetection';
9+
/**
10+
* A generic registry for managing components with support for design-time decoration.
11+
* This registry allows components to be registered and retrieved in different modes,
12+
* with optional decoration for design-time usage.
13+
*
14+
* @template TComponent - The type of components stored in this registry
15+
* @example
16+
* import type React from 'react';
17+
* import pkg from 'commerce-sdk-isomorphic';
18+
* const { design: { ComponentRegistry, createReactDesignDecorator } } = pkg;
19+
*
20+
* const registry = new ComponentRegistry<React.Component>({
21+
* designDecorator: createReactDesignDecorator(),
22+
* });
23+
*
24+
* registry.registerComponent('commerce/productList', ProductListComponent);
25+
*
26+
* const ProductList = registry.getComponent('commerce/productList');
27+
*
28+
* // Get the component in design mode - the component will be decorated
29+
* const ProductList = registry.getComponent('commerce/productList');
30+
*/
31+
export class ComponentRegistry<TComponent> {
32+
private readonly registry = new Map<string, TComponent>();
33+
34+
private readonly designDecorator: (component: TComponent) => TComponent;
35+
36+
/**
37+
* @param options - Configuration options for the registry
38+
* @param options.designDecorator - Optional function to decorate components when retrieved in design mode.
39+
* If not provided, components are returned unchanged in design mode.
40+
*/
41+
constructor({
42+
designDecorator = component => component,
43+
}: {
44+
designDecorator?: (component: TComponent) => TComponent;
45+
} = {}) {
46+
this.designDecorator = designDecorator;
47+
}
48+
49+
/**
50+
* Registers a component in the registry with the specified name.
51+
* If a component with the same name already exists, it will be overwritten.
52+
*
53+
* @param name - The unique identifier for the component
54+
* @param component - The component to register
55+
*/
56+
registerComponent(name: string, component: TComponent): void {
57+
this.registry.set(name, component);
58+
}
59+
60+
/**
61+
* Retrieves a component from the registry by its identifier.
62+
* The component may be decorated based on the specified mode.
63+
*
64+
* @param id - The identifier of the component to retrieve
65+
* @param options - Options for component retrieval
66+
* @param options.mode - The mode in which to retrieve the component. Defaults to 'runtime'.
67+
* @returns The component if found, null otherwise. In design mode, the component will be decorated.
68+
*/
69+
getComponent(id: string): TComponent | null {
70+
const component = this.registry.get(id) ?? null;
71+
72+
return component && isDesignModeActive()
73+
? this.designDecorator(component)
74+
: component;
75+
}
76+
}

src/design/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* Copyright (c) 2025, salesforce.com, 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
6+
*/
7+
export * from './componentRegistry';
8+
export * from './messaging-api/index';
9+
export {isDesignModeActive, isPreviewModeActive} from './modeDetection';

src/design/jest.config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/* eslint-disable */
2+
module.exports = {
3+
displayName: 'design',
4+
testEnvironment: 'jsdom',
5+
passWithNoTests: true,
6+
transform: {
7+
'^.+\\.[tj]sx?$': ['ts-jest', {tsconfig: '<rootDir>/tsconfig.json'}]
8+
},
9+
moduleNameMapper: {
10+
'\\.(css|less|scss|sass)$': '<rootDir>/test/styleMock.js',
11+
},
12+
moduleFileExtensions: ['ts', 'js', 'html', 'tsx', 'jsx'],
13+
};

0 commit comments

Comments
 (0)