Skip to content
This repository has been archived by the owner on Dec 17, 2020. It is now read-only.

Commit

Permalink
feat(default): Add *-default properties and state inheritance capabil…
Browse files Browse the repository at this point in the history
…ities

This enhance the way state properties are retrieved from data nodes. If a default property is
undefined or null, state value will be inherited from parent recursively until a value is found.
  • Loading branch information
Toilal committed Jan 5, 2018
1 parent 3e6fc5c commit b97b06c
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 44 deletions.
37 changes: 37 additions & 0 deletions app/components/Docs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,15 @@ theme.register('my-template', myTemplate)
user interacts with the component view.
</td>
</tr>
<tr>
<td class="properties-table__name">opened-default</td>
<td class="properties-table__type"><code>Boolean</code></td>
<td class="properties-table__default"><code>true</code></td>
<td class="properties-table__description">Default opened state value used for nodes that have a
null or undefined value computed from <code>opened</code> component property.
When <code>undefined</code> or <code>null</code>, the state value is inherited from parent node.
</td>
</tr>
<tr>
<td class="properties-table__name">hidden</td>
<td class="properties-table__type"><code>String</code></td>
Expand Down Expand Up @@ -448,6 +457,15 @@ theme.register('my-template', myTemplate)
user interacts with the component view.
</td>
</tr>
<tr>
<td class="properties-table__name">hidden-default</td>
<td class="properties-table__type"><code>Boolean</code></td>
<td class="properties-table__default"><code>false</code></td>
<td class="properties-table__description">Default hidden state value used for nodes that have a
null or undefined value computed from <code>hidden</code> component property.
When <code>undefined</code> or <code>null</code>, the state value is inherited from parent node.
</td>
</tr>
<tr>
<td class="properties-table__name">selectable</td>
<td class="properties-table__type"><code>Boolean</code></td>
Expand All @@ -470,6 +488,15 @@ theme.register('my-template', myTemplate)
<td class="properties-table__description">Function that returns initial selectable state for given node.
</td>
</tr>
<tr>
<td class="properties-table__name">selectable-default</td>
<td class="properties-table__type"><code>Boolean</code></td>
<td class="properties-table__default"><code>false</code></td>
<td class="properties-table__description">Default selectable state value used for nodes that have a
null or undefined value computed from <code>selectable</code> component property.
When <code>undefined</code> or <code>null</code>, the state value is inherited from parent node.
</td>
</tr>
<tr>
<td class="properties-table__name">selected</td>
<td class="properties-table__type"><code>Boolean</code></td>
Expand Down Expand Up @@ -506,6 +533,15 @@ theme.register('my-template', myTemplate)
user interacts with the component view.
</td>
</tr>
<tr>
<td class="properties-table__name">selected-default</td>
<td class="properties-table__type"><code>Boolean</code></td>
<td class="properties-table__default"><code>false</code></td>
<td class="properties-table__description">Default selected state value used for nodes that have a
null or undefined value computed from <code>selected</code> component property.
When <code>undefined</code> or <code>null</code>, the state value is inherited from parent node.
</td>
</tr>
<tr>
<td class="properties-table__name">theme</td>
<td class="properties-table__type"><code>String</code></td>
Expand Down Expand Up @@ -596,6 +632,7 @@ theme.register('my-template', myTemplate)
.content {
.properties-table__name {
font-weight: bold;
white-space: nowrap;
}
.properties-table__type {
Expand Down
4 changes: 4 additions & 0 deletions src/VueTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
:children-async="childrenAsync"
:children="children"
:hidden="hidden"
:hidden-default="hiddenDefault"
:opened="opened"
:opened-default="openedDefault"
:selectable="selectable"
:selectable-default="selectableDefault"
:selected="selected"
:selected-default="selectedDefault"
:label="label"
:leaf="leaf"
:theme="theme"></vue-tree-node>
Expand Down
27 changes: 17 additions & 10 deletions src/VueTreeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import Vue from 'vue'

import props from './props'
import {
AsyncTreeNodeChildrenLoader, PropertyGetter, PropertyMapper, TreeNode,
AsyncTreeNodeChildrenLoader,
PropertyGetter,
PropertyMapper,
TreeNode,
TreeNodeChildrenLoader
} from './vue-tree-types'
import { isString, getPropertyValue, setPropertyValue } from '@/utils'
import { getFirstValue, getPropertyValue, isString, setPropertyValue } from '@/utils'
import theme, { Theme, ThemeContext } from '@/theme'

export default Vue.extend({
Expand All @@ -17,29 +20,32 @@ export default Vue.extend({
childrenLoading: false as boolean,
childrenLoaded: false as boolean,
childrenNodes: null as TreeNode[] | null | undefined,
dataOpened: false as boolean,
dataHidden: false as boolean,
dataSelectable: false as boolean,
dataSelected: false as boolean
dataOpened: getFirstValue(this.openedDefault, (this.$parent as any).computedOpened, true) as boolean,
dataHidden: getFirstValue(this.hiddenDefault, (this.$parent as any).dataHidden, false) as boolean,
dataSelectable: getFirstValue(this.selectableDefault, (this.$parent as any).dataSelectable, false) as boolean,
dataSelected: getFirstValue(this.selectedDefault, (this.$parent as any).dataSelected, false) as boolean
}
},
watch: {
opened: {
immediate: true,
handler: function (opened: PropertyMapper<boolean> | PropertyGetter<boolean> | String | boolean) {
this.computedOpened = getPropertyValue(this.data, opened, true)
this.computedOpened = getPropertyValue(this.data, opened,
() => getFirstValue(this.openedDefault, (this.$parent as any).computedOpened, true))
}
},
hidden: {
immediate: true,
handler: function (hidden: PropertyGetter<boolean> | String | boolean) {
this.dataHidden = getPropertyValue(this.data, hidden, false)
this.dataHidden = getPropertyValue(this.data, hidden,
() => getFirstValue(this.hiddenDefault, (this.$parent as any).dataHidden, false))
}
},
selectable: {
immediate: true,
handler: function (selectable: PropertyGetter<boolean> | String | boolean) {
this.dataSelectable = getPropertyValue(this.data, selectable, false)
this.dataSelectable = getPropertyValue(this.data, selectable,
() => getFirstValue(this.selectableDefault, (this.$parent as any).dataSelectable, false))
}
},
dataSelected: function (selected: boolean) {
Expand All @@ -48,7 +54,8 @@ export default Vue.extend({
selected: {
immediate: true,
handler: function (selected: PropertyMapper<boolean> | PropertyGetter<boolean> | String | boolean) {
this.dataSelected = getPropertyValue(this.data, selected, false)
this.dataSelected = getPropertyValue(this.data, selected,
() => getFirstValue(this.selectedDefault, (this.$parent as any).dataSelected, false))
}
}
},
Expand Down
6 changes: 5 additions & 1 deletion src/VueTreeNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@
:data="child"
:children-async="childrenAsync"
:children="children"
:opened="opened"
:hidden="hidden"
:hidden-default="hiddenDefault"
:opened="opened"
:opened-default="openedDefault"
:selectable="selectable"
:selectable-default="selectableDefault"
:selected="selected"
:selected-default="selectedDefault"
:label="label"
:leaf="leaf"
:theme="themeInstance">
Expand Down
26 changes: 21 additions & 5 deletions src/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,46 @@ export default {
type: [Function, String],
default: 'leaf'
},
hidden: { // PropertyGetter<boolean> | String | boolean
type: [Function, String, Boolean],
default: function () {
return false
}
},
hiddenDefault: { // boolean
type: Boolean,
default: false
},
opened: { // PropertyMapper<boolean> | PropertyGetter<boolean> | String | boolean
type: [Object, Function, String, Boolean],
default: function () {
return true
}
},
hidden: { // PropertyGetter<boolean> | String | boolean
type: [Function, String, Boolean],
default: function () {
return false
}
openedDefault: { // boolean
type: Boolean,
default: true
},
selectable: {
type: [Function, String, Boolean], // PropertyGetter<boolean> | String | boolean
default: function () {
return false
}
},
selectableDefault: { // boolean
type: Boolean,
default: false
},
selected: { // PropertyMapper<boolean> | PropertyGetter<boolean> | String | boolean
type: [Object, Function, String, Boolean],
default: function () {
return false
}
},
selectedDefault: { // boolean
type: Boolean,
default: false
},
theme: { // Theme | string
type: [Object, String],
default: function () {
Expand Down
13 changes: 11 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,16 @@ export function isFunction (val: any): boolean {
return typeof val === 'function'
}

export function getPropertyValue<T> (data: any, property: PropertyMapper<T> | PropertyGetter<T> | String | T, defaultValue: T): T {
export function getFirstValue<T> (...values: T[]): T {
for (const value of values) {
if (value !== undefined && value !== null) {
return value
}
}
return values[values.length - 1]
}

export function getPropertyValue<T> (data: any, property: PropertyMapper<T> | PropertyGetter<T> | String | T, defaultValueProvider: () => T): T {
let value: T
if (isString(property)) {
value = data[property as string]
Expand All @@ -27,7 +36,7 @@ export function getPropertyValue<T> (data: any, property: PropertyMapper<T> | Pr
}

if (value === undefined) {
return defaultValue
return defaultValueProvider()
}

return value
Expand Down
48 changes: 23 additions & 25 deletions test/unit/specs/selectedProp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,31 +256,29 @@ describe('Selected', () => {
expect(checkedInput.element.parentElement!.parentElement!.parentElement!.textContent).toEqual('✖ #A')

return Vue.nextTick().then(() => {
return Vue.nextTick().then(() => {
checkedInput.trigger('click')

let otherInput = wrapper.findAll('input.vue-tree__selected-checkbox').at(2)
otherInput.trigger('click')

expect(wrapper.findAll('input.vue-tree__selected-checkbox:checked')).toHaveLength(1)
expect(wrapper.vm.$props.data).toEqual({
label: 'Test',
children: [{
label: 'Cat #1', children: [
{ label: '#1', selected: true },
{ label: '#2' },
{ label: '#3' },
{ label: '#4' }
]
},
{
label: 'Cat #2', children: [
{ label: '#A', selected: false },
{ label: '#B' },
{ label: '#C' }
]
}]
})
checkedInput.trigger('click')

let otherInput = wrapper.findAll('input.vue-tree__selected-checkbox').at(2)
otherInput.trigger('click')

expect(wrapper.findAll('input.vue-tree__selected-checkbox:checked')).toHaveLength(1)
expect(wrapper.vm.$props.data).toEqual({
label: 'Test',
children: [{
label: 'Cat #1', children: [
{ label: '#1', selected: true },
{ label: '#2' },
{ label: '#3' },
{ label: '#4' }
]
},
{
label: 'Cat #2', children: [
{ label: '#A', selected: false },
{ label: '#B' },
{ label: '#C' }
]
}]
})
})
})
Expand Down
2 changes: 1 addition & 1 deletion test/unit/specs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import VueTreeNode from '@/VueTreeNode.vue'

import { createWrapperArray, Wrapper, WrapperArray } from 'vue-test-utils'

export function isVisible(wrapper: Wrapper<any>) {
export function isVisible (wrapper: Wrapper<any>) {
const computedStyle = window.getComputedStyle(wrapper.element)
return computedStyle.display !== 'none'
}
Expand Down

0 comments on commit b97b06c

Please sign in to comment.