zero-query 1.3.0 → 1.3.1
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/README.md +10 -8
- package/dist/API.md +22 -19
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +26 -8
- package/dist/zquery.min.js +5 -5
- package/package.json +5 -3
- package/src/diff.js +23 -5
- package/tests/coverage-targets.test.js +524 -0
- package/tests/diff.test.js +87 -0
package/tests/diff.test.js
CHANGED
|
@@ -616,6 +616,93 @@ describe('morph - keyed edge cases', () => {
|
|
|
616
616
|
});
|
|
617
617
|
|
|
618
618
|
|
|
619
|
+
// ---------------------------------------------------------------------------
|
|
620
|
+
// Duplicate-key collision fallback
|
|
621
|
+
//
|
|
622
|
+
// Auto-keys (id / data-id / data-key) can collide among siblings - the
|
|
623
|
+
// canonical case is event delegation, where several action buttons in one
|
|
624
|
+
// row all carry data-id="<record-id>". Duplicate keys make keyed
|
|
625
|
+
// reconciliation ill-defined (later keyMap entries overwrite earlier ones,
|
|
626
|
+
// so nodes get dropped, duplicated, and reordered). _morphChildren detects
|
|
627
|
+
// a duplicate on either side and falls back to positional morphing, which
|
|
628
|
+
// is always well-defined for colliding sets.
|
|
629
|
+
// ---------------------------------------------------------------------------
|
|
630
|
+
|
|
631
|
+
describe('morph - duplicate-key collision fallback', () => {
|
|
632
|
+
it('morphs delegation buttons sharing one data-id without drops or dupes', () => {
|
|
633
|
+
// Faithful to the molexcloud uploads-row repro: a row-actions span whose
|
|
634
|
+
// buttons all carry the same data-id for event delegation. Growing the
|
|
635
|
+
// button set must produce exactly the new buttons, in order, once each.
|
|
636
|
+
const root = el('<span><button data-id="42">scrap</button></span>');
|
|
637
|
+
morph(
|
|
638
|
+
root,
|
|
639
|
+
'<span>' +
|
|
640
|
+
'<button data-id="42">pause</button>' +
|
|
641
|
+
'<button data-id="42">resume</button>' +
|
|
642
|
+
'<button data-id="42">scrap</button>' +
|
|
643
|
+
'<button data-id="42">link</button>' +
|
|
644
|
+
'</span>'
|
|
645
|
+
);
|
|
646
|
+
const buttons = [...root.querySelector('span').children];
|
|
647
|
+
expect(buttons.map(b => b.textContent)).toEqual(['pause', 'resume', 'scrap', 'link']);
|
|
648
|
+
// Every button carries the shared id, none dropped, none duplicated
|
|
649
|
+
expect(buttons.every(b => b.dataset.id === '42')).toBe(true);
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it('shrinks a shared-data-id button set positionally', () => {
|
|
653
|
+
const root = el(
|
|
654
|
+
'<span>' +
|
|
655
|
+
'<button data-id="7">pause</button>' +
|
|
656
|
+
'<button data-id="7">resume</button>' +
|
|
657
|
+
'<button data-id="7">scrap</button>' +
|
|
658
|
+
'</span>'
|
|
659
|
+
);
|
|
660
|
+
morph(
|
|
661
|
+
root,
|
|
662
|
+
'<span>' +
|
|
663
|
+
'<button data-id="7">scrap</button>' +
|
|
664
|
+
'<button data-id="7">link</button>' +
|
|
665
|
+
'</span>'
|
|
666
|
+
);
|
|
667
|
+
const buttons = [...root.querySelector('span').children];
|
|
668
|
+
expect(buttons.map(b => b.textContent)).toEqual(['scrap', 'link']);
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
it('falls back cleanly when explicit z-key values collide', () => {
|
|
672
|
+
const root = el('<div z-key="dup">A</div><div z-key="dup">B</div>');
|
|
673
|
+
morph(
|
|
674
|
+
root,
|
|
675
|
+
'<div z-key="dup">X</div><div z-key="dup">Y</div><div z-key="dup">Z</div>'
|
|
676
|
+
);
|
|
677
|
+
// Positional morph: exact new content, no drops/dupes despite the collision
|
|
678
|
+
expect([...root.children].map(c => c.textContent)).toEqual(['X', 'Y', 'Z']);
|
|
679
|
+
expect(root.children.length).toBe(3);
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it('falls back when the collision is only on the new side', () => {
|
|
683
|
+
const root = el('<div data-id="1">A</div><div data-id="2">B</div>');
|
|
684
|
+
morph(root, '<div data-id="9">X</div><div data-id="9">Y</div>');
|
|
685
|
+
expect([...root.children].map(c => c.textContent)).toEqual(['X', 'Y']);
|
|
686
|
+
expect(root.children.length).toBe(2);
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
it('still uses the keyed path (preserving identity) when data-ids are unique', () => {
|
|
690
|
+
// Contrast case: unique data-ids keep the LIS-optimised keyed path, so
|
|
691
|
+
// node identity survives a reorder rather than being positionally rebuilt.
|
|
692
|
+
const root = el(
|
|
693
|
+
'<li data-id="1">One</li><li data-id="2">Two</li><li data-id="3">Three</li>'
|
|
694
|
+
);
|
|
695
|
+
const ref1 = root.children[0];
|
|
696
|
+
const ref2 = root.children[1];
|
|
697
|
+
const ref3 = root.children[2];
|
|
698
|
+
morph(root, '<li data-id="3">Three</li><li data-id="1">One</li><li data-id="2">Two</li>');
|
|
699
|
+
expect(root.children[0]).toBe(ref3);
|
|
700
|
+
expect(root.children[1]).toBe(ref1);
|
|
701
|
+
expect(root.children[2]).toBe(ref2);
|
|
702
|
+
});
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
|
|
619
706
|
// ---------------------------------------------------------------------------
|
|
620
707
|
// Boolean attribute morphing
|
|
621
708
|
// ---------------------------------------------------------------------------
|