@wordpress/core-data 7.43.2-next.v.202604091042.0 → 7.44.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/CHANGELOG.md +1 -1
- package/build/hooks/use-entity-prop.cjs +1 -1
- package/build/hooks/use-entity-prop.cjs.map +2 -2
- package/build/queried-data/reducer.cjs +2 -1
- package/build/queried-data/reducer.cjs.map +2 -2
- package/build/utils/crdt-blocks.cjs +54 -26
- package/build/utils/crdt-blocks.cjs.map +2 -2
- package/build/utils/crdt-selection.cjs.map +2 -2
- package/build/utils/crdt.cjs.map +2 -2
- package/build-module/hooks/use-entity-prop.mjs +1 -1
- package/build-module/hooks/use-entity-prop.mjs.map +2 -2
- package/build-module/queried-data/reducer.mjs +2 -1
- package/build-module/queried-data/reducer.mjs.map +2 -2
- package/build-module/utils/crdt-blocks.mjs +54 -26
- package/build-module/utils/crdt-blocks.mjs.map +2 -2
- package/build-module/utils/crdt-selection.mjs.map +2 -2
- package/build-module/utils/crdt.mjs.map +2 -2
- package/build-types/queried-data/reducer.d.ts.map +1 -1
- package/build-types/utils/crdt-blocks.d.ts.map +1 -1
- package/build-types/utils/crdt-selection.d.ts.map +1 -1
- package/build-types/utils/crdt.d.ts.map +1 -1
- package/package.json +18 -18
- package/src/hooks/use-entity-prop.js +1 -1
- package/src/queried-data/reducer.js +3 -1
- package/src/queried-data/test/reducer.js +7 -0
- package/src/utils/crdt-blocks.ts +103 -36
- package/src/utils/crdt-selection.ts +0 -1
- package/src/utils/crdt.ts +0 -1
- package/src/utils/test/crdt-blocks.ts +544 -1
|
@@ -1428,7 +1428,7 @@ describe( 'crdt-blocks', () => {
|
|
|
1428
1428
|
expect( a1Content.toString() ).toBe( 'A1-edited' );
|
|
1429
1429
|
} );
|
|
1430
1430
|
|
|
1431
|
-
it( '
|
|
1431
|
+
it( 'preserves existing elements and appends new rows when row count increases', () => {
|
|
1432
1432
|
const tableBlocks: Block[] = [
|
|
1433
1433
|
{
|
|
1434
1434
|
name: 'core/table',
|
|
@@ -1567,6 +1567,549 @@ describe( 'crdt-blocks', () => {
|
|
|
1567
1567
|
doc2.destroy();
|
|
1568
1568
|
} );
|
|
1569
1569
|
|
|
1570
|
+
it( 'concurrent cell edit is preserved when another user appends a row', () => {
|
|
1571
|
+
// Two users: A edits a cell, B appends a row. After sync,
|
|
1572
|
+
// A's edit should be preserved alongside the new row.
|
|
1573
|
+
const initialBlocks: Block[] = [
|
|
1574
|
+
{
|
|
1575
|
+
name: 'core/table',
|
|
1576
|
+
attributes: {
|
|
1577
|
+
body: [
|
|
1578
|
+
{
|
|
1579
|
+
cells: [
|
|
1580
|
+
{ content: 'A1', tag: 'td' },
|
|
1581
|
+
{ content: 'B1', tag: 'td' },
|
|
1582
|
+
],
|
|
1583
|
+
},
|
|
1584
|
+
{
|
|
1585
|
+
cells: [
|
|
1586
|
+
{ content: 'A2', tag: 'td' },
|
|
1587
|
+
{ content: 'B2', tag: 'td' },
|
|
1588
|
+
],
|
|
1589
|
+
},
|
|
1590
|
+
],
|
|
1591
|
+
},
|
|
1592
|
+
innerBlocks: [],
|
|
1593
|
+
},
|
|
1594
|
+
];
|
|
1595
|
+
|
|
1596
|
+
// Set up doc1 (User A).
|
|
1597
|
+
mergeCrdtBlocks( yblocks, initialBlocks, null );
|
|
1598
|
+
|
|
1599
|
+
// Set up doc2 (User B) by syncing initial state.
|
|
1600
|
+
const doc2 = new Y.Doc();
|
|
1601
|
+
const yblocks2 = doc2.getArray< YBlock >();
|
|
1602
|
+
Y.applyUpdate( doc2, Y.encodeStateAsUpdate( doc ) );
|
|
1603
|
+
|
|
1604
|
+
// User A edits cell A1.
|
|
1605
|
+
const userABlocks: Block[] = [
|
|
1606
|
+
{
|
|
1607
|
+
name: 'core/table',
|
|
1608
|
+
attributes: {
|
|
1609
|
+
body: [
|
|
1610
|
+
{
|
|
1611
|
+
cells: [
|
|
1612
|
+
{ content: 'A1-userA', tag: 'td' },
|
|
1613
|
+
{ content: 'B1', tag: 'td' },
|
|
1614
|
+
],
|
|
1615
|
+
},
|
|
1616
|
+
{
|
|
1617
|
+
cells: [
|
|
1618
|
+
{ content: 'A2', tag: 'td' },
|
|
1619
|
+
{ content: 'B2', tag: 'td' },
|
|
1620
|
+
],
|
|
1621
|
+
},
|
|
1622
|
+
],
|
|
1623
|
+
},
|
|
1624
|
+
innerBlocks: [],
|
|
1625
|
+
},
|
|
1626
|
+
];
|
|
1627
|
+
mergeCrdtBlocks( yblocks, userABlocks, null );
|
|
1628
|
+
|
|
1629
|
+
// User B appends a third row (concurrently, before syncing).
|
|
1630
|
+
const userBBlocks: Block[] = [
|
|
1631
|
+
{
|
|
1632
|
+
name: 'core/table',
|
|
1633
|
+
attributes: {
|
|
1634
|
+
body: [
|
|
1635
|
+
{
|
|
1636
|
+
cells: [
|
|
1637
|
+
{ content: 'A1', tag: 'td' },
|
|
1638
|
+
{ content: 'B1', tag: 'td' },
|
|
1639
|
+
],
|
|
1640
|
+
},
|
|
1641
|
+
{
|
|
1642
|
+
cells: [
|
|
1643
|
+
{ content: 'A2', tag: 'td' },
|
|
1644
|
+
{ content: 'B2', tag: 'td' },
|
|
1645
|
+
],
|
|
1646
|
+
},
|
|
1647
|
+
{
|
|
1648
|
+
cells: [
|
|
1649
|
+
{ content: 'A3', tag: 'td' },
|
|
1650
|
+
{ content: 'B3', tag: 'td' },
|
|
1651
|
+
],
|
|
1652
|
+
},
|
|
1653
|
+
],
|
|
1654
|
+
},
|
|
1655
|
+
innerBlocks: [],
|
|
1656
|
+
},
|
|
1657
|
+
];
|
|
1658
|
+
mergeCrdtBlocks( yblocks2, userBBlocks, null );
|
|
1659
|
+
|
|
1660
|
+
// Sync: apply each other's changes.
|
|
1661
|
+
const updateA = Y.encodeStateAsUpdate( doc );
|
|
1662
|
+
const updateB = Y.encodeStateAsUpdate( doc2 );
|
|
1663
|
+
Y.applyUpdate( doc2, updateA );
|
|
1664
|
+
Y.applyUpdate( doc, updateB );
|
|
1665
|
+
|
|
1666
|
+
// Both docs should have A's cell edit and B's new row.
|
|
1667
|
+
for ( const checkBlocks of [ yblocks, yblocks2 ] ) {
|
|
1668
|
+
const attrs = checkBlocks
|
|
1669
|
+
.get( 0 )
|
|
1670
|
+
.get( 'attributes' ) as YBlockAttributes;
|
|
1671
|
+
const body = (
|
|
1672
|
+
attrs.get( 'body' ) as Y.Array< unknown >
|
|
1673
|
+
).toJSON() as { cells: { content: string }[] }[];
|
|
1674
|
+
|
|
1675
|
+
expect( body.length ).toBe( 3 );
|
|
1676
|
+
expect( body[ 0 ].cells[ 0 ].content ).toBe( 'A1-userA' );
|
|
1677
|
+
expect( body[ 0 ].cells[ 1 ].content ).toBe( 'B1' );
|
|
1678
|
+
expect( body[ 1 ].cells[ 0 ].content ).toBe( 'A2' );
|
|
1679
|
+
expect( body[ 2 ].cells[ 0 ].content ).toBe( 'A3' );
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
doc2.destroy();
|
|
1683
|
+
} );
|
|
1684
|
+
|
|
1685
|
+
it( 'concurrent cell edit is preserved when another user deletes a different row', () => {
|
|
1686
|
+
// Two users: A edits a cell in row 1, B deletes row 2.
|
|
1687
|
+
// After sync, A's edit should survive and row 2 should be gone.
|
|
1688
|
+
const initialBlocks: Block[] = [
|
|
1689
|
+
{
|
|
1690
|
+
name: 'core/table',
|
|
1691
|
+
attributes: {
|
|
1692
|
+
body: [
|
|
1693
|
+
{
|
|
1694
|
+
cells: [
|
|
1695
|
+
{ content: 'A1', tag: 'td' },
|
|
1696
|
+
{ content: 'B1', tag: 'td' },
|
|
1697
|
+
],
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
cells: [
|
|
1701
|
+
{ content: 'A2', tag: 'td' },
|
|
1702
|
+
{ content: 'B2', tag: 'td' },
|
|
1703
|
+
],
|
|
1704
|
+
},
|
|
1705
|
+
{
|
|
1706
|
+
cells: [
|
|
1707
|
+
{ content: 'A3', tag: 'td' },
|
|
1708
|
+
{ content: 'B3', tag: 'td' },
|
|
1709
|
+
],
|
|
1710
|
+
},
|
|
1711
|
+
],
|
|
1712
|
+
},
|
|
1713
|
+
innerBlocks: [],
|
|
1714
|
+
},
|
|
1715
|
+
];
|
|
1716
|
+
|
|
1717
|
+
// Set up doc1 (User A).
|
|
1718
|
+
mergeCrdtBlocks( yblocks, initialBlocks, null );
|
|
1719
|
+
|
|
1720
|
+
// Set up doc2 (User B) by syncing initial state.
|
|
1721
|
+
const doc2 = new Y.Doc();
|
|
1722
|
+
const yblocks2 = doc2.getArray< YBlock >();
|
|
1723
|
+
Y.applyUpdate( doc2, Y.encodeStateAsUpdate( doc ) );
|
|
1724
|
+
|
|
1725
|
+
// User A edits cell A1 in row 1.
|
|
1726
|
+
const userABlocks: Block[] = [
|
|
1727
|
+
{
|
|
1728
|
+
name: 'core/table',
|
|
1729
|
+
attributes: {
|
|
1730
|
+
body: [
|
|
1731
|
+
{
|
|
1732
|
+
cells: [
|
|
1733
|
+
{ content: 'A1-userA', tag: 'td' },
|
|
1734
|
+
{ content: 'B1', tag: 'td' },
|
|
1735
|
+
],
|
|
1736
|
+
},
|
|
1737
|
+
{
|
|
1738
|
+
cells: [
|
|
1739
|
+
{ content: 'A2', tag: 'td' },
|
|
1740
|
+
{ content: 'B2', tag: 'td' },
|
|
1741
|
+
],
|
|
1742
|
+
},
|
|
1743
|
+
{
|
|
1744
|
+
cells: [
|
|
1745
|
+
{ content: 'A3', tag: 'td' },
|
|
1746
|
+
{ content: 'B3', tag: 'td' },
|
|
1747
|
+
],
|
|
1748
|
+
},
|
|
1749
|
+
],
|
|
1750
|
+
},
|
|
1751
|
+
innerBlocks: [],
|
|
1752
|
+
},
|
|
1753
|
+
];
|
|
1754
|
+
mergeCrdtBlocks( yblocks, userABlocks, null );
|
|
1755
|
+
|
|
1756
|
+
// User B deletes row 2 (the middle row).
|
|
1757
|
+
const userBBlocks: Block[] = [
|
|
1758
|
+
{
|
|
1759
|
+
name: 'core/table',
|
|
1760
|
+
attributes: {
|
|
1761
|
+
body: [
|
|
1762
|
+
{
|
|
1763
|
+
cells: [
|
|
1764
|
+
{ content: 'A1', tag: 'td' },
|
|
1765
|
+
{ content: 'B1', tag: 'td' },
|
|
1766
|
+
],
|
|
1767
|
+
},
|
|
1768
|
+
{
|
|
1769
|
+
cells: [
|
|
1770
|
+
{ content: 'A3', tag: 'td' },
|
|
1771
|
+
{ content: 'B3', tag: 'td' },
|
|
1772
|
+
],
|
|
1773
|
+
},
|
|
1774
|
+
],
|
|
1775
|
+
},
|
|
1776
|
+
innerBlocks: [],
|
|
1777
|
+
},
|
|
1778
|
+
];
|
|
1779
|
+
mergeCrdtBlocks( yblocks2, userBBlocks, null );
|
|
1780
|
+
|
|
1781
|
+
// Sync: apply each other's changes.
|
|
1782
|
+
const updateA = Y.encodeStateAsUpdate( doc );
|
|
1783
|
+
const updateB = Y.encodeStateAsUpdate( doc2 );
|
|
1784
|
+
Y.applyUpdate( doc2, updateA );
|
|
1785
|
+
Y.applyUpdate( doc, updateB );
|
|
1786
|
+
|
|
1787
|
+
// Both docs should have A's cell edit and B's row deletion.
|
|
1788
|
+
for ( const checkBlocks of [ yblocks, yblocks2 ] ) {
|
|
1789
|
+
const attrs = checkBlocks
|
|
1790
|
+
.get( 0 )
|
|
1791
|
+
.get( 'attributes' ) as YBlockAttributes;
|
|
1792
|
+
const body = (
|
|
1793
|
+
attrs.get( 'body' ) as Y.Array< unknown >
|
|
1794
|
+
).toJSON() as { cells: { content: string }[] }[];
|
|
1795
|
+
|
|
1796
|
+
expect( body.length ).toBe( 2 );
|
|
1797
|
+
expect( body[ 0 ].cells[ 0 ].content ).toBe( 'A1-userA' );
|
|
1798
|
+
expect( body[ 1 ].cells[ 0 ].content ).toBe( 'A3' );
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
doc2.destroy();
|
|
1802
|
+
} );
|
|
1803
|
+
|
|
1804
|
+
it( 'preserves Y.Map identity for untouched rows when a row is appended', () => {
|
|
1805
|
+
const initialBlocks: Block[] = [
|
|
1806
|
+
{
|
|
1807
|
+
name: 'core/table',
|
|
1808
|
+
attributes: {
|
|
1809
|
+
body: [
|
|
1810
|
+
{
|
|
1811
|
+
cells: [ { content: 'A1', tag: 'td' } ],
|
|
1812
|
+
},
|
|
1813
|
+
],
|
|
1814
|
+
},
|
|
1815
|
+
innerBlocks: [],
|
|
1816
|
+
},
|
|
1817
|
+
];
|
|
1818
|
+
|
|
1819
|
+
mergeCrdtBlocks( yblocks, initialBlocks, null );
|
|
1820
|
+
|
|
1821
|
+
// Capture a reference to the Y.Map for the first row.
|
|
1822
|
+
const attrs = yblocks
|
|
1823
|
+
.get( 0 )
|
|
1824
|
+
.get( 'attributes' ) as YBlockAttributes;
|
|
1825
|
+
const body = attrs.get( 'body' ) as Y.Array< unknown >;
|
|
1826
|
+
const row0Before = body.get( 0 ) as Y.Map< unknown >;
|
|
1827
|
+
|
|
1828
|
+
// Append a second row.
|
|
1829
|
+
const updatedBlocks: Block[] = [
|
|
1830
|
+
{
|
|
1831
|
+
name: 'core/table',
|
|
1832
|
+
attributes: {
|
|
1833
|
+
body: [
|
|
1834
|
+
{
|
|
1835
|
+
cells: [ { content: 'A1', tag: 'td' } ],
|
|
1836
|
+
},
|
|
1837
|
+
{
|
|
1838
|
+
cells: [ { content: 'A2', tag: 'td' } ],
|
|
1839
|
+
},
|
|
1840
|
+
],
|
|
1841
|
+
},
|
|
1842
|
+
innerBlocks: [],
|
|
1843
|
+
},
|
|
1844
|
+
];
|
|
1845
|
+
|
|
1846
|
+
mergeCrdtBlocks( yblocks, updatedBlocks, null );
|
|
1847
|
+
|
|
1848
|
+
// The first row's Y.Map should be the exact same object.
|
|
1849
|
+
const row0After = body.get( 0 ) as Y.Map< unknown >;
|
|
1850
|
+
expect( row0After ).toBe( row0Before );
|
|
1851
|
+
|
|
1852
|
+
// And the new row should exist.
|
|
1853
|
+
expect( body.length ).toBe( 2 );
|
|
1854
|
+
const row1 = body.get( 1 ) as Y.Map< unknown >;
|
|
1855
|
+
const cells = ( row1.get( 'cells' ) as Y.Array< unknown > ).get(
|
|
1856
|
+
0
|
|
1857
|
+
) as Y.Map< unknown >;
|
|
1858
|
+
const content = cells.get( 'content' ) as Y.Text;
|
|
1859
|
+
expect( content.toString() ).toBe( 'A2' );
|
|
1860
|
+
} );
|
|
1861
|
+
|
|
1862
|
+
it( 'preserves Y.Map identity when a row is prepended', () => {
|
|
1863
|
+
const initialBlocks: Block[] = [
|
|
1864
|
+
{
|
|
1865
|
+
name: 'core/table',
|
|
1866
|
+
attributes: {
|
|
1867
|
+
body: [
|
|
1868
|
+
{
|
|
1869
|
+
cells: [ { content: 'A1', tag: 'td' } ],
|
|
1870
|
+
},
|
|
1871
|
+
{
|
|
1872
|
+
cells: [ { content: 'A2', tag: 'td' } ],
|
|
1873
|
+
},
|
|
1874
|
+
],
|
|
1875
|
+
},
|
|
1876
|
+
innerBlocks: [],
|
|
1877
|
+
},
|
|
1878
|
+
];
|
|
1879
|
+
|
|
1880
|
+
mergeCrdtBlocks( yblocks, initialBlocks, null );
|
|
1881
|
+
|
|
1882
|
+
const attrs = yblocks
|
|
1883
|
+
.get( 0 )
|
|
1884
|
+
.get( 'attributes' ) as YBlockAttributes;
|
|
1885
|
+
const body = attrs.get( 'body' ) as Y.Array< unknown >;
|
|
1886
|
+
const row0Before = body.get( 0 ) as Y.Map< unknown >;
|
|
1887
|
+
const row1Before = body.get( 1 ) as Y.Map< unknown >;
|
|
1888
|
+
|
|
1889
|
+
// Prepend a new row.
|
|
1890
|
+
const updatedBlocks: Block[] = [
|
|
1891
|
+
{
|
|
1892
|
+
name: 'core/table',
|
|
1893
|
+
attributes: {
|
|
1894
|
+
body: [
|
|
1895
|
+
{
|
|
1896
|
+
cells: [ { content: 'NEW', tag: 'td' } ],
|
|
1897
|
+
},
|
|
1898
|
+
{
|
|
1899
|
+
cells: [ { content: 'A1', tag: 'td' } ],
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
cells: [ { content: 'A2', tag: 'td' } ],
|
|
1903
|
+
},
|
|
1904
|
+
],
|
|
1905
|
+
},
|
|
1906
|
+
innerBlocks: [],
|
|
1907
|
+
},
|
|
1908
|
+
];
|
|
1909
|
+
|
|
1910
|
+
mergeCrdtBlocks( yblocks, updatedBlocks, null );
|
|
1911
|
+
|
|
1912
|
+
expect( body.length ).toBe( 3 );
|
|
1913
|
+
|
|
1914
|
+
const bodyJson = body.toJSON() as {
|
|
1915
|
+
cells: { content: string }[];
|
|
1916
|
+
}[];
|
|
1917
|
+
expect( bodyJson[ 0 ].cells[ 0 ].content ).toBe( 'NEW' );
|
|
1918
|
+
expect( bodyJson[ 1 ].cells[ 0 ].content ).toBe( 'A1' );
|
|
1919
|
+
expect( bodyJson[ 2 ].cells[ 0 ].content ).toBe( 'A2' );
|
|
1920
|
+
|
|
1921
|
+
// Original rows should be the same Y.Map objects (shifted).
|
|
1922
|
+
expect( body.get( 1 ) ).toBe( row0Before );
|
|
1923
|
+
expect( body.get( 2 ) ).toBe( row1Before );
|
|
1924
|
+
} );
|
|
1925
|
+
|
|
1926
|
+
it( 'preserves Y.Map identity when a row is inserted in the middle', () => {
|
|
1927
|
+
const initialBlocks: Block[] = [
|
|
1928
|
+
{
|
|
1929
|
+
name: 'core/table',
|
|
1930
|
+
attributes: {
|
|
1931
|
+
body: [
|
|
1932
|
+
{
|
|
1933
|
+
cells: [ { content: 'A1', tag: 'td' } ],
|
|
1934
|
+
},
|
|
1935
|
+
{
|
|
1936
|
+
cells: [ { content: 'A3', tag: 'td' } ],
|
|
1937
|
+
},
|
|
1938
|
+
],
|
|
1939
|
+
},
|
|
1940
|
+
innerBlocks: [],
|
|
1941
|
+
},
|
|
1942
|
+
];
|
|
1943
|
+
|
|
1944
|
+
mergeCrdtBlocks( yblocks, initialBlocks, null );
|
|
1945
|
+
|
|
1946
|
+
const attrs = yblocks
|
|
1947
|
+
.get( 0 )
|
|
1948
|
+
.get( 'attributes' ) as YBlockAttributes;
|
|
1949
|
+
const body = attrs.get( 'body' ) as Y.Array< unknown >;
|
|
1950
|
+
const row0Before = body.get( 0 ) as Y.Map< unknown >;
|
|
1951
|
+
const row1Before = body.get( 1 ) as Y.Map< unknown >;
|
|
1952
|
+
|
|
1953
|
+
// Insert a new row in the middle.
|
|
1954
|
+
const updatedBlocks: Block[] = [
|
|
1955
|
+
{
|
|
1956
|
+
name: 'core/table',
|
|
1957
|
+
attributes: {
|
|
1958
|
+
body: [
|
|
1959
|
+
{
|
|
1960
|
+
cells: [ { content: 'A1', tag: 'td' } ],
|
|
1961
|
+
},
|
|
1962
|
+
{
|
|
1963
|
+
cells: [ { content: 'A2', tag: 'td' } ],
|
|
1964
|
+
},
|
|
1965
|
+
{
|
|
1966
|
+
cells: [ { content: 'A3', tag: 'td' } ],
|
|
1967
|
+
},
|
|
1968
|
+
],
|
|
1969
|
+
},
|
|
1970
|
+
innerBlocks: [],
|
|
1971
|
+
},
|
|
1972
|
+
];
|
|
1973
|
+
|
|
1974
|
+
mergeCrdtBlocks( yblocks, updatedBlocks, null );
|
|
1975
|
+
|
|
1976
|
+
expect( body.length ).toBe( 3 );
|
|
1977
|
+
|
|
1978
|
+
const bodyJson = body.toJSON() as {
|
|
1979
|
+
cells: { content: string }[];
|
|
1980
|
+
}[];
|
|
1981
|
+
expect( bodyJson[ 0 ].cells[ 0 ].content ).toBe( 'A1' );
|
|
1982
|
+
expect( bodyJson[ 1 ].cells[ 0 ].content ).toBe( 'A2' );
|
|
1983
|
+
expect( bodyJson[ 2 ].cells[ 0 ].content ).toBe( 'A3' );
|
|
1984
|
+
|
|
1985
|
+
// Original rows should be preserved.
|
|
1986
|
+
expect( body.get( 0 ) ).toBe( row0Before );
|
|
1987
|
+
expect( body.get( 2 ) ).toBe( row1Before );
|
|
1988
|
+
} );
|
|
1989
|
+
|
|
1990
|
+
it( 'preserves Y.Map identity when a row is deleted from the end', () => {
|
|
1991
|
+
const initialBlocks: Block[] = [
|
|
1992
|
+
{
|
|
1993
|
+
name: 'core/table',
|
|
1994
|
+
attributes: {
|
|
1995
|
+
body: [
|
|
1996
|
+
{
|
|
1997
|
+
cells: [ { content: 'A1', tag: 'td' } ],
|
|
1998
|
+
},
|
|
1999
|
+
{
|
|
2000
|
+
cells: [ { content: 'A2', tag: 'td' } ],
|
|
2001
|
+
},
|
|
2002
|
+
{
|
|
2003
|
+
cells: [ { content: 'A3', tag: 'td' } ],
|
|
2004
|
+
},
|
|
2005
|
+
],
|
|
2006
|
+
},
|
|
2007
|
+
innerBlocks: [],
|
|
2008
|
+
},
|
|
2009
|
+
];
|
|
2010
|
+
|
|
2011
|
+
mergeCrdtBlocks( yblocks, initialBlocks, null );
|
|
2012
|
+
|
|
2013
|
+
const attrs = yblocks
|
|
2014
|
+
.get( 0 )
|
|
2015
|
+
.get( 'attributes' ) as YBlockAttributes;
|
|
2016
|
+
const body = attrs.get( 'body' ) as Y.Array< unknown >;
|
|
2017
|
+
const row0Before = body.get( 0 ) as Y.Map< unknown >;
|
|
2018
|
+
const row1Before = body.get( 1 ) as Y.Map< unknown >;
|
|
2019
|
+
|
|
2020
|
+
// Delete the last row.
|
|
2021
|
+
const updatedBlocks: Block[] = [
|
|
2022
|
+
{
|
|
2023
|
+
name: 'core/table',
|
|
2024
|
+
attributes: {
|
|
2025
|
+
body: [
|
|
2026
|
+
{
|
|
2027
|
+
cells: [ { content: 'A1', tag: 'td' } ],
|
|
2028
|
+
},
|
|
2029
|
+
{
|
|
2030
|
+
cells: [ { content: 'A2', tag: 'td' } ],
|
|
2031
|
+
},
|
|
2032
|
+
],
|
|
2033
|
+
},
|
|
2034
|
+
innerBlocks: [],
|
|
2035
|
+
},
|
|
2036
|
+
];
|
|
2037
|
+
|
|
2038
|
+
mergeCrdtBlocks( yblocks, updatedBlocks, null );
|
|
2039
|
+
|
|
2040
|
+
expect( body.length ).toBe( 2 );
|
|
2041
|
+
|
|
2042
|
+
const bodyJson = body.toJSON() as {
|
|
2043
|
+
cells: { content: string }[];
|
|
2044
|
+
}[];
|
|
2045
|
+
expect( bodyJson[ 0 ].cells[ 0 ].content ).toBe( 'A1' );
|
|
2046
|
+
expect( bodyJson[ 1 ].cells[ 0 ].content ).toBe( 'A2' );
|
|
2047
|
+
|
|
2048
|
+
// Remaining rows should be the same Y.Map objects.
|
|
2049
|
+
expect( body.get( 0 ) ).toBe( row0Before );
|
|
2050
|
+
expect( body.get( 1 ) ).toBe( row1Before );
|
|
2051
|
+
} );
|
|
2052
|
+
|
|
2053
|
+
it( 'updates all elements in-place when every row changes', () => {
|
|
2054
|
+
const initialBlocks: Block[] = [
|
|
2055
|
+
{
|
|
2056
|
+
name: 'core/table',
|
|
2057
|
+
attributes: {
|
|
2058
|
+
body: [
|
|
2059
|
+
{
|
|
2060
|
+
cells: [ { content: 'A1', tag: 'td' } ],
|
|
2061
|
+
},
|
|
2062
|
+
{
|
|
2063
|
+
cells: [ { content: 'A2', tag: 'td' } ],
|
|
2064
|
+
},
|
|
2065
|
+
],
|
|
2066
|
+
},
|
|
2067
|
+
innerBlocks: [],
|
|
2068
|
+
},
|
|
2069
|
+
];
|
|
2070
|
+
|
|
2071
|
+
mergeCrdtBlocks( yblocks, initialBlocks, null );
|
|
2072
|
+
|
|
2073
|
+
const attrs = yblocks
|
|
2074
|
+
.get( 0 )
|
|
2075
|
+
.get( 'attributes' ) as YBlockAttributes;
|
|
2076
|
+
const body = attrs.get( 'body' ) as Y.Array< unknown >;
|
|
2077
|
+
const row0Before = body.get( 0 ) as Y.Map< unknown >;
|
|
2078
|
+
const row1Before = body.get( 1 ) as Y.Map< unknown >;
|
|
2079
|
+
|
|
2080
|
+
// Replace all row contents.
|
|
2081
|
+
const updatedBlocks: Block[] = [
|
|
2082
|
+
{
|
|
2083
|
+
name: 'core/table',
|
|
2084
|
+
attributes: {
|
|
2085
|
+
body: [
|
|
2086
|
+
{
|
|
2087
|
+
cells: [ { content: 'X1', tag: 'td' } ],
|
|
2088
|
+
},
|
|
2089
|
+
{
|
|
2090
|
+
cells: [ { content: 'X2', tag: 'td' } ],
|
|
2091
|
+
},
|
|
2092
|
+
],
|
|
2093
|
+
},
|
|
2094
|
+
innerBlocks: [],
|
|
2095
|
+
},
|
|
2096
|
+
];
|
|
2097
|
+
|
|
2098
|
+
mergeCrdtBlocks( yblocks, updatedBlocks, null );
|
|
2099
|
+
|
|
2100
|
+
expect( body.length ).toBe( 2 );
|
|
2101
|
+
|
|
2102
|
+
const bodyJson = body.toJSON() as {
|
|
2103
|
+
cells: { content: string }[];
|
|
2104
|
+
}[];
|
|
2105
|
+
expect( bodyJson[ 0 ].cells[ 0 ].content ).toBe( 'X1' );
|
|
2106
|
+
expect( bodyJson[ 1 ].cells[ 0 ].content ).toBe( 'X2' );
|
|
2107
|
+
|
|
2108
|
+
// Y.Map objects should be updated in-place, not recreated.
|
|
2109
|
+
expect( body.get( 0 ) ).toBe( row0Before );
|
|
2110
|
+
expect( body.get( 1 ) ).toBe( row1Before );
|
|
2111
|
+
} );
|
|
2112
|
+
|
|
1570
2113
|
it( 'migrates plain array to Y.Array on first update', () => {
|
|
1571
2114
|
// Manually set up a block with a plain array body (old format).
|
|
1572
2115
|
const block = new Y.Map() as unknown as YBlock;
|