Skip to content

Commit be0e693

Browse files
committed
Extend keyedContainer to handle nested keys
1 parent 6aefb41 commit be0e693

File tree

3 files changed

+96
-14
lines changed

3 files changed

+96
-14
lines changed

src/lib/keyed_container.js

+24-7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var NONE = 0;
1717
var NAME = 1;
1818
var VALUE = 2;
1919
var BOTH = 3;
20+
var UNSET = 4;
2021

2122
module.exports = function keyedContainer(baseObj, path, keyName, valueName) {
2223
keyName = keyName || 'name';
@@ -43,16 +44,18 @@ module.exports = function keyedContainer(baseObj, path, keyName, valueName) {
4344
var obj = {
4445
// NB: this does not actually modify the baseObj
4546
set: function(name, value) {
46-
var changeType = NONE;
47+
var changeType = value === null ? UNSET : NONE;
48+
4749
var idx = indexLookup[name];
4850
if(idx === undefined) {
49-
changeType = BOTH;
51+
changeType = changeType | BOTH;
5052
idx = arr.length;
5153
indexLookup[name] = idx;
5254
} else if(value !== (isSimpleValueProp ? arr[idx][valueName] : nestedProperty(arr[idx], valueName).get())) {
53-
changeType = VALUE;
55+
changeType = changeType | VALUE;
5456
}
55-
var newValue = {};
57+
58+
var newValue = arr[idx] = arr[idx] || {};
5659
newValue[keyName] = name;
5760

5861
if(isSimpleValueProp) {
@@ -61,7 +64,11 @@ module.exports = function keyedContainer(baseObj, path, keyName, valueName) {
6164
nestedProperty(newValue, valueName).set(value);
6265
}
6366

64-
arr[idx] = newValue;
67+
// If it's not an unset, force that bit to be unset. This is all related to the fact
68+
// that undefined and null are a bit specially implemented in nestedProperties.
69+
if(value !== null) {
70+
changeType = changeType & ~UNSET;
71+
}
6572

6673
changeTypes[idx] = changeTypes[idx] | changeType;
6774

@@ -93,7 +100,17 @@ module.exports = function keyedContainer(baseObj, path, keyName, valueName) {
93100
},
94101
remove: function(name) {
95102
var idx = indexLookup[name];
103+
96104
if(idx === undefined) return obj;
105+
106+
var object = arr[idx];
107+
if(Object.keys(object).length > 2) {
108+
// This object contains more than just the key/value, so unset
109+
// the value without modifying the entry otherwise:
110+
changeTypes[idx] = changeTypes[idx] | VALUE;
111+
return obj.set(name, null);
112+
}
113+
97114
for(i = idx; i < arr.length; i++) {
98115
changeTypes[i] = changeTypes[i] | BOTH;
99116
}
@@ -118,9 +135,9 @@ module.exports = function keyedContainer(baseObj, path, keyName, valueName) {
118135
}
119136
if(changeTypes[idx] & VALUE) {
120137
if(isSimpleValueProp) {
121-
update[astr + '.' + valueName] = arr[idx][valueName];
138+
update[astr + '.' + valueName] = (changeTypes[idx] & UNSET) ? null : arr[idx][valueName];
122139
} else {
123-
update[astr + '.' + valueName] = nestedProperty(arr[idx], valueName).get();
140+
update[astr + '.' + valueName] = (changeTypes[idx] & UNSET) ? null : nestedProperty(arr[idx], valueName).get();
124141
}
125142
}
126143
} else {

test/jasmine/tests/legend_test.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,7 @@ describe('legend interaction', function() {
885885
});
886886
});
887887

888+
888889
describe('editable mode interactions', function() {
889890
var gd;
890891
var mock = {
@@ -927,15 +928,15 @@ describe('legend interaction', function() {
927928
// Set the name of the third legend item:
928929
return _setValue(3, 'bar');
929930
}).then(function() {
930-
expect(gd.data[1].transforms[0].groupnames).toEqual([
931-
{name: 'bar', group: 3}
931+
expect(gd.data[1].transforms[0].styles).toEqual([
932+
{value: {name: 'bar'}, target: 3}
932933
]);
933934
}).then(function() {
934935
return _setValue(4, 'asdf');
935936
}).then(function() {
936-
expect(gd.data[1].transforms[0].groupnames).toEqual([
937-
{name: 'bar', group: 3},
938-
{name: 'asdf', group: 4}
937+
expect(gd.data[1].transforms[0].styles).toEqual([
938+
{value: {name: 'bar'}, target: 3},
939+
{value: {name: 'asdf'}, target: 4}
939940
]);
940941
}).then(function() {
941942
// Unset the group names:
@@ -944,7 +945,7 @@ describe('legend interaction', function() {
944945
return _setValue(4, '');
945946
}).then(function() {
946947
// Verify the group names have been cleaned up:
947-
expect(gd.data[1].transforms[0].groupnames).toEqual([]);
948+
expect(gd.data[1].transforms[0].styles).toEqual([]);
948949
}).catch(fail).then(done);
949950
});
950951
});

test/jasmine/tests/lib_test.js

+65-1
Original file line numberDiff line numberDiff line change
@@ -1702,16 +1702,46 @@ describe('Test lib.js:', function() {
17021702
});
17031703

17041704
describe('with custom named properties', function() {
1705-
it('performs standard operations', function() {
1705+
it('performs all of the operations', function() {
17061706
var container = {styles: [
17071707
{foo: 'name1', bar: 'value1'},
17081708
{foo: 'name2', bar: 'value2'}
17091709
]};
17101710

17111711
var carr = Lib.keyedContainer(container, 'styles', 'foo', 'bar');
17121712

1713+
// SET A VALUE
1714+
17131715
carr.set('name3', 'value3');
1716+
1717+
expect(container).toEqual({styles: [
1718+
{foo: 'name1', bar: 'value1'},
1719+
{foo: 'name2', bar: 'value2'},
1720+
{foo: 'name3', bar: 'value3'}
1721+
]});
1722+
1723+
expect(carr.constructUpdate()).toEqual({
1724+
'styles[2].foo': 'name3',
1725+
'styles[2].bar': 'value3'
1726+
});
1727+
1728+
// REMOVE A VALUE
1729+
17141730
carr.remove('name2');
1731+
1732+
expect(container).toEqual({styles: [
1733+
{foo: 'name1', bar: 'value1'},
1734+
{foo: 'name3', bar: 'value3'}
1735+
]});
1736+
1737+
expect(carr.constructUpdate()).toEqual({
1738+
'styles[1].foo': 'name3',
1739+
'styles[1].bar': 'value3',
1740+
'styles[2]': null
1741+
});
1742+
1743+
// RENAME A VALUE
1744+
17151745
carr.rename('name1', 'name2');
17161746

17171747
expect(container).toEqual({styles: [
@@ -1725,6 +1755,24 @@ describe('Test lib.js:', function() {
17251755
'styles[1].bar': 'value3',
17261756
'styles[2]': null
17271757
});
1758+
1759+
// SET A VALUE
1760+
1761+
carr.set('name2', 'value2');
1762+
1763+
expect(container).toEqual({styles: [
1764+
{foo: 'name2', bar: 'value2'},
1765+
{foo: 'name3', bar: 'value3'}
1766+
]});
1767+
1768+
expect(carr.constructUpdate()).toEqual({
1769+
'styles[0].foo': 'name2',
1770+
'styles[0].bar': 'value2',
1771+
'styles[1].foo': 'name3',
1772+
'styles[1].bar': 'value3',
1773+
'styles[2]': null
1774+
});
1775+
17281776
});
17291777
});
17301778

@@ -1783,6 +1831,22 @@ describe('Test lib.js:', function() {
17831831
'styles[2]': null
17841832
});
17851833
});
1834+
1835+
it('unsets but does not remove items with extra top-level data', function() {
1836+
var container = {styles: [
1837+
{foo: 'name', bar: {value: 'value'}, extra: 'data'}
1838+
]};
1839+
1840+
var carr = Lib.keyedContainer(container, 'styles', 'foo', 'bar.value');
1841+
1842+
carr.remove('name');
1843+
1844+
expect(container.styles).toEqual([{foo: 'name', extra: 'data'}]);
1845+
1846+
expect(carr.constructUpdate()).toEqual({
1847+
'styles[0].bar.value': null,
1848+
});
1849+
});
17861850
});
17871851
});
17881852

0 commit comments

Comments
 (0)