@servicetitan/ko-bridge 23.1.0 → 23.2.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.
@@ -1,14 +1,8 @@
1
+ /// <reference types="knockout" />
2
+ declare type KO = typeof import('knockout');
1
3
  export declare const koBindingHandlers: {
2
- reactUncontrolled: (ko: any) => {
3
- init: (el: Element, valueAccessor: any, _1: any, _2: any, bindingContext: any) => {
4
- controlsDescendantBindings: boolean;
5
- };
6
- };
7
- react: (ko: any) => {
8
- init: (el: any) => {
9
- controlsDescendantBindings: boolean;
10
- };
11
- update: (el: Element, valueAccessor: any, allBindings: any, viewModel: any) => void;
12
- };
4
+ reactUncontrolled: (ko: KO) => KnockoutBindingHandler;
5
+ react: (ko: KO) => KnockoutBindingHandler;
13
6
  };
7
+ export {};
14
8
  //# sourceMappingURL=ko-binding-handlers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ko-binding-handlers.d.ts","sourceRoot":"","sources":["../src/ko-binding-handlers.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB;4BAEF,GAAG;mBACZ,OAAO,iBAAiB,GAAG,MAAM,GAAG,MAAM,GAAG,kBAAkB,GAAG;;;;gBAYrE,GAAG;mBACA,GAAG;;;qBAMD,OAAO,iBAAiB,GAAG,eAAe,GAAG,aAAa,GAAG;;CAOjF,CAAC"}
1
+ {"version":3,"file":"ko-binding-handlers.d.ts","sourceRoot":"","sources":["../src/ko-binding-handlers.tsx"],"names":[],"mappings":";AAIA,aAAK,EAAE,GAAG,cAAc,UAAU,CAAC,CAAC;AAapC,eAAO,MAAM,iBAAiB;4BAEF,EAAE,KAAG,sBAAsB;gBAsBvC,EAAE,KAAG,sBAAsB;CAoB1C,CAAC"}
@@ -1,34 +1,72 @@
1
- import { createElement } from 'react';
2
- // eslint-disable-next-line react/no-deprecated
3
- import { render, unmountComponentAtNode } from 'react-dom';
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment, useEffect } from 'react';
3
+ import ReactDOM from 'react-dom';
4
+ let createRoot;
5
+ try {
6
+ /**
7
+ * Note, the webpack.IgnorePlugin must be configured to ignore this dependency.
8
+ * @see {@link file://./../../startup/src/webpack/configs/plugins/ignore-plugin/ignore-plugin.ts}
9
+ */
10
+ createRoot = require('react-dom/client').createRoot;
11
+ }
12
+ catch (e) {
13
+ // ignore
14
+ }
4
15
  export const koBindingHandlers = {
5
16
  // reactUncontrolled
6
17
  reactUncontrolled: (ko) => ({
7
- init: (el, valueAccessor, _1, _2, bindingContext) => {
8
- const component = ko.unwrap(valueAccessor());
9
- ko.utils.domNodeDisposal.addDisposeCallback(el, () => {
10
- unmountComponentAtNode(el);
11
- });
12
- render(createElement(component), el, () => {
13
- ko.applyBindingsToDescendants(bindingContext, el);
14
- });
18
+ init: (el, valueAccessor, allBindings, _viewModel, bindingContext) => {
19
+ initRoot(ko, el, allBindings);
20
+ const Component = ko.unwrap(valueAccessor());
21
+ render(ko, el, _jsxs(Fragment, { children: [_jsx(Component, {}), _jsx(ApplyBindings, { bindingContext: bindingContext, el: el, ko: ko })] }));
15
22
  return { controlsDescendantBindings: true };
16
23
  },
17
24
  }),
18
25
  // React component renderer for knockout
19
26
  react: (ko) => ({
20
- init: (el) => {
21
- ko.utils.domNodeDisposal.addDisposeCallback(el, () => {
22
- unmountComponentAtNode(el);
23
- });
27
+ init: (el, _valueAccessor, allBindings) => {
28
+ initRoot(ko, el, allBindings);
24
29
  return { controlsDescendantBindings: true }; // important
25
30
  },
26
31
  update: (el, valueAccessor, allBindings, viewModel) => {
27
- const component = ko.unwrap(valueAccessor());
28
- let props = allBindings.get('props');
29
- props = props ? props : viewModel ? viewModel : null;
30
- render(createElement(component, props), el);
32
+ const Component = ko.unwrap(valueAccessor());
33
+ const props = allBindings.get('props') || viewModel || null;
34
+ render(ko, el, _jsx(Component, Object.assign({}, props)));
31
35
  },
32
36
  }),
33
37
  };
38
+ function ApplyBindings({ bindingContext, el, ko }) {
39
+ // eslint-disable-next-line react-hooks/exhaustive-deps
40
+ useEffect(() => ko.applyBindingsToDescendants(bindingContext, el), []);
41
+ return null;
42
+ }
43
+ const ROOT_KEY = 'ko-binding-handler-root';
44
+ function disposalCallback(el, ko) {
45
+ const root = ko.utils.domData.get(el, ROOT_KEY);
46
+ if (root) {
47
+ return () => {
48
+ root.unmount();
49
+ ko.utils.domData.clear(el);
50
+ };
51
+ }
52
+ // eslint-disable-next-line react/no-deprecated
53
+ return () => ReactDOM.unmountComponentAtNode(el);
54
+ }
55
+ function initRoot(ko, el, allBindings) {
56
+ if (createRoot && allBindings.get('legacyRoot') !== true) {
57
+ const root = createRoot(el);
58
+ ko.utils.domData.set(el, ROOT_KEY, root);
59
+ }
60
+ ko.utils.domNodeDisposal.addDisposeCallback(el, disposalCallback(el, ko));
61
+ }
62
+ function render(ko, container, element) {
63
+ const root = ko.utils.domData.get(container, ROOT_KEY);
64
+ if (root) {
65
+ root.render(element);
66
+ }
67
+ else {
68
+ // eslint-disable-next-line react/no-deprecated
69
+ ReactDOM.render(element, container);
70
+ }
71
+ }
34
72
  //# sourceMappingURL=ko-binding-handlers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ko-binding-handlers.js","sourceRoot":"","sources":["../src/ko-binding-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,+CAA+C;AAC/C,OAAO,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAE3D,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC7B,oBAAoB;IACpB,iBAAiB,EAAE,CAAC,EAAO,EAAE,EAAE,CAAC,CAAC;QAC7B,IAAI,EAAE,CAAC,EAAW,EAAE,aAAkB,EAAE,EAAO,EAAE,EAAO,EAAE,cAAmB,EAAE,EAAE;YAC7E,MAAM,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;YAC7C,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,kBAAkB,CAAC,EAAE,EAAE,GAAG,EAAE;gBACjD,sBAAsB,CAAC,EAAE,CAAC,CAAC;YAC/B,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE;gBACtC,EAAE,CAAC,0BAA0B,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;YACtD,CAAC,CAAC,CAAC;YACH,OAAO,EAAE,0BAA0B,EAAE,IAAI,EAAE,CAAC;QAChD,CAAC;KACJ,CAAC;IACF,wCAAwC;IACxC,KAAK,EAAE,CAAC,EAAO,EAAE,EAAE,CAAC,CAAC;QACjB,IAAI,EAAE,CAAC,EAAO,EAAE,EAAE;YACd,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,kBAAkB,CAAC,EAAE,EAAE,GAAG,EAAE;gBACjD,sBAAsB,CAAC,EAAE,CAAC,CAAC;YAC/B,CAAC,CAAC,CAAC;YACH,OAAO,EAAE,0BAA0B,EAAE,IAAI,EAAE,CAAC,CAAC,YAAY;QAC7D,CAAC;QACD,MAAM,EAAE,CAAC,EAAW,EAAE,aAAkB,EAAE,WAAgB,EAAE,SAAc,EAAE,EAAE;YAC1E,MAAM,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;YAC7C,IAAI,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACrC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;YACrD,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QAChD,CAAC;KACJ,CAAC;CACL,CAAC"}
1
+ {"version":3,"file":"ko-binding-handlers.js","sourceRoot":"","sources":["../src/ko-binding-handlers.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/ko-bridge",
3
- "version": "23.1.0",
3
+ "version": "23.2.0",
4
4
  "description": "",
5
5
  "homepage": "https://docs.st.dev/docs/frontend/ko-bridge",
6
6
  "repository": {
@@ -16,6 +16,8 @@
16
16
  "src"
17
17
  ],
18
18
  "devDependencies": {
19
+ "@testing-library/jest-dom": "^6.4.2",
20
+ "@testing-library/react": "^14.2.1",
19
21
  "@types/knockout": "~3.4.71",
20
22
  "@types/react": "~18.2.55",
21
23
  "@types/react-dom": "~18.2.19",
@@ -36,5 +38,5 @@
36
38
  "cli": {
37
39
  "webpack": false
38
40
  },
39
- "gitHead": "7267270e54cdb3a2926f61feb4939d057e82adb3"
41
+ "gitHead": "4b72bab5ab6cb52a688d9329c7fbaa702ae6780c"
40
42
  }
@@ -0,0 +1,218 @@
1
+ import '@testing-library/jest-dom';
2
+ import { act, screen, waitFor } from '@testing-library/react';
3
+ import ko from 'knockout';
4
+ import { ComponentType } from 'react';
5
+ import ReactDOM from 'react-dom';
6
+
7
+ import { koBindingHandlers } from '../ko-binding-handlers';
8
+
9
+ describe('[ko-bridge] koBindingHandlers', () => {
10
+ let el: HTMLElement;
11
+ let mockAllBindings: any;
12
+ let mockBindingContext: any;
13
+ let valueAccessor: () => ComponentType;
14
+ let viewModel: Record<string, any>;
15
+
16
+ interface MockButtonProps {
17
+ title?: string;
18
+ }
19
+
20
+ function MockButton({ title }: Readonly<MockButtonProps>) {
21
+ return <button>{title ?? 'MockButton'}</button>;
22
+ }
23
+
24
+ function itRendersComponent(subject: Function) {
25
+ test('renders Component', async () => {
26
+ subject();
27
+
28
+ await waitFor(() => {
29
+ expect(screen.getByRole('button', { name: 'MockButton' })).toBeInTheDocument();
30
+ });
31
+ });
32
+ }
33
+
34
+ function itRegistersUnmountCallback(subject: Function) {
35
+ test('registers callback that unmounts Component', () => {
36
+ const callbackSpy = jest.spyOn(ko.utils.domNodeDisposal, 'addDisposeCallback');
37
+ subject();
38
+
39
+ const root = ko.utils.domData.get(el, 'ko-binding-handler-root'); // Beware: this relies on an implementation detail
40
+ const unmountSpy = jest.spyOn(root, 'unmount');
41
+
42
+ const [, disposeCallback] = callbackSpy.mock.calls[0];
43
+ act(() => disposeCallback());
44
+
45
+ expect(unmountSpy).toHaveBeenCalled();
46
+ });
47
+ }
48
+
49
+ function itRendersLegacyComponent(subject: Function) {
50
+ // eslint-disable-next-line react/no-deprecated
51
+ const originalRender = ReactDOM.render;
52
+
53
+ test('uses legacy API to render Component', async () => {
54
+ const renderSpy = jest
55
+ .spyOn(ReactDOM, 'render')
56
+ .mockImplementation((...args: Parameters<ReactDOM.Renderer>) =>
57
+ originalRender(...args)
58
+ );
59
+
60
+ subject();
61
+
62
+ await waitFor(() => {
63
+ expect(screen.getByRole('button', { name: 'MockButton' })).toBeInTheDocument();
64
+ });
65
+
66
+ expect(renderSpy).toHaveBeenCalled();
67
+ });
68
+ }
69
+
70
+ function itRegistersLegacyUnmountCallback(subject: Function) {
71
+ test('uses legacy API to unmount Component', () => {
72
+ const callbackSpy = jest.spyOn(ko.utils.domNodeDisposal, 'addDisposeCallback');
73
+
74
+ subject();
75
+
76
+ jest.spyOn(ReactDOM, 'unmountComponentAtNode');
77
+
78
+ const [, disposeCallback] = callbackSpy.mock.calls[0];
79
+ disposeCallback();
80
+
81
+ // eslint-disable-next-line react/no-deprecated
82
+ expect(ReactDOM.unmountComponentAtNode).toHaveBeenCalledWith(el);
83
+ });
84
+ }
85
+
86
+ beforeEach(() => {
87
+ jest.clearAllMocks();
88
+
89
+ el = document.createElement('div');
90
+ document.body.appendChild(el);
91
+
92
+ mockAllBindings = new Map();
93
+ mockBindingContext = {};
94
+ valueAccessor = () => MockButton;
95
+ viewModel = {};
96
+ });
97
+
98
+ afterEach(() => {
99
+ el.remove();
100
+ });
101
+
102
+ function init(handler: KnockoutBindingHandler) {
103
+ let result: ReturnType<Required<KnockoutBindingHandler>['init']> = undefined;
104
+
105
+ act(() => {
106
+ result = handler.init!(
107
+ el,
108
+ valueAccessor,
109
+ mockAllBindings,
110
+ viewModel,
111
+ mockBindingContext
112
+ );
113
+ });
114
+
115
+ return result;
116
+ }
117
+
118
+ function update(handler: KnockoutBindingHandler) {
119
+ let result: ReturnType<Required<KnockoutBindingHandler>['update']> = undefined;
120
+
121
+ act(() => {
122
+ result = handler.update!(
123
+ el,
124
+ valueAccessor,
125
+ mockAllBindings,
126
+ viewModel,
127
+ mockBindingContext
128
+ );
129
+ });
130
+
131
+ return result;
132
+ }
133
+
134
+ describe('reactUncontrolled', () => {
135
+ const handler: KnockoutBindingHandler = koBindingHandlers.reactUncontrolled(ko);
136
+
137
+ describe('init', () => {
138
+ const subject = () => init(handler);
139
+
140
+ itRendersComponent(subject);
141
+ itRegistersUnmountCallback(subject);
142
+
143
+ test('applies bindings to descendants', async () => {
144
+ jest.spyOn(ko, 'applyBindingsToDescendants');
145
+
146
+ expect(subject()).toEqual({ controlsDescendantBindings: true });
147
+ await waitFor(() =>
148
+ expect(ko.applyBindingsToDescendants).toHaveBeenCalledWith(
149
+ mockBindingContext,
150
+ el
151
+ )
152
+ );
153
+ });
154
+
155
+ describe('with {legacyRoot: true}', () => {
156
+ beforeEach(() => mockAllBindings.set('legacyRoot', true));
157
+
158
+ itRendersLegacyComponent(subject);
159
+ itRegistersLegacyUnmountCallback(subject);
160
+ });
161
+ });
162
+ });
163
+
164
+ describe('react', () => {
165
+ const handler: KnockoutBindingHandler = koBindingHandlers.react(ko);
166
+
167
+ describe('init', () => {
168
+ const subject = () => init(handler);
169
+
170
+ itRegistersUnmountCallback(subject);
171
+
172
+ describe('with {legacyRoot: true}', () => {
173
+ beforeEach(() => mockAllBindings.set('legacyRoot', true));
174
+
175
+ itRegistersLegacyUnmountCallback(subject);
176
+ });
177
+ });
178
+
179
+ describe('update', () => {
180
+ const subject = () => {
181
+ init(handler);
182
+ update(handler);
183
+ };
184
+
185
+ itRendersComponent(subject);
186
+
187
+ describe('with {legacyRoot: true}', () => {
188
+ beforeEach(() => mockAllBindings.set('legacyRoot', true));
189
+
190
+ itRendersLegacyComponent(subject);
191
+ });
192
+
193
+ describe('with props', () => {
194
+ const props = { title: 'Foo' };
195
+
196
+ beforeEach(() => mockAllBindings.set('props', props));
197
+
198
+ test('renders Component with props', () => {
199
+ subject();
200
+
201
+ expect(screen.getByRole('button', { name: props.title })).toBeInTheDocument();
202
+ });
203
+ });
204
+
205
+ describe('with viewModel', () => {
206
+ beforeEach(() => (viewModel = { title: 'Bar' }));
207
+
208
+ test('renders Component with viewModel as props', () => {
209
+ subject();
210
+
211
+ expect(
212
+ screen.getByRole('button', { name: viewModel.title })
213
+ ).toBeInTheDocument();
214
+ });
215
+ });
216
+ });
217
+ });
218
+ });
@@ -0,0 +1,100 @@
1
+ import { ComponentType, Fragment, ReactElement, useEffect } from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import type ReactDOMClient from 'react-dom/client';
4
+
5
+ type KO = typeof import('knockout');
6
+
7
+ let createRoot: typeof ReactDOMClient.createRoot;
8
+ try {
9
+ /**
10
+ * Note, the webpack.IgnorePlugin must be configured to ignore this dependency.
11
+ * @see {@link file://./../../startup/src/webpack/configs/plugins/ignore-plugin/ignore-plugin.ts}
12
+ */
13
+ createRoot = require('react-dom/client').createRoot;
14
+ } catch (e) {
15
+ // ignore
16
+ }
17
+
18
+ export const koBindingHandlers = {
19
+ // reactUncontrolled
20
+ reactUncontrolled: (ko: KO): KnockoutBindingHandler => ({
21
+ init: (
22
+ el: Element,
23
+ valueAccessor: () => any,
24
+ allBindings: KnockoutAllBindingsAccessor,
25
+ _viewModel: any,
26
+ bindingContext: KnockoutBindingContext
27
+ ) => {
28
+ initRoot(ko, el, allBindings);
29
+ const Component = ko.unwrap<ComponentType>(valueAccessor());
30
+ render(
31
+ ko,
32
+ el,
33
+ <Fragment>
34
+ <Component />
35
+ <ApplyBindings bindingContext={bindingContext} el={el} ko={ko} />
36
+ </Fragment>
37
+ );
38
+ return { controlsDescendantBindings: true };
39
+ },
40
+ }),
41
+ // React component renderer for knockout
42
+ react: (ko: KO): KnockoutBindingHandler => ({
43
+ init: (
44
+ el: Element,
45
+ _valueAccessor: () => any,
46
+ allBindings: KnockoutAllBindingsAccessor
47
+ ) => {
48
+ initRoot(ko, el, allBindings);
49
+ return { controlsDescendantBindings: true }; // important
50
+ },
51
+ update: (
52
+ el: Element,
53
+ valueAccessor: () => any,
54
+ allBindings: KnockoutAllBindingsAccessor,
55
+ viewModel: any
56
+ ) => {
57
+ const Component = ko.unwrap<ComponentType>(valueAccessor());
58
+ const props = allBindings.get('props') || viewModel || null;
59
+ render(ko, el, <Component {...props} />);
60
+ },
61
+ }),
62
+ };
63
+
64
+ function ApplyBindings({ bindingContext, el, ko }: { bindingContext: any; el: Element; ko: KO }) {
65
+ // eslint-disable-next-line react-hooks/exhaustive-deps
66
+ useEffect(() => ko.applyBindingsToDescendants(bindingContext, el), []);
67
+ return null;
68
+ }
69
+
70
+ const ROOT_KEY = 'ko-binding-handler-root';
71
+
72
+ function disposalCallback(el: Element, ko: KO) {
73
+ const root = ko.utils.domData.get(el, ROOT_KEY);
74
+ if (root) {
75
+ return () => {
76
+ root.unmount();
77
+ ko.utils.domData.clear(el);
78
+ };
79
+ }
80
+ // eslint-disable-next-line react/no-deprecated
81
+ return () => ReactDOM.unmountComponentAtNode(el);
82
+ }
83
+ function initRoot(ko: KO, el: Element, allBindings: KnockoutAllBindingsAccessor) {
84
+ if (createRoot && allBindings.get('legacyRoot') !== true) {
85
+ const root = createRoot(el);
86
+ ko.utils.domData.set(el, ROOT_KEY, root);
87
+ }
88
+
89
+ ko.utils.domNodeDisposal.addDisposeCallback(el, disposalCallback(el, ko));
90
+ }
91
+
92
+ function render(ko: KO, container: Element, element: ReactElement) {
93
+ const root = ko.utils.domData.get(container, ROOT_KEY);
94
+ if (root) {
95
+ root.render(element);
96
+ } else {
97
+ // eslint-disable-next-line react/no-deprecated
98
+ ReactDOM.render(element, container);
99
+ }
100
+ }
@@ -1,34 +0,0 @@
1
- import { createElement } from 'react';
2
- // eslint-disable-next-line react/no-deprecated
3
- import { render, unmountComponentAtNode } from 'react-dom';
4
-
5
- export const koBindingHandlers = {
6
- // reactUncontrolled
7
- reactUncontrolled: (ko: any) => ({
8
- init: (el: Element, valueAccessor: any, _1: any, _2: any, bindingContext: any) => {
9
- const component = ko.unwrap(valueAccessor());
10
- ko.utils.domNodeDisposal.addDisposeCallback(el, () => {
11
- unmountComponentAtNode(el);
12
- });
13
- render(createElement(component), el, () => {
14
- ko.applyBindingsToDescendants(bindingContext, el);
15
- });
16
- return { controlsDescendantBindings: true };
17
- },
18
- }),
19
- // React component renderer for knockout
20
- react: (ko: any) => ({
21
- init: (el: any) => {
22
- ko.utils.domNodeDisposal.addDisposeCallback(el, () => {
23
- unmountComponentAtNode(el);
24
- });
25
- return { controlsDescendantBindings: true }; // important
26
- },
27
- update: (el: Element, valueAccessor: any, allBindings: any, viewModel: any) => {
28
- const component = ko.unwrap(valueAccessor());
29
- let props = allBindings.get('props');
30
- props = props ? props : viewModel ? viewModel : null;
31
- render(createElement(component, props), el);
32
- },
33
- }),
34
- };