Skip to content

Commit 4943e3a

Browse files
committed
Fix keyedContainer behavior to unset entries without removing
1 parent be0e693 commit 4943e3a

File tree

3 files changed

+103
-15
lines changed

3 files changed

+103
-15
lines changed

src/lib/keyed_container.js

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,18 @@ var nestedProperty = require('./nested_property');
1212

1313
var SIMPLE_PROPERTY_REGEX = /^\w*$/;
1414

15-
// bitmask for deciding what's updated:
15+
// bitmask for deciding what's updated. Sometimes the name needs to be updated,
16+
// sometimes the value needs to be updated, and sometimes both do. This is just
17+
// a simple way to track what's updated such that it's a simple OR operation to
18+
// assimilate new updates.
19+
//
20+
// The only exception is the UNSET bit that tracks when we need to explicitly
21+
// unset and remove the property. This concrn arises because of the special
22+
// way in which nestedProperty handles null/undefined. When you specify `null`,
23+
// it prunes any unused items in the tree. I ran into some issues with it getting
24+
// null vs undefined confused, so UNSET is just a bit that forces the property
25+
// update to send `null`, removing the property explicitly rather than setting
26+
// it to undefined.
1627
var NONE = 0;
1728
var NAME = 1;
1829
var VALUE = 2;
@@ -111,14 +122,27 @@ module.exports = function keyedContainer(baseObj, path, keyName, valueName) {
111122
return obj.set(name, null);
112123
}
113124

114-
for(i = idx; i < arr.length; i++) {
115-
changeTypes[i] = changeTypes[i] | BOTH;
116-
}
117-
for(i = idx; i < arr.length; i++) {
118-
indexLookup[arr[i][keyName]]--;
125+
if(isSimpleValueProp) {
126+
for(i = idx; i < arr.length; i++) {
127+
changeTypes[i] = changeTypes[i] | BOTH;
128+
}
129+
for(i = idx; i < arr.length; i++) {
130+
indexLookup[arr[i][keyName]]--;
131+
}
132+
arr.splice(idx, 1);
133+
delete(indexLookup[name]);
134+
} else {
135+
// Perform this update *strictly* so we can check whether the result's
136+
// been pruned. If so, it's a removal. If not, it's a value unset only.
137+
nestedProperty(object, valueName).set(null);
138+
139+
// Now check if the top level nested property has any keys left. If so,
140+
// the object still has values so we only want to unset the key. If not,
141+
// the entire object can be removed since there's no other data.
142+
// var topLevelKeys = Object.keys(object[valueName.split('.')[0]] || []);
143+
144+
changeTypes[idx] = changeTypes[idx] | VALUE | UNSET;
119145
}
120-
arr.splice(idx, 1);
121-
delete(indexLookup[name]);
122146

123147
return obj;
124148
},

test/jasmine/tests/legend_test.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,10 @@ describe('legend interaction', function() {
945945
return _setValue(4, '');
946946
}).then(function() {
947947
// Verify the group names have been cleaned up:
948-
expect(gd.data[1].transforms[0].styles).toEqual([]);
948+
expect(gd.data[1].transforms[0].styles).toEqual([
949+
{target: 3},
950+
{target: 4}
951+
]);
949952
}).catch(fail).then(done);
950953
});
951954
});

test/jasmine/tests/lib_test.js

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1817,18 +1817,19 @@ describe('Test lib.js:', function() {
18171817

18181818
carr.set('name3', 'value3');
18191819
carr.remove('name2');
1820-
carr.rename('name1', 'name2');
1820+
carr.rename('name1', 'name4');
18211821

18221822
expect(container).toEqual({styles: [
1823-
{foo: 'name2', bar: {value: 'value1'}},
1823+
{foo: 'name4', bar: {value: 'value1'}},
1824+
{foo: 'name2'},
18241825
{foo: 'name3', bar: {value: 'value3'}}
18251826
]});
18261827

18271828
expect(carr.constructUpdate()).toEqual({
1828-
'styles[0].foo': 'name2',
1829-
'styles[1].foo': 'name3',
1830-
'styles[1].bar.value': 'value3',
1831-
'styles[2]': null
1829+
'styles[0].foo': 'name4',
1830+
'styles[1].bar.value': null,
1831+
'styles[2].foo': 'name3',
1832+
'styles[2].bar.value': 'value3',
18321833
});
18331834
});
18341835

@@ -1847,6 +1848,66 @@ describe('Test lib.js:', function() {
18471848
'styles[0].bar.value': null,
18481849
});
18491850
});
1851+
1852+
it('unsets but does not remove items with extra value data', function() {
1853+
var container = {styles: [
1854+
{foo: 'name1', bar: {value: 'value1', extra: 'data'}},
1855+
{foo: 'name2', bar: {value: 'value2'}},
1856+
{foo: 'name3', bar: {value: 'value3', extra: 'data'}},
1857+
]};
1858+
1859+
var carr = Lib.keyedContainer(container, 'styles', 'foo', 'bar.value');
1860+
1861+
// Remove the first value:
1862+
1863+
carr.remove('name1');
1864+
1865+
expect(container.styles).toEqual([
1866+
{foo: 'name1', bar: {extra: 'data'}},
1867+
{foo: 'name2', bar: {value: 'value2'}},
1868+
{foo: 'name3', bar: {value: 'value3', extra: 'data'}},
1869+
]);
1870+
1871+
expect(carr.constructUpdate()).toEqual({
1872+
'styles[0].bar.value': null
1873+
});
1874+
1875+
// Remove the second value:
1876+
carr.remove('name2');
1877+
1878+
expect(container.styles).toEqual([
1879+
{foo: 'name1', bar: {extra: 'data'}},
1880+
{foo: 'name2'},
1881+
{foo: 'name3', bar: {value: 'value3', extra: 'data'}},
1882+
]);
1883+
1884+
expect(carr.constructUpdate()).toEqual({
1885+
'styles[0].bar.value': null,
1886+
'styles[1].bar.value': null
1887+
});
1888+
});
1889+
1890+
it('does not compress nested attributes *sigh*', function() {
1891+
var container = {styles: [
1892+
{foo: 'name1', bar: {value: 'value1'}},
1893+
{foo: 'name2', bar: {value: 'value2', extra: 'data2'}},
1894+
]};
1895+
1896+
var carr = Lib.keyedContainer(container, 'styles', 'foo', 'bar.value');
1897+
1898+
// Remove the first value:
1899+
1900+
carr.remove('name1');
1901+
1902+
expect(container.styles).toEqual([
1903+
{foo: 'name1'},
1904+
{foo: 'name2', bar: {value: 'value2', extra: 'data2'}},
1905+
]);
1906+
1907+
expect(carr.constructUpdate()).toEqual({
1908+
'styles[0].bar.value': null
1909+
});
1910+
});
18501911
});
18511912
});
18521913

0 commit comments

Comments
 (0)