@gogocat/data-bind 1.11.0 → 2.0.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.
- package/.editorconfig +14 -14
- package/.vscode/launch.json +12 -12
- package/CONFIGURATION.md +294 -0
- package/REACTIVE_MODE.md +553 -0
- package/README.md +266 -829
- package/babel.config.json +30 -0
- package/dist/js/_escape.d.ts +14 -0
- package/dist/js/_escape.d.ts.map +1 -0
- package/dist/js/applyBinding.d.ts +11 -0
- package/dist/js/applyBinding.d.ts.map +1 -0
- package/dist/js/attrBinding.d.ts +12 -0
- package/dist/js/attrBinding.d.ts.map +1 -0
- package/dist/js/binder.d.ts +67 -0
- package/dist/js/binder.d.ts.map +1 -0
- package/dist/js/changeBinding.d.ts +19 -0
- package/dist/js/changeBinding.d.ts.map +1 -0
- package/dist/js/commentWrapper.d.ts +39 -0
- package/dist/js/commentWrapper.d.ts.map +1 -0
- package/dist/js/config.d.ts +55 -0
- package/dist/js/config.d.ts.map +1 -0
- package/dist/js/createBindingOption.d.ts +32 -0
- package/dist/js/createBindingOption.d.ts.map +1 -0
- package/dist/js/createEventBinding.d.ts +10 -0
- package/dist/js/createEventBinding.d.ts.map +1 -0
- package/dist/js/cssBinding.d.ts +15 -0
- package/dist/js/cssBinding.d.ts.map +1 -0
- package/dist/js/dataBind.js +2772 -2519
- package/dist/js/dataBind.min.js +8 -1
- package/dist/js/dataBind.min.js.map +1 -1
- package/dist/js/domWalker.d.ts +9 -0
- package/dist/js/domWalker.d.ts.map +1 -0
- package/dist/js/forOfBinding.d.ts +12 -0
- package/dist/js/forOfBinding.d.ts.map +1 -0
- package/dist/js/hoverBinding.d.ts +13 -0
- package/dist/js/hoverBinding.d.ts.map +1 -0
- package/dist/js/ifBinding.d.ts +12 -0
- package/dist/js/ifBinding.d.ts.map +1 -0
- package/dist/js/index.d.ts +10 -0
- package/dist/js/index.d.ts.map +1 -0
- package/dist/js/modelBinding.d.ts +12 -0
- package/dist/js/modelBinding.d.ts.map +1 -0
- package/dist/js/postProcess.d.ts +3 -0
- package/dist/js/postProcess.d.ts.map +1 -0
- package/dist/js/pubSub.d.ts +11 -0
- package/dist/js/pubSub.d.ts.map +1 -0
- package/dist/js/reactiveProxy.d.ts +28 -0
- package/dist/js/reactiveProxy.d.ts.map +1 -0
- package/dist/js/renderForOfBinding.d.ts +8 -0
- package/dist/js/renderForOfBinding.d.ts.map +1 -0
- package/dist/js/renderIfBinding.d.ts +22 -0
- package/dist/js/renderIfBinding.d.ts.map +1 -0
- package/dist/js/renderIteration.d.ts +16 -0
- package/dist/js/renderIteration.d.ts.map +1 -0
- package/dist/js/renderTemplate.d.ts +14 -0
- package/dist/js/renderTemplate.d.ts.map +1 -0
- package/dist/js/renderTemplatesBinding.d.ts +19 -0
- package/dist/js/renderTemplatesBinding.d.ts.map +1 -0
- package/dist/js/showBinding.d.ts +13 -0
- package/dist/js/showBinding.d.ts.map +1 -0
- package/dist/js/switchBinding.d.ts +13 -0
- package/dist/js/switchBinding.d.ts.map +1 -0
- package/dist/js/textBinding.d.ts +13 -0
- package/dist/js/textBinding.d.ts.map +1 -0
- package/dist/js/types/_escape.d.ts +14 -0
- package/dist/js/types/_escape.d.ts.map +1 -0
- package/dist/js/types/applyBinding.d.ts +11 -0
- package/dist/js/types/applyBinding.d.ts.map +1 -0
- package/dist/js/types/attrBinding.d.ts +12 -0
- package/dist/js/types/attrBinding.d.ts.map +1 -0
- package/dist/js/types/binder.d.ts +67 -0
- package/dist/js/types/binder.d.ts.map +1 -0
- package/dist/js/types/changeBinding.d.ts +19 -0
- package/dist/js/types/changeBinding.d.ts.map +1 -0
- package/dist/js/types/commentWrapper.d.ts +39 -0
- package/dist/js/types/commentWrapper.d.ts.map +1 -0
- package/dist/js/types/config.d.ts +55 -0
- package/dist/js/types/config.d.ts.map +1 -0
- package/dist/js/types/createBindingOption.d.ts +32 -0
- package/dist/js/types/createBindingOption.d.ts.map +1 -0
- package/dist/js/types/createEventBinding.d.ts +10 -0
- package/dist/js/types/createEventBinding.d.ts.map +1 -0
- package/dist/js/types/cssBinding.d.ts +15 -0
- package/dist/js/types/cssBinding.d.ts.map +1 -0
- package/dist/js/types/domWalker.d.ts +9 -0
- package/dist/js/types/domWalker.d.ts.map +1 -0
- package/dist/js/types/forOfBinding.d.ts +12 -0
- package/dist/js/types/forOfBinding.d.ts.map +1 -0
- package/dist/js/types/hoverBinding.d.ts +13 -0
- package/dist/js/types/hoverBinding.d.ts.map +1 -0
- package/dist/js/types/ifBinding.d.ts +12 -0
- package/dist/js/types/ifBinding.d.ts.map +1 -0
- package/dist/js/types/index.d.ts +10 -0
- package/dist/js/types/index.d.ts.map +1 -0
- package/dist/js/types/modelBinding.d.ts +12 -0
- package/dist/js/types/modelBinding.d.ts.map +1 -0
- package/dist/js/types/postProcess.d.ts +3 -0
- package/dist/js/types/postProcess.d.ts.map +1 -0
- package/dist/js/types/pubSub.d.ts +11 -0
- package/dist/js/types/pubSub.d.ts.map +1 -0
- package/dist/js/types/reactiveProxy.d.ts +28 -0
- package/dist/js/types/reactiveProxy.d.ts.map +1 -0
- package/dist/js/types/renderForOfBinding.d.ts +8 -0
- package/dist/js/types/renderForOfBinding.d.ts.map +1 -0
- package/dist/js/types/renderIfBinding.d.ts +22 -0
- package/dist/js/types/renderIfBinding.d.ts.map +1 -0
- package/dist/js/types/renderIteration.d.ts +16 -0
- package/dist/js/types/renderIteration.d.ts.map +1 -0
- package/dist/js/types/renderTemplate.d.ts +14 -0
- package/dist/js/types/renderTemplate.d.ts.map +1 -0
- package/dist/js/types/renderTemplatesBinding.d.ts +19 -0
- package/dist/js/types/renderTemplatesBinding.d.ts.map +1 -0
- package/dist/js/types/showBinding.d.ts +13 -0
- package/dist/js/types/showBinding.d.ts.map +1 -0
- package/dist/js/types/switchBinding.d.ts +13 -0
- package/dist/js/types/switchBinding.d.ts.map +1 -0
- package/dist/js/types/textBinding.d.ts +13 -0
- package/dist/js/types/textBinding.d.ts.map +1 -0
- package/dist/js/types/types.d.ts +111 -0
- package/dist/js/types/types.d.ts.map +1 -0
- package/dist/js/types/util.d.ts +119 -0
- package/dist/js/types/util.d.ts.map +1 -0
- package/dist/js/types.d.ts +111 -0
- package/dist/js/types.d.ts.map +1 -0
- package/dist/js/util.d.ts +119 -0
- package/dist/js/util.d.ts.map +1 -0
- package/eslint.config.js +124 -0
- package/examples/DBMONSTER_COMPARISON.md +123 -0
- package/examples/afterRenderDemo.html +119 -0
- package/examples/bootstrap/css/animate.css +1579 -1579
- package/examples/bootstrap/css/bootstrap.min.css +6 -6
- package/examples/bootstrap/css/homeservices.css +378 -390
- package/examples/bootstrap/css/open-iconic.css +511 -511
- package/examples/bootstrap/fonts/open-iconic.svg +543 -543
- package/examples/bootstrap/js/compMessageDialog.js +20 -19
- package/examples/bootstrap/js/compSearchBar.js +12 -19
- package/examples/bootstrap/js/compSearchResults.js +50 -46
- package/examples/bootstrap/js/featureAdsResult.json +65 -65
- package/examples/bootstrap/js/searchResult.json +57 -57
- package/examples/bootstrap.html +343 -332
- package/examples/css/baseTodo.css +141 -141
- package/examples/css/dbMonsterStyles.css +27 -27
- package/examples/css/indexTodo.css +374 -374
- package/examples/dbmonsterForOfReactive.html +40 -0
- package/examples/dbmonsterReact.html +19 -0
- package/examples/forOfBindingSimpleDebug.html +45 -0
- package/examples/form.html +20 -4
- package/examples/globalConfig.html +131 -0
- package/examples/js/afterRenderDemo.js +190 -0
- package/examples/js/appTodo.js +46 -46
- package/examples/js/attrBindingDemo.js +2 -2
- package/examples/js/dbMonApp.js +24 -26
- package/examples/js/dbMonAppReact.jsx +79 -0
- package/examples/js/dbMonAppReactive.js +28 -0
- package/examples/js/fiberDemo.js +4 -4
- package/examples/js/filtersDemo.js +8 -8
- package/examples/js/forOfDemo.js +7 -9
- package/examples/js/forOfDemoComplex.js +44 -17
- package/examples/js/form.js +44 -12
- package/examples/js/globalConfig.js +117 -0
- package/examples/js/ifBindingDemo.js +16 -16
- package/examples/js/reactiveDemo.js +119 -0
- package/examples/js/switchBindingDemo.js +8 -8
- package/examples/react-dbmonster/dist/bundle.js +43 -0
- package/examples/react-dbmonster/package-lock.json +537 -0
- package/examples/react-dbmonster/package.json +16 -0
- package/examples/react-dbmonster/src/index.jsx +80 -0
- package/examples/reactiveDemo.html +127 -0
- package/examples/refreshRateTest.html +75 -75
- package/index.html +841 -0
- package/package.json +31 -34
- package/rollup.config.js +79 -36
- package/src/{_escape.js → _escape.ts} +19 -17
- package/src/applyBinding.ts +179 -0
- package/src/{attrBinding.js → attrBinding.ts} +14 -13
- package/src/binder.ts +289 -0
- package/src/changeBinding.ts +93 -0
- package/src/{commentWrapper.js → commentWrapper.ts} +33 -30
- package/src/config.ts +107 -0
- package/src/createBindingOption.ts +91 -0
- package/src/createEventBinding.ts +88 -0
- package/src/{cssBinding.js → cssBinding.ts} +13 -11
- package/src/{domWalker.js → domWalker.ts} +44 -30
- package/src/{forOfBinding.js → forOfBinding.ts} +4 -3
- package/src/hoverBinding.ts +84 -0
- package/src/{ifBinding.js → ifBinding.ts} +14 -12
- package/src/index.ts +53 -0
- package/src/{modelBinding.js → modelBinding.ts} +11 -9
- package/src/postProcess.ts +22 -0
- package/src/{pubSub.js → pubSub.ts} +24 -15
- package/src/reactiveProxy.ts +285 -0
- package/src/{renderForOfBinding.js → renderForOfBinding.ts} +55 -33
- package/src/{renderIfBinding.js → renderIfBinding.ts} +45 -20
- package/src/renderIteration.ts +53 -0
- package/src/renderTemplate.ts +165 -0
- package/src/renderTemplatesBinding.ts +73 -0
- package/src/{showBinding.js → showBinding.ts} +4 -3
- package/src/{switchBinding.js → switchBinding.ts} +18 -15
- package/src/{textBinding.js → textBinding.ts} +5 -4
- package/src/types.ts +124 -0
- package/src/util.ts +810 -0
- package/test/css/reporter.css +9 -9
- package/test/fixtures/dataBindBootstrap.html +2 -2
- package/test/fixtures/formBindings.html +9 -1
- package/test/globals.d.ts +19 -0
- package/test/helpers/testHelper.js +46 -11
- package/test/mocks/featureAdsResult.json +65 -65
- package/test/mocks/searchResult.json +57 -57
- package/test/specs/{attrBinding.spec.js → attrBinding.spec.ts} +103 -106
- package/test/specs/{binder.spec.js → binder.spec.ts} +29 -27
- package/test/specs/blurBinding.spec.ts +60 -0
- package/test/specs/chainableUse.spec.ts +125 -0
- package/test/specs/clickBinding.spec.ts +194 -0
- package/test/specs/{cssBinding.spec.js → cssBinding.spec.ts} +72 -79
- package/test/specs/{dataBindBootstrap.spec.js → dataBindBootstrap.spec.ts} +332 -313
- package/test/specs/{filter.spec.js → filter.spec.ts} +75 -76
- package/test/specs/{forOfBinding.spec.js → forOfBinding.spec.ts} +208 -219
- package/test/specs/formBinding.spec.ts +272 -0
- package/test/specs/ifBinding.spec.ts +165 -0
- package/test/specs/{nestedComponent.spec.js → nestedComponent.spec.ts} +88 -88
- package/test/specs/reactiveProxy.spec.ts +465 -0
- package/test/specs/{showBinding.spec.js → showBinding.spec.ts} +148 -149
- package/test/specs/{switchBinding.spec.js → switchBinding.spec.ts} +172 -173
- package/test/specs/templateBinding.spec.ts +273 -0
- package/test/specs/{textBinding.spec.js → textBinding.spec.ts} +47 -48
- package/test/tsconfig.json +31 -0
- package/test-output.txt +200 -0
- package/test-reactive.html +224 -0
- package/tsconfig.json +28 -0
- package/vendors/lodash.custom.js +4577 -4577
- package/vendors/lodash.custom.min.js +45 -45
- package/vitest.config.js +27 -0
- package/.eslintrc.js +0 -1
- package/.grunt/grunt-contrib-jasmine/boot.js +0 -161
- package/.grunt/grunt-contrib-jasmine/dist/js/dataBind.js +0 -9
- package/.grunt/grunt-contrib-jasmine/grunt-template-jasmine-istanbul/reporter.js +0 -23
- package/.grunt/grunt-contrib-jasmine/jasmine-html.js +0 -853
- package/.grunt/grunt-contrib-jasmine/jasmine.css +0 -271
- package/.grunt/grunt-contrib-jasmine/jasmine.js +0 -9761
- package/.grunt/grunt-contrib-jasmine/jasmine_favicon.png +0 -0
- package/.grunt/grunt-contrib-jasmine/json2.js +0 -489
- package/.grunt/grunt-contrib-jasmine/reporter.js +0 -107
- package/coverage/coverage.json +0 -1
- package/coverage/lcov/lcov-report/base.css +0 -213
- package/coverage/lcov/lcov-report/index.html +0 -93
- package/coverage/lcov/lcov-report/js/dataBind.js.html +0 -6596
- package/coverage/lcov/lcov-report/js/index.html +0 -93
- package/coverage/lcov/lcov-report/prettify.css +0 -1
- package/coverage/lcov/lcov-report/prettify.js +0 -1
- package/coverage/lcov/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov/lcov-report/sorter.js +0 -158
- package/coverage/lcov/lcov.info +0 -1991
- package/eslintrc.json +0 -40
- package/examples/bootstrap/js/bootstrap.min.js +0 -6
- package/examples/bootstrap/js/popper.min.js +0 -5
- package/examples/bootstrap/js/searchSuggestion.js +0 -58
- package/examples/bootstrap/js/typeahead.jquery.js +0 -1538
- package/gruntfile.js +0 -92
- package/gulpfile.js +0 -32
- package/src/binder.js +0 -422
- package/src/changeBinding.js +0 -57
- package/src/config.js +0 -65
- package/src/createBindingOption.js +0 -66
- package/src/createEventBinding.js +0 -46
- package/src/eventSystem.js +0 -46
- package/src/hoverBinding.js +0 -57
- package/src/index.js +0 -26
- package/src/renderTemplate.js +0 -128
- package/src/util.js +0 -648
- package/test/specs/blurBinding.spec.js +0 -57
- package/test/specs/formBinding.spec.js +0 -292
- package/test/specs/ifBinding.spec.js +0 -169
- package/test/specs/templateBinding.spec.js +0 -117
- package/vendors/jasmine-jquery.js +0 -841
- package/vendors/jquery-3.2.1.min.js +0 -4
|
@@ -1,27 +1,29 @@
|
|
|
1
|
-
describe
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
1
|
+
import {describe, it, expect} from 'vitest';
|
|
2
|
+
|
|
3
|
+
describe('Given dataBind lib loaded', () => {
|
|
4
|
+
it('Then dataBind object exists', () => {
|
|
5
|
+
expect(typeof dataBind).toBe('object');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('Then "dataBind.init" should be a functions', () => {
|
|
9
|
+
expect(typeof dataBind.init).toBe('function');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('Then "dataBind.use" should be a functions', () => {
|
|
13
|
+
expect(typeof dataBind.use).toBe('function');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('Should throw error if mounting root element does not exits', () => {
|
|
17
|
+
const viewModel = {};
|
|
18
|
+
|
|
19
|
+
expect(() => {
|
|
20
|
+
dataBind.init(document.getElementById('#xyz'), viewModel);
|
|
21
|
+
}).toThrow();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('Should throw error if viewModel does not exits', () => {
|
|
25
|
+
expect(() => {
|
|
26
|
+
dataBind.init(document.getElementById('#xyz'), undefined as any);
|
|
27
|
+
}).toThrow();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {describe, it, expect, beforeEach, afterEach} from 'vitest';
|
|
2
|
+
import {waitFor} from '@testing-library/dom';
|
|
3
|
+
|
|
4
|
+
describe('Given [data-bind-comp="blur-component"] initised', () => {
|
|
5
|
+
const namespace: any = {};
|
|
6
|
+
const testBlurValue = 'onBlur called';
|
|
7
|
+
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
loadFixture('test/fixtures/blurBinding.html');
|
|
10
|
+
|
|
11
|
+
namespace.viewModel = {
|
|
12
|
+
heading: 'blur component test',
|
|
13
|
+
myData: 'blur component',
|
|
14
|
+
onFocusFn(_e: Event, _$element: any) {},
|
|
15
|
+
onBlurFn(_e: Event, _$element: any) {
|
|
16
|
+
this.myData = testBlurValue;
|
|
17
|
+
this.updateView();
|
|
18
|
+
},
|
|
19
|
+
updateView(opt?: any) {
|
|
20
|
+
this.APP.render(opt);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
namespace.blurComponent = dataBind.init(document.querySelector('[data-bind-comp="blur-component"]'), namespace.viewModel);
|
|
25
|
+
await namespace.blurComponent.render();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
// clean up app
|
|
30
|
+
// clean up all app/components
|
|
31
|
+
for (const prop in namespace) {
|
|
32
|
+
if (Object.prototype.hasOwnProperty.call(namespace, prop)) {
|
|
33
|
+
delete namespace[prop];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should render heading defined in viewModel', async () => {
|
|
39
|
+
await waitFor(() => {
|
|
40
|
+
expect(document.getElementById('heading')!.textContent).toBe(namespace.viewModel.heading);
|
|
41
|
+
expect((document.getElementById('blurInput') as HTMLInputElement).value).toBe(namespace.viewModel.myData);
|
|
42
|
+
}, {timeout: 500});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should update "blurInput" value after onBlur', async () => {
|
|
46
|
+
await waitFor(() => {
|
|
47
|
+
const $blurInput = document.getElementById('blurInput');
|
|
48
|
+
expect($blurInput).not.toBeNull();
|
|
49
|
+
}, {timeout: 500});
|
|
50
|
+
|
|
51
|
+
const $blurInput = document.getElementById('blurInput')!;
|
|
52
|
+
|
|
53
|
+
// Use the test helper to simulate blur event
|
|
54
|
+
simulateBlur($blurInput);
|
|
55
|
+
|
|
56
|
+
await waitFor(() => {
|
|
57
|
+
expect(($blurInput as HTMLInputElement).value).toBe(testBlurValue);
|
|
58
|
+
}, {timeout: 500});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {describe, it, expect, beforeEach, afterEach} from 'vitest';
|
|
2
|
+
|
|
3
|
+
describe('Chainable use() API', () => {
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
document.body.innerHTML = '';
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
document.body.innerHTML = '';
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should support chaining use() with init()', () => {
|
|
13
|
+
document.body.innerHTML = `
|
|
14
|
+
<div id="app" data-bind-comp="test">
|
|
15
|
+
<span data-bind-text="value"></span>
|
|
16
|
+
</div>
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
const viewModel = {
|
|
20
|
+
value: 'Hello',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Chain use() with init()
|
|
24
|
+
const app = (window as any).dataBind
|
|
25
|
+
.use({reactive: false})
|
|
26
|
+
.init(document.getElementById('app'), viewModel);
|
|
27
|
+
|
|
28
|
+
expect(app).toBeDefined();
|
|
29
|
+
expect(app.isReactive).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should support multiple use() calls in chain', () => {
|
|
33
|
+
document.body.innerHTML = `
|
|
34
|
+
<div id="app" data-bind-comp="test">
|
|
35
|
+
<span data-bind-text="value"></span>
|
|
36
|
+
</div>
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const viewModel = {
|
|
40
|
+
value: 'Hello',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Multiple chained use() calls (last one wins)
|
|
44
|
+
const app = (window as any).dataBind
|
|
45
|
+
.use({reactive: true})
|
|
46
|
+
.use({trackChanges: true})
|
|
47
|
+
.use({reactive: false})
|
|
48
|
+
.init(document.getElementById('app'), viewModel);
|
|
49
|
+
|
|
50
|
+
expect(app).toBeDefined();
|
|
51
|
+
expect(app.isReactive).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should work with traditional use() then init() pattern', () => {
|
|
55
|
+
document.body.innerHTML = `
|
|
56
|
+
<div id="app" data-bind-comp="test">
|
|
57
|
+
<span data-bind-text="value"></span>
|
|
58
|
+
</div>
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
const viewModel = {
|
|
62
|
+
value: 'Hello',
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Traditional pattern (still works)
|
|
66
|
+
(window as any).dataBind.use({reactive: false});
|
|
67
|
+
const app = (window as any).dataBind.init(
|
|
68
|
+
document.getElementById('app'),
|
|
69
|
+
viewModel,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
expect(app).toBeDefined();
|
|
73
|
+
expect(app.isReactive).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should allow instance options to override chained use() settings', () => {
|
|
77
|
+
document.body.innerHTML = `
|
|
78
|
+
<div id="app" data-bind-comp="test">
|
|
79
|
+
<span data-bind-text="value"></span>
|
|
80
|
+
</div>
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
const viewModel = {
|
|
84
|
+
value: 'Hello',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// use() sets reactive: false, but instance option overrides to true
|
|
88
|
+
const app = (window as any).dataBind
|
|
89
|
+
.use({reactive: false})
|
|
90
|
+
.init(document.getElementById('app'), viewModel, {reactive: true});
|
|
91
|
+
|
|
92
|
+
expect(app).toBeDefined();
|
|
93
|
+
expect(app.isReactive).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should reset to default after using chained use()', () => {
|
|
97
|
+
document.body.innerHTML = `
|
|
98
|
+
<div id="app1" data-bind-comp="test1">
|
|
99
|
+
<span data-bind-text="value"></span>
|
|
100
|
+
</div>
|
|
101
|
+
<div id="app2" data-bind-comp="test2">
|
|
102
|
+
<span data-bind-text="value"></span>
|
|
103
|
+
</div>
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
const viewModel = {value: 'Hello'};
|
|
107
|
+
|
|
108
|
+
// First component with chained use
|
|
109
|
+
const app1 = (window as any).dataBind
|
|
110
|
+
.use({reactive: false})
|
|
111
|
+
.init(document.getElementById('app1'), viewModel);
|
|
112
|
+
|
|
113
|
+
// Second component should inherit the global setting from use()
|
|
114
|
+
const app2 = (window as any).dataBind.init(
|
|
115
|
+
document.getElementById('app2'),
|
|
116
|
+
viewModel,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
expect(app1.isReactive).toBe(false);
|
|
120
|
+
expect(app2.isReactive).toBe(false); // Global setting persists
|
|
121
|
+
|
|
122
|
+
// Reset to default
|
|
123
|
+
(window as any).dataBind.use({reactive: true});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest';
|
|
2
|
+
import {waitFor} from '@testing-library/dom';
|
|
3
|
+
|
|
4
|
+
describe('Click Binding - Event Handler Deduplication', () => {
|
|
5
|
+
let namespace: any;
|
|
6
|
+
|
|
7
|
+
const loadFixture = (fixturePath: string) => {
|
|
8
|
+
const xhr = new XMLHttpRequest();
|
|
9
|
+
xhr.open('GET', fixturePath, false);
|
|
10
|
+
xhr.send();
|
|
11
|
+
document.body.innerHTML = xhr.responseText;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
namespace = (window as any).namespace || {};
|
|
16
|
+
(window as any).namespace = namespace;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
document.body.innerHTML = '';
|
|
21
|
+
delete (window as any).namespace;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should only call click handler once per click, even after re-render', async () => {
|
|
25
|
+
// Create a simple fixture
|
|
26
|
+
document.body.innerHTML = `
|
|
27
|
+
<div data-bind-comp="test-component">
|
|
28
|
+
<button id="testButton" data-bind-click="handleClick">Click Me</button>
|
|
29
|
+
<span id="counter" data-bind-text="count"></span>
|
|
30
|
+
</div>
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const clickSpy = vi.fn();
|
|
34
|
+
const viewModel = {
|
|
35
|
+
count: 0,
|
|
36
|
+
handleClick() {
|
|
37
|
+
clickSpy();
|
|
38
|
+
this.count++;
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const component = (window as any).dataBind.init(
|
|
43
|
+
document.querySelector('[data-bind-comp="test-component"]'),
|
|
44
|
+
viewModel,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
await component.render();
|
|
48
|
+
|
|
49
|
+
const button = document.getElementById('testButton') as HTMLButtonElement;
|
|
50
|
+
|
|
51
|
+
// First click
|
|
52
|
+
button.click();
|
|
53
|
+
|
|
54
|
+
await waitFor(() => {
|
|
55
|
+
expect(clickSpy).toHaveBeenCalledTimes(1);
|
|
56
|
+
expect(component.viewModel.count).toBe(1);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Wait for reactive render to complete
|
|
60
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
61
|
+
|
|
62
|
+
// Second click - should still only call once
|
|
63
|
+
clickSpy.mockClear();
|
|
64
|
+
button.click();
|
|
65
|
+
|
|
66
|
+
await waitFor(() => {
|
|
67
|
+
expect(clickSpy).toHaveBeenCalledTimes(1);
|
|
68
|
+
expect(component.viewModel.count).toBe(2);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should properly remove old handlers when re-rendering with template binding', async () => {
|
|
73
|
+
document.body.innerHTML = `
|
|
74
|
+
<div data-bind-comp="test-component">
|
|
75
|
+
<div data-bind-for="item in items">
|
|
76
|
+
<button class="item-button" data-bind-click="$root.handleItemClick($index)">
|
|
77
|
+
Item <span data-bind-text="$index"></span>
|
|
78
|
+
</button>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
const clickSpy = vi.fn();
|
|
84
|
+
const viewModel = {
|
|
85
|
+
items: [1, 2, 3],
|
|
86
|
+
handleItemClick(e: Event, el: HTMLElement, index: number) {
|
|
87
|
+
clickSpy(index);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const component = (window as any).dataBind.init(
|
|
92
|
+
document.querySelector('[data-bind-comp="test-component"]'),
|
|
93
|
+
viewModel,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
await component.render();
|
|
97
|
+
|
|
98
|
+
const buttons = document.querySelectorAll('.item-button') as NodeListOf<HTMLButtonElement>;
|
|
99
|
+
expect(buttons.length).toBe(3);
|
|
100
|
+
|
|
101
|
+
// Click first button
|
|
102
|
+
buttons[0].click();
|
|
103
|
+
|
|
104
|
+
await waitFor(() => {
|
|
105
|
+
expect(clickSpy).toHaveBeenCalledTimes(1);
|
|
106
|
+
expect(clickSpy).toHaveBeenCalledWith(0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Wait for any reactive updates
|
|
110
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
111
|
+
|
|
112
|
+
// Update items to trigger re-render
|
|
113
|
+
clickSpy.mockClear();
|
|
114
|
+
component.viewModel.items = [1, 2, 3, 4];
|
|
115
|
+
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
const newButtons = document.querySelectorAll('.item-button') as NodeListOf<HTMLButtonElement>;
|
|
118
|
+
expect(newButtons.length).toBe(4);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Click first button again - should only fire once
|
|
122
|
+
const newButtons = document.querySelectorAll('.item-button') as NodeListOf<HTMLButtonElement>;
|
|
123
|
+
newButtons[0].click();
|
|
124
|
+
|
|
125
|
+
await waitFor(() => {
|
|
126
|
+
expect(clickSpy).toHaveBeenCalledTimes(1);
|
|
127
|
+
expect(clickSpy).toHaveBeenCalledWith(0);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should handle multiple event types without conflicts', async () => {
|
|
132
|
+
document.body.innerHTML = `
|
|
133
|
+
<div data-bind-comp="test-component">
|
|
134
|
+
<button
|
|
135
|
+
id="multiEventButton"
|
|
136
|
+
data-bind-click="handleClick"
|
|
137
|
+
data-bind-dblclick="handleDblClick"
|
|
138
|
+
>Multi Event Button</button>
|
|
139
|
+
<span id="clickCount" data-bind-text="clickCount"></span>
|
|
140
|
+
<span id="dblClickCount" data-bind-text="dblClickCount"></span>
|
|
141
|
+
</div>
|
|
142
|
+
`;
|
|
143
|
+
|
|
144
|
+
const viewModel = {
|
|
145
|
+
clickCount: 0,
|
|
146
|
+
dblClickCount: 0,
|
|
147
|
+
handleClick() {
|
|
148
|
+
this.clickCount++;
|
|
149
|
+
},
|
|
150
|
+
handleDblClick() {
|
|
151
|
+
this.dblClickCount++;
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const component = (window as any).dataBind.init(
|
|
156
|
+
document.querySelector('[data-bind-comp="test-component"]'),
|
|
157
|
+
viewModel,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
await component.render();
|
|
161
|
+
|
|
162
|
+
const button = document.getElementById('multiEventButton') as HTMLButtonElement;
|
|
163
|
+
|
|
164
|
+
// Single click
|
|
165
|
+
button.click();
|
|
166
|
+
|
|
167
|
+
await waitFor(() => {
|
|
168
|
+
expect(component.viewModel.clickCount).toBe(1);
|
|
169
|
+
expect(component.viewModel.dblClickCount).toBe(0);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Wait for reactive render
|
|
173
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
174
|
+
|
|
175
|
+
// Double click
|
|
176
|
+
button.dispatchEvent(new MouseEvent('dblclick', {bubbles: true}));
|
|
177
|
+
|
|
178
|
+
await waitFor(() => {
|
|
179
|
+
expect(component.viewModel.clickCount).toBe(1);
|
|
180
|
+
expect(component.viewModel.dblClickCount).toBe(1);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Wait for reactive render
|
|
184
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
185
|
+
|
|
186
|
+
// Another click - should still only increment by 1
|
|
187
|
+
button.click();
|
|
188
|
+
|
|
189
|
+
await waitFor(() => {
|
|
190
|
+
expect(component.viewModel.clickCount).toBe(2);
|
|
191
|
+
expect(component.viewModel.dblClickCount).toBe(1);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
@@ -1,79 +1,72 @@
|
|
|
1
|
-
describe
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should apply css bindings', (
|
|
50
|
-
|
|
51
|
-
const $testCssOne = document.getElementById('testCssOne')
|
|
52
|
-
const testCssOneClassName = $testCssOne.className;
|
|
53
|
-
const $testCssTwo = document.getElementById('testCssTwo')
|
|
54
|
-
const testCssTwoClassName = $testCssTwo.className;
|
|
55
|
-
|
|
56
|
-
expect(testCssOneClassName).toBe('testCssOne a b c');
|
|
57
|
-
expect(testCssTwoClassName).toBe('testCssTwo x y z e f');
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
namespace.viewModel.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
testCssOne
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const testCssOneClassName = $testCssOne.className;
|
|
74
|
-
|
|
75
|
-
expect(testCssOneClassName).toBe('testCssOne a c');
|
|
76
|
-
done();
|
|
77
|
-
}, 200);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
1
|
+
import {describe, it, expect, beforeEach, afterEach} from 'vitest';
|
|
2
|
+
import {waitFor} from '@testing-library/dom';
|
|
3
|
+
|
|
4
|
+
describe('Given [data-bind-comp="css-component"] inited', () => {
|
|
5
|
+
const namespace: any = {};
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
loadFixture('test/fixtures/cssBinding.html');
|
|
9
|
+
|
|
10
|
+
namespace.viewModel = {
|
|
11
|
+
heading: 'myCssComponent',
|
|
12
|
+
testOneCss: {
|
|
13
|
+
a: true,
|
|
14
|
+
b: true,
|
|
15
|
+
c: true,
|
|
16
|
+
},
|
|
17
|
+
getTestTwoCss(_$data: any, _oldValue: any, _el: any) {
|
|
18
|
+
return {
|
|
19
|
+
e: true,
|
|
20
|
+
f: true,
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
updateView(opt?: any) {
|
|
24
|
+
this.APP.render(opt);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
namespace.myCssComponent = dataBind.init(document.querySelector('[data-bind-comp="css-component"]'), namespace.viewModel);
|
|
29
|
+
|
|
30
|
+
namespace.myCssComponent.render();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
// clean up all app/components
|
|
35
|
+
for (const prop in namespace) {
|
|
36
|
+
if (Object.prototype.hasOwnProperty.call(namespace, prop)) {
|
|
37
|
+
delete namespace[prop];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('Then [data-bind-comp="myCssComponent"] should have render', async () => {
|
|
43
|
+
await waitFor(() => {
|
|
44
|
+
const $heading = document.getElementById('myCssComponentHeading')!;
|
|
45
|
+
expect($heading.textContent).toBe(namespace.viewModel.heading);
|
|
46
|
+
}, {timeout: 500});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should apply css bindings', async () => {
|
|
50
|
+
await waitFor(() => {
|
|
51
|
+
const $testCssOne = document.getElementById('testCssOne')!;
|
|
52
|
+
const testCssOneClassName = $testCssOne.className;
|
|
53
|
+
const $testCssTwo = document.getElementById('testCssTwo')!;
|
|
54
|
+
const testCssTwoClassName = $testCssTwo.className;
|
|
55
|
+
|
|
56
|
+
expect(testCssOneClassName).toBe('testCssOne a b c');
|
|
57
|
+
expect(testCssTwoClassName).toBe('testCssTwo x y z e f');
|
|
58
|
+
}, {timeout: 500});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should remove duplicated css', async () => {
|
|
62
|
+
namespace.viewModel.testOneCss.b = false;
|
|
63
|
+
namespace.viewModel.updateView();
|
|
64
|
+
|
|
65
|
+
await waitFor(() => {
|
|
66
|
+
const $testCssOne = document.getElementById('testCssOne')!;
|
|
67
|
+
const testCssOneClassName = $testCssOne.className;
|
|
68
|
+
|
|
69
|
+
expect(testCssOneClassName).toBe('testCssOne a c');
|
|
70
|
+
}, {timeout: 500});
|
|
71
|
+
});
|
|
72
|
+
});
|