Skip to content

Commit

Permalink
feat: focus a new declaration prop automatically (#18)
Browse files Browse the repository at this point in the history
* refactor: split style declaration component

* chore: add ability to auto focus on style value component

* feat: focus a new declaration prop automatically

* test: add style declaration component test
  • Loading branch information
ktsn committed Mar 22, 2018
1 parent 81b88bf commit 0374aeb
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 46 deletions.
84 changes: 84 additions & 0 deletions src/view/components/StyleDeclaration.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<template>
<div class="style-declaration">
<span class="style-declaration-prop"><StyleValue
class="style-declaration-prop-text"
:auto-focus="autoFocusProp"
:value="prop"
@input="inputProp"
@input-end="finishInputProp"
/></span>
<span class="style-declaration-value"><StyleValue
:value="value"
@input="inputValue"
@input-end="finishInputValue"
/></span>
</div>
</template>

<script lang="ts">
import Vue from 'vue'
import StyleValue from './StyleValue.vue'
export default Vue.extend({
name: 'StyleDeclaration',
components: {
StyleValue
},
props: {
prop: {
type: String,
required: true
},
value: {
type: String,
required: true
},
autoFocusProp: Boolean
},
methods: {
inputProp(prop: string): void {
this.$emit('update:prop', prop)
},
inputValue(value: string): void {
this.$emit('update:value', value)
},
finishInputProp(rawProp: string): void {
const prop = rawProp.trim()
if (!prop) {
this.$emit('remove')
} else {
this.inputProp(prop)
}
},
finishInputValue(rawValue: string): void {
const value = rawValue.trim()
if (!value) {
this.$emit('remove')
} else {
this.inputValue(value)
}
}
}
})
</script>


<style lang="scss" scoped>
.style-declaration-prop::after {
content: ':';
}
.style-declaration-value::after {
content: ';';
}
.style-declaration-prop-text {
color: #24b600;
}
</style>
74 changes: 29 additions & 45 deletions src/view/components/StyleInformation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,14 @@

<ul class="declaration-list" @click.stop>
<li class="declaration" v-for="d in rule.declarations" :key="d.path.join('.')">
<span class="declaration-prop"><StyleValue
class="declaration-prop-text"
:value="d.prop"
@input="inputStyleProp(d.path, arguments[0])"
@input-end="finishInputStyleProp(d.path, arguments[0])"
/></span>
<span class="declaration-value"><StyleValue
<StyleDeclaration
:prop="d.prop"
:value="d.value"
@input="inputStyleValue(d.path, arguments[0])"
@input-end="finishInputStyleValue(d.path, arguments[0])"
/></span>
:auto-focus-prop="autoFocusOnNextRender"
@update:prop="updateDeclarationProp(d.path, arguments[0])"
@update:value="updateDeclarationValue(d.path, arguments[0])"
@remove="removeDeclaration(d.path)"
/>
</li>
</ul>
</li>
Expand All @@ -32,13 +29,15 @@
<script lang="ts">
import Vue from 'vue'
import StyleValue from './StyleValue.vue'
import StyleDeclaration from './StyleDeclaration.vue'
import { RuleForPrint } from '@/parser/style/types'
export default Vue.extend({
name: 'StyleInformation',
components: {
StyleValue
StyleValue,
StyleDeclaration
},
props: {
Expand All @@ -48,47 +47,44 @@ export default Vue.extend({
}
},
data() {
return {
autoFocusOnNextRender: false
}
},
methods: {
inputStyleProp(path: number[], prop: string): void {
updateDeclarationProp(path: number[], prop: string): void {
this.$emit('update-declaration', {
path,
prop
})
},
inputStyleValue(path: number[], value: string): void {
updateDeclarationValue(path: number[], value: string): void {
this.$emit('update-declaration', {
path,
value
})
},
finishInputStyleProp(path: number[], rawProp: string): void {
const prop = rawProp.trim()
if (!prop) {
this.$emit('remove-declaration', {
path
})
} else {
this.inputStyleProp(path, prop)
}
},
finishInputStyleValue(path: number[], rawValue: string): void {
const value = rawValue.trim()
if (!value) {
this.$emit('remove-declaration', {
path
})
} else {
this.inputStyleValue(path, value)
}
removeDeclaration(path: number[]): void {
this.$emit('remove-declaration', { path })
},
onClickRule(rule: RuleForPrint): void {
this.$emit('add-declaration', {
path: rule.path.concat(rule.declarations.length)
})
this.autoFocusOnNextRender = true
}
},
watch: {
rules(): void {
this.$nextTick(() => {
this.autoFocusOnNextRender = false
})
}
}
})
Expand Down Expand Up @@ -127,16 +123,4 @@ export default Vue.extend({
.declaration-list {
padding-left: 1em;
}
.declaration-prop::after {
content: ':';
}
.declaration-value::after {
content: ';';
}
.declaration-prop-text {
color: #24b600;
}
</style>
10 changes: 9 additions & 1 deletion src/view/components/StyleValue.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export default Vue.extend({
value: {
type: String,
required: true
}
},
autoFocus: Boolean
},
data() {
Expand Down Expand Up @@ -70,6 +72,12 @@ export default Vue.extend({
const el = event.currentTarget as HTMLDivElement
this.$emit('input', el.textContent)
}
},
mounted() {
if (this.autoFocus) {
this.startEdit()
}
}
})
</script>
Expand Down
71 changes: 71 additions & 0 deletions test/view/StyleDeclaration.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { mount } from '@vue/test-utils'
import StyleDeclaration from '@/view/components/StyleDeclaration.vue'
import StyleValue from '@/view/components/StyleValue.vue'

describe('StyleDeclaration', () => {
it('should notify prop update', () => {
const wrapper = mount(StyleDeclaration, {
propsData: {
prop: 'color',
value: 'red'
}
})

const prop = wrapper.find(StyleValue)
prop.vm.$emit('input', 'background-color')
prop.vm.$emit('input-end', 'background-color')

expect(wrapper.emitted('update:prop')[0]).toEqual(['background-color'])
expect(wrapper.emitted('remove')).toBe(undefined)
})

it('should notify value update', () => {
const wrapper = mount(StyleDeclaration, {
propsData: {
prop: 'color',
value: 'red'
}
})

const value = wrapper.findAll(StyleValue).at(1)
value.vm.$emit('input', 'blue')
value.vm.$emit('input-end', 'background-color')

expect(wrapper.emitted('update:value')[0]).toEqual(['blue'])
expect(wrapper.emitted('remove')).toBe(undefined)
})

it('should request removing when prop is empty', () => {
const wrapper = mount(StyleDeclaration, {
propsData: {
prop: 'color',
value: 'red'
}
})

const prop = wrapper.find(StyleValue)
prop.vm.$emit('input', '')
prop.vm.$emit('input-end', '')

expect(wrapper.emitted('update:prop').length).toBe(1)
expect(wrapper.emitted('update:prop')[0]).toEqual([''])
expect(wrapper.emitted('remove')).not.toBe(undefined)
})

it('should request removing when value is empty', () => {
const wrapper = mount(StyleDeclaration, {
propsData: {
prop: 'color',
value: 'red'
}
})

const value = wrapper.findAll(StyleValue).at(1)
value.vm.$emit('input', '')
value.vm.$emit('input-end', '')

expect(wrapper.emitted('update:value').length).toBe(1)
expect(wrapper.emitted('update:value')[0]).toEqual([''])
expect(wrapper.emitted('remove')).not.toBe(undefined)
})
})
13 changes: 13 additions & 0 deletions test/view/StyleValue/basic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,17 @@ describe('StyleValue basic', () => {
expect(wrapper.attributes()!.contenteditable).toBe('true')
expect(wrapper.text()).toBe('blue')
})

it('should be editable when autoFocus is specified', async () => {
const wrapper = mount(StyleValue, {
propsData: {
value: 'red',
autoFocus: true
}
})

await wrapper.vm.$nextTick()

expect(wrapper.attributes()!.contenteditable).toBe('true')
})
})

0 comments on commit 0374aeb

Please sign in to comment.