Skip to content

Commit 3e532c7

Browse files
authored
Merge pull request #146 from grapoza/rafactor-radio-handling
Refactors radio button initialization
2 parents 0fa37fe + fae4328 commit 3e532c7

14 files changed

+162
-196
lines changed

docsrc/demos.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ Support for checkboxes and radio buttons is built into the treeview.
297297
298298
To create a checkbox node, specify `input.type = 'checkbox'` on the node's `treeNodeSpec`. To initialize the node as checked, specify `state.input.value = true`.
299299

300-
To create a radio button node, specify `input.type = 'radio'` on the node's `treeNodeSpec`, give the node a name using the `input.name` property, and give the node a value using `input.value`. The name will determine the radio button group to which the radio button belongs. The values of the selected radio button within each group are stored at the tree level in the `radioGroupValues`, which is an object that maps the name for a group of radio buttons to the selected node's value. To initialize a node as checked pass a `radioGroupValues` object to the tree with the format `{ matchesGroupInputNameProp: 'matchesNodeInputValueProp' }`.
300+
To create a radio button node, specify `input.type = 'radio'` on the node's `treeNodeSpec`, give the node a name using the `input.name` property, and give the node a value using `input.value`. The name will determine the radio button group to which the radio button belongs. To initialize a node as checked set the node's `input.isInitialRadioGroupValue` to `true`. If multiple nodes within a radio button group are specified as `isInitialRadioGroupValue`, the last one in wins.
301301

302302
The convenience methods `getCheckedRadioButtons` and `getCheckedCheckboxes` are exposed on the tree component to make it easy to get the nodes that have been checked.
303303

@@ -306,15 +306,15 @@ The convenience methods `getCheckedRadioButtons` and `getCheckedCheckboxes` are
306306
<summary>
307307
```
308308
```html
309-
<tree id="customtree-inputs" :initial-model="model" :radio-group-values="radioGroupValues" ref="treeInputs"></tree>
309+
<tree id="customtree-inputs" :initial-model="model" ref="treeInputs"></tree>
310310
```
311311
```{=html5}
312312
</summary>
313313
```
314314
<!--- The leading spaces are to render the html aligned correctly --->
315315
```html
316316
<div id="app-inputs" class="demo-tree">
317-
<tree id="customtree-inputs" :initial-model="model" :radio-group-values="radioGroupValues" ref="treeInputs"></tree>
317+
<tree id="customtree-inputs" :initial-model="model" ref="treeInputs"></tree>
318318
<section id="checked-stuff-inputs">
319319
<button type="button" class="tree-processor-trigger" v-on:click="refreshCheckedList">What's been checked?</button>
320320
<ul id="checked-list-inputs">
@@ -339,7 +339,8 @@ The convenience methods `getCheckedRadioButtons` and `getCheckedCheckboxes` are
339339
input: {
340340
type: 'radio',
341341
name: 'radio1',
342-
value: 'aValueToSubmit'
342+
value: 'aValueToSubmit',
343+
isInitialRadioGroupValue: true
343344
}
344345
}
345346
},
@@ -393,8 +394,7 @@ The convenience methods `getCheckedRadioButtons` and `getCheckedCheckboxes` are
393394
}
394395
}
395396
],
396-
checkedNodes: [],
397-
radioGroupValues: { 'radio1': 'aValueToSubmit' }
397+
checkedNodes: []
398398
};
399399
},
400400
methods: {
@@ -413,7 +413,7 @@ The convenience methods `getCheckedRadioButtons` and `getCheckedCheckboxes` are
413413

414414
```{=html5}
415415
<div id="app-inputs" class="demo-tree">
416-
<tree id="customtree-inputs" :initial-model="model" :radio-group-values="radioGroupValues" ref="treeInputs"></tree>
416+
<tree id="customtree-inputs" :initial-model="model" ref="treeInputs"></tree>
417417
<section id="checked-stuff-inputs">
418418
<button type="button" class="tree-processor-trigger" v-on:click="refreshCheckedList">What's been checked?</button>
419419
<ul id="checked-list-inputs">
@@ -437,7 +437,8 @@ The convenience methods `getCheckedRadioButtons` and `getCheckedCheckboxes` are
437437
input: {
438438
type: 'radio',
439439
name: 'radio1',
440-
value: 'aValueToSubmit'
440+
value: 'aValueToSubmit',
441+
isInitialRadioGroupValue: true
441442
}
442443
}
443444
},
@@ -491,8 +492,7 @@ The convenience methods `getCheckedRadioButtons` and `getCheckedCheckboxes` are
491492
}
492493
}
493494
],
494-
checkedNodes: [],
495-
radioGroupValues: { 'radio1': 'aValueToSubmit' }
495+
checkedNodes: []
496496
};
497497
},
498498
methods: {

docsrc/index.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ To see it in action, try out the [demos](demos.html).
105105
| initialModel | Array | The data model containing [model data](#model-data) | - | Yes |
106106
| customAriaKeyMap | Object | An object, the properties of which are arrays to keyCodes for various actions | See [Aria](#setting-key-bindings) | |
107107
| modelDefaults | Object | An object containing defaults for all nodes that do not specify the given properties | `{}` | |
108-
| radioGroupValues | Object | An object, the properties of which correspond to radio button group selections | `{}` | |
109108
| selectionMode | String | How selection should operate (see [Selection Mode](#selection-mode)) | `null` (cannot select nodes) | |
110109
| skinClass | String | A class name to apply to the tree that specifies a skin to use (see [Skins](#skins)) | `"default-tree-view-skin"` | |
111110

@@ -168,14 +167,15 @@ The `treeNodeSpec` of the objects in the data model passed to the treeview's `in
168167
selectable: false,
169168
input: {
170169
type: 'radio',
171-
name: 'rbGroup1', // Used as the name attribute for the radio button
172-
value: 'thisValue' // Used as the value attribute for the radio button
170+
name: 'rbGroup1', // Used as the name attribute for the radio button
171+
value: 'thisValue', // Used as the value attribute for the radio button
172+
isInitialRadioGroupValue: true // Indicates this should be the initially selected value for the group
173173
},
174174
state: {
175175
expanded: true,
176176
selected: false
177177
// No input.value here; to let complex radio button groupings work, state value is
178-
// bound to a tree-level property. input.disabled, however, is valid here for radio buttons.
178+
// bound to an internal tree-level property. input.disabled, however, is valid here for radio buttons.
179179
},
180180
addChildCallback: () => Promise.resolve({ id: '1', label: 'label' })
181181
}
@@ -210,6 +210,7 @@ The `treeNodeSpec` property contains any data about the node's capabilities and
210210
| input.type | String | The type of input; valid values are `checkbox` or `radio` | - | Yes* |
211211
| input.name | String | The name attribute of the input; used with `radio` type | `'unspecifiedRadioName'` | |
212212
| input.value | String | The value attribute of the input; used with `radio` type | `label`'s value** | |
213+
| input.isInitialRadioGroupValue | Boolean | Indicates this should be the initially selected value for the group | `null` | |
213214
| state | Object | Contains the current state of the node | - | |
214215
| state.expanded | Boolean | True if this node's subnode list is expanded | `false` | |
215216
| state.selected | Boolean | True if the node is selected | `false` | |

src/components/TreeView.vue

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
:initial-model="nodeModel"
1212
:selection-mode="selectionMode"
1313
:tree-id="uniqueId"
14-
:radio-group-values="radioGroupValues"
14+
:initial-radio-group-values="radioGroupValues"
1515
@treeViewNodeClick="(t, e)=>$emit('treeViewNodeClick', t, e)"
1616
@treeViewNodeDblclick="(t, e)=>$emit('treeViewNodeDblclick', t, e)"
1717
@treeViewNodeCheckboxChange="(t, e)=>$emit('treeViewNodeCheckboxChange', t, e)"
@@ -61,11 +61,6 @@
6161
required: false,
6262
default: function () { return {}; }
6363
},
64-
radioGroupValues: {
65-
type: Object,
66-
required: false,
67-
default: function () { return {}; }
68-
},
6964
selectionMode: {
7065
type: String,
7166
required: false,
@@ -86,7 +81,8 @@
8681
data() {
8782
return {
8883
uniqueId: null,
89-
model: this.initialModel
84+
model: this.initialModel,
85+
radioGroupValues: {}
9086
};
9187
},
9288
computed: {

src/components/TreeViewNode.vue

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@
149149
:parent-id="model[idPropName]"
150150
:selection-mode="selectionMode"
151151
:tree-id="treeId"
152-
:radio-group-values="radioGroupValues"
152+
:initial-radio-group-values="radioGroupValues"
153153
:aria-key-map="ariaKeyMap"
154154
@treeViewNodeClick="(t, e)=>$emit('treeViewNodeClick', t, e)"
155155
@treeViewNodeDblclick="(t, e)=>$emit('treeViewNodeDblclick', t, e)"
@@ -200,7 +200,7 @@
200200
type: Object,
201201
required: true
202202
},
203-
radioGroupValues: {
203+
initialRadioGroupValues: {
204204
type: Object,
205205
required: true
206206
},
@@ -220,8 +220,9 @@
220220
},
221221
data() {
222222
return {
223+
elementsThatIgnoreClicks: 'input, .tree-view-node-self-expander, .tree-view-node-self-expander *, .tree-view-node-self-action, .tree-view-node-self-action *',
223224
model: this.initialModel,
224-
elementsThatIgnoreClicks: 'input, .tree-view-node-self-expander, .tree-view-node-self-expander *, .tree-view-node-self-action, .tree-view-node-self-action *'
225+
radioGroupValues: this.initialRadioGroupValues
225226
}
226227
},
227228
computed: {
@@ -386,6 +387,10 @@
386387
if (!this.radioGroupValues.hasOwnProperty(input.name)) {
387388
this.$set(this.radioGroupValues, input.name, '');
388389
}
390+
391+
if (input.isInitialRadioGroupValue === true) {
392+
this.$set(this.radioGroupValues, input.name, input.value);
393+
}
389394
}
390395
}
391396
},

tests/data/node-generator.js

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
/**
22
* Generates nodes, one per array element that is not itself an array.
33
* Array elements that are arrays recursively generate child nodes
4-
* of the last created node. Additionally, the initial state for radio
5-
* buttons will be added to the radioState parameter.
4+
* of the last created node.
65
*
76
* The node spec's node string should be matched by the regex:
87
* `[eE]?[sS]?[d]?[f]?[[cCrR]!?]?`
@@ -18,11 +17,10 @@
1817
* A node that allows adds will use the callback function passed to generateNodes.
1918
*
2019
* @param {Array<String, Array>} nodeSpec The node specification array.
21-
* @param {Object} radioState An object in which the state of radio button groups is generated. Essentially an "out".
2220
* @param {String} baseId The base string used in the node IDs.
2321
* @param {Function} addChildCallback A method that returns a Promise that resolves to the node data to add as a subnode.
2422
*/
25-
export function generateNodes(nodeSpec, radioState, baseId = "", addChildCallback = null) {
23+
export function generateNodes(nodeSpec, baseId = "", addChildCallback = null) {
2624
let nodes = [];
2725
let prevNode = null;
2826

@@ -32,7 +30,7 @@ export function generateNodes(nodeSpec, radioState, baseId = "", addChildCallbac
3230
if (prevNode === null) {
3331
return;
3432
}
35-
prevNode.children = generateNodes(item, radioState, prevNode.id, addChildCallback);
33+
prevNode.children = generateNodes(item, prevNode.id, addChildCallback);
3634
}
3735
else {
3836
let lowerItem = item.toLowerCase();
@@ -77,16 +75,8 @@ export function generateNodes(nodeSpec, radioState, baseId = "", addChildCallbac
7775
}
7876

7977
// Set up the radiobutton state in the radioState
80-
if (lowerItem.includes('r')) {
81-
if (!radioState.hasOwnProperty(prevNode.treeNodeSpec.input.name)) {
82-
radioState[prevNode.treeNodeSpec.input.name] = null;
83-
}
84-
85-
// Radio button selectedness can also be specified in the normal way by providing
86-
// the radio button data to the TreeView's radioGroupValues prop.
87-
if (item.includes('R')) {
88-
radioState[prevNode.treeNodeSpec.input.name] = prevNode.treeNodeSpec.input.value;
89-
}
78+
if (item.includes('R')) {
79+
prevNode.treeNodeSpec.input.isInitialRadioGroupValue = true;
9080
}
9181
}
9282

tests/local/radioBasic.html

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<div class="container">
1313
<h1>Basic Treeview Radiobutton Demo</h1>
1414
<div id="app">
15-
<tree id="customtree" :initial-model="model" :radio-group-values="radioGroupValues" ref="tree"></tree>
15+
<tree id="customtree" :initial-model="model" ref="tree"></tree>
1616
<section id="checkedStuff">
1717
<button type="button" @click="refreshCheckedList">What's been checked?</button>
1818
<ul id="checkedList">
@@ -32,8 +32,7 @@ <h1>Basic Treeview Radiobutton Demo</h1>
3232
data() {
3333
return {
3434
model: basicRadioData,
35-
checkedNodes: [],
36-
radioGroupValues: { 'radio1': 'aValueToSubmit' }
35+
checkedNodes: []
3736
};
3837
},
3938
methods: {

tests/local/radioBasic.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ export default [
99
input: {
1010
type: 'radio',
1111
name: 'radio1',
12-
value: 'aValueToSubmit'
12+
value: 'aValueToSubmit',
13+
isInitialRadioGroupValue: true
1314
},
1415
state: {
1516
expanded: false,
@@ -30,7 +31,8 @@ export default [
3031
selectable: true,
3132
input: {
3233
type: 'radio',
33-
name: 'radio2'
34+
name: 'radio2',
35+
isInitialRadioGroupValue: true
3436
},
3537
state: {
3638
expanded: false,

tests/unit/TreeView.eventHandling.spec.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ describe('TreeView.vue (event handling)', () => {
3131
describe('when a node fires a treeViewNodeClick event', () => {
3232

3333
beforeEach(() => {
34-
wrapper = createWrapper({ initialModel: generateNodes(['es'], {}), selectionMode: 'multiple' });
34+
wrapper = createWrapper({ initialModel: generateNodes(['es']), selectionMode: 'multiple' });
3535
wrapper.find(TreeViewNode).vm.$emit('treeViewNodeClick');
3636
});
3737

@@ -43,7 +43,7 @@ describe('TreeView.vue (event handling)', () => {
4343
describe('when a node fires a treeViewNodeDblclick event', () => {
4444

4545
beforeEach(() => {
46-
wrapper = createWrapper({ initialModel: generateNodes(['es'], {}), selectionMode: 'multiple' });
46+
wrapper = createWrapper({ initialModel: generateNodes(['es']), selectionMode: 'multiple' });
4747
wrapper.find(TreeViewNode).vm.$emit('treeViewNodeDblclick');
4848
});
4949

@@ -55,7 +55,7 @@ describe('TreeView.vue (event handling)', () => {
5555
describe('when a node fires a treeViewNodeCheckboxChange event', () => {
5656

5757
beforeEach(() => {
58-
wrapper = createWrapper({ initialModel: generateNodes(['es'], {}), selectionMode: 'multiple' });
58+
wrapper = createWrapper({ initialModel: generateNodes(['es']), selectionMode: 'multiple' });
5959
wrapper.find(TreeViewNode).vm.$emit('treeViewNodeCheckboxChange');
6060
});
6161

@@ -67,7 +67,7 @@ describe('TreeView.vue (event handling)', () => {
6767
describe('when a node fires a treeViewNodeRadioChange event', () => {
6868

6969
beforeEach(() => {
70-
wrapper = createWrapper({ initialModel: generateNodes(['es'], {}), selectionMode: 'multiple' });
70+
wrapper = createWrapper({ initialModel: generateNodes(['es']), selectionMode: 'multiple' });
7171
wrapper.find(TreeViewNode).vm.$emit('treeViewNodeRadioChange');
7272
});
7373

@@ -79,7 +79,7 @@ describe('TreeView.vue (event handling)', () => {
7979
describe('when a node fires a treeViewNodeExpandedChange event', () => {
8080

8181
beforeEach(() => {
82-
wrapper = createWrapper({ initialModel: generateNodes(['es'], {}), selectionMode: 'multiple' });
82+
wrapper = createWrapper({ initialModel: generateNodes(['es']), selectionMode: 'multiple' });
8383
wrapper.find(TreeViewNode).vm.$emit('treeViewNodeExpandedChange');
8484
});
8585

@@ -93,7 +93,7 @@ describe('TreeView.vue (event handling)', () => {
9393
describe('always', () => {
9494

9595
beforeEach(() => {
96-
wrapper = createWrapper({ initialModel: generateNodes(['eS', 'es'], {}), selectionMode: 'multiple' });
96+
wrapper = createWrapper({ initialModel: generateNodes(['eS', 'es']), selectionMode: 'multiple' });
9797
wrapper.find(TreeViewNode).vm.$emit('treeViewNodeSelectedChange', wrapper.vm.model[0]);
9898
});
9999

@@ -107,7 +107,7 @@ describe('TreeView.vue (event handling)', () => {
107107
describe('and the target node is selected', () => {
108108

109109
beforeEach(() => {
110-
wrapper = createWrapper({ initialModel: generateNodes(['eS', 'eS', 'es'], {}), selectionMode: 'single' });
110+
wrapper = createWrapper({ initialModel: generateNodes(['eS', 'eS', 'es']), selectionMode: 'single' });
111111
wrapper.find(TreeViewNode).vm.$emit('treeViewNodeSelectedChange', wrapper.vm.model[0]);
112112
});
113113

@@ -119,7 +119,7 @@ describe('TreeView.vue (event handling)', () => {
119119
describe('and the target node is not selected', () => {
120120

121121
beforeEach(() => {
122-
wrapper = createWrapper({ initialModel: generateNodes(['es', 'eS', 'es'], {}), selectionMode: 'single' });
122+
wrapper = createWrapper({ initialModel: generateNodes(['es', 'eS', 'es']), selectionMode: 'single' });
123123
wrapper.find(TreeViewNode).vm.$emit('treeViewNodeSelectedChange', wrapper.vm.model[0]);
124124
});
125125

@@ -133,7 +133,7 @@ describe('TreeView.vue (event handling)', () => {
133133
describe('when a node fires a treeViewNodeAdd event', () => {
134134

135135
beforeEach(() => {
136-
wrapper = createWrapper({ initialModel: generateNodes(['es'], {}), selectionMode: 'multiple' });
136+
wrapper = createWrapper({ initialModel: generateNodes(['es']), selectionMode: 'multiple' });
137137
wrapper.find(TreeViewNode).vm.$emit('treeViewNodeAdd');
138138
});
139139

@@ -145,7 +145,7 @@ describe('TreeView.vue (event handling)', () => {
145145
describe('when a node fires a treeViewNodeDelete event', () => {
146146

147147
beforeEach(() => {
148-
wrapper = createWrapper({ initialModel: generateNodes(['es'], {}), selectionMode: 'multiple' });
148+
wrapper = createWrapper({ initialModel: generateNodes(['es']), selectionMode: 'multiple' });
149149
wrapper.find(TreeViewNode).vm.$emit('treeViewNodeDelete', wrapper.vm.model[0]);
150150
});
151151

0 commit comments

Comments
 (0)