Skip to content

Commit

Permalink
feat(forms): add support for tooltip-style feedback text (#2188)
Browse files Browse the repository at this point in the history
* Update form-invalid-feedback.js

enable tooltip style invalid feedback

* Update form-valid-feedback.js

enable tooltip style feedback support

* Update form-group.js

enable tooltip style feedback support

* Update README.md

* Update form-invalid-feedback.spec.js

* Update form-valid-feedback.spec.js

* Delete form-invalid-feedback.html

* Delete form-invalid-feedback.js

* Delete form-valid-feedback.html

* Delete form-valid-feedback.js

* Update README.md

* lint

* Update form-invalid-feedback.spec.js

* Update form-valid-feedback.spec.js

* Update form-invalid-feedback.spec.js

* Update form-invalid-feedback.spec.js

* Update form-valid-feedback.spec.js

* Update form-invalid-feedback.spec.js

needs context for functional components

* Update form-valid-feedback.spec.js

use context for functional components

* Update form-invalid-feedback.spec.js

* Update form-invalid-feedback.spec.js

* Update form-invalid-feedback.spec.js

* Update form-invalid-feedback.spec.js

* Update form-invalid-feedback.spec.js

* Update form-invalid-feedback.spec.js

* Update form-group.js
  • Loading branch information
tmorehouse committed Nov 16, 2018
1 parent 5bdc2e6 commit 5203436
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 86 deletions.
5 changes: 5 additions & 0 deletions src/components/form-group/README.md
Expand Up @@ -241,6 +241,11 @@ by setting the prop `valid-feedback` or using the named slot `valid-feedback`.
Valid feedback is rendered using the [`<b-form-valid-feedback>`](/docs/components/form#helper-components)
form sub-componment.

### Feedback style
By default, when visible, feedback (valid or invalid) will show as a block of text. You can change
the feedback so that it shows as a static tooltip when visible, by setting the prop `tooltip` to `true`.


### Feedback limitations
**Note:** When using `<b-input-group>`, `<b-form-file>`, `<b-form-radio-group>`,
`<b-form-radio>`, `<b-form-checkbox-group>` or `<b-form-checkbox>` inside a
Expand Down
13 changes: 10 additions & 3 deletions src/components/form-group/form-group.js
Expand Up @@ -77,7 +77,8 @@ export default {
{
props: {
id: this.invalidFeedbackId,
forceShow: this.computedState === false
forceShow: this.computedState === false,
tooltip: this.tooltip
},
attrs: {
role: 'alert',
Expand All @@ -99,7 +100,8 @@ export default {
{
props: {
id: this.validFeedbackId,
forceShow: this.computedState === true
forceShow: this.computedState === true,
tooltip: this.tooltip
},
attrs: {
role: 'alert',
Expand Down Expand Up @@ -212,6 +214,10 @@ export default {
type: String,
default: null
},
tooltip: {
type: Boolean,
default: false
},
validated: {
type: Boolean,
default: false
Expand Down Expand Up @@ -246,7 +252,8 @@ export default {
},
inputLayoutClasses () {
return [
this.horizontal ? `col-${this.breakpoint}-${12 - Number(this.labelCols)}` : null
this.horizontal ? `col-${this.breakpoint}-${12 - Number(this.labelCols)}` : null,
this.tooltip ? 'position-relative' : null
]
},
hasLabel () {
Expand Down
19 changes: 18 additions & 1 deletion src/components/form/README.md
Expand Up @@ -180,6 +180,23 @@ See also:
- `<b-form-invalid-feedback>` Invalid feedback text blocks for input `invalid` states
- `<b-form-valid-feedback>` Valid feedback text blocks for input `valid` states

### Text helper
Display a block of help text below an input with the `<b-form-text>` helper component.
text is displayed with a muted color and slightly smaller font-size.

### Feedback helpers
The valid and invalid feedback helper components will display feedback (based on input state)
as a block of colored text. They rely on being placed after an input (sibling) and will show
based on the browser native validation state of the input. To force them to show,
set the prop `force-show`, or set the `was-validated` class on a parent element (such as a form).
See the **Validation** section below for additional details.

Use the optional Boolean prop `tooltip` to change the display from a
block to a static tooltip style. The feedback will typically appear below the form control.
When this mode is enabled, it is important that the parent container have a
`position: relative:` css style (or `position-relative` class). Note that tooltip style
feedback may, since it's positioning is static, obscure other inputs, labels, etc.


## Validation

Expand All @@ -189,7 +206,7 @@ on `<b-form>`.
Set the `validated` prop, on `<b-form>`, to `true` to add the Bootstrap V4 `.was-validated` class
to the form to trigger validation states

Refer to the [Bootstrap V4 Form Validation Documentation](https://getbootstrap.com/docs/4.0/components/forms/#validation)
Refer to the [Bootstrap V4 Form Validation Documentation](https://getbootstrap.com/docs/4.1/components/forms/#validation)
for details on the new Bootstrap V4 validation states.

### Validation mechanisms
Expand Down
8 changes: 0 additions & 8 deletions src/components/form/fixtures/form-invalid-feedback.html

This file was deleted.

3 changes: 0 additions & 3 deletions src/components/form/fixtures/form-invalid-feedback.js

This file was deleted.

8 changes: 0 additions & 8 deletions src/components/form/fixtures/form-valid-feedback.html

This file was deleted.

3 changes: 0 additions & 3 deletions src/components/form/fixtures/form-valid-feedback.js

This file was deleted.

11 changes: 9 additions & 2 deletions src/components/form/form-invalid-feedback.js
Expand Up @@ -9,6 +9,10 @@ export const props = {
type: String,
default: 'div'
},
tooltip: {
type: Boolean,
default: false
},
forceShow: {
type: Boolean,
default: false
Expand All @@ -22,8 +26,11 @@ export default {
return h(
props.tag,
mergeData(data, {
staticClass: 'invalid-feedback',
class: { 'd-block': props.forceShow },
class: {
'invalid-feedback': !props.tooltip,
'invalid-tooltip': props.tooltip,
'd-block': props.forceShow
},
attrs: { id: props.id }
}),
children
Expand Down
99 changes: 71 additions & 28 deletions src/components/form/form-invalid-feedback.spec.js
@@ -1,51 +1,94 @@
import { loadFixture, testVM } from '../../../tests/utils'
import Feedback from './form-invalid-feedback'
import { mount } from '@vue/test-utils'

describe('form-invalid-feedback', async () => {
beforeEach(loadFixture(__dirname, 'form-invalid-feedback'))
testVM()

it('default should have tag div', async () => {
const { app: { $refs } } = window
expect($refs.default).toBeElement('div')
const feedback = mount(Feedback)
expect(feedback.is('div')).toBe(true)
})

it('default should contain base class', async () => {
const { app: { $refs } } = window
expect($refs.default).toHaveClass('invalid-feedback')
const feedback = mount(Feedback)
expect(feedback.classes()).toContain('invalid-feedback')
})

it('default should not have class d-block', async () => {
const { app: { $refs } } = window
expect($refs.default).not.toHaveClass('d-block')
const feedback = mount(Feedback)
expect(feedback.classes()).not.toContain('d-block')
})

it('default should not have class invalid-tooltip', async () => {
const feedback = mount(Feedback)
expect(feedback.classes()).not.toContain('invalid-tooltip')
})

it('default should not have id', async () => {
const feedback = mount(Feedback)
expect(feedback.attributes('id')).not.toBeDefined()
})

it('default should have id', async () => {
const { app: { $refs } } = window
expect($refs.default.getAttribute('id')).toBe('default')
it('default should have user supplied id', async () => {
const feedback = mount(Feedback, {
context: {
props: {
id: 'foobar'
}
}
})
expect(feedback.attributes('id')).toBe('foobar')
})

it('tag should have tag small', async () => {
const { app: { $refs } } = window
expect($refs.tag).toBeElement('small')
it('should have tag small when tag=small', async () => {
const feedback = mount(Feedback, {
context: {
props: {
tag: 'small'
}
}
})
expect(feedback.is('small')).toBe(true)
})

it('tag should contain base class', async () => {
const { app: { $refs } } = window
expect($refs.tag).toHaveClass('invalid-feedback')
it('should contain class d-block when force-show is set', async () => {
const feedback = mount(Feedback, {
context: {
props: {
forceShow: true
}
}
})
expect(feedback.classes()).toContain('d-block')
})

it('show should have tag div', async () => {
const { app: { $refs } } = window
expect($refs.show).toBeElement('div')
it('should contain class invalid-tooltip when tooltip is set', async () => {
const feedback = mount(Feedback, {
context: {
props: {
tooltip: true
}
}
})
expect(feedback.classes()).toContain('invalid-tooltip')
})

it('show should contain base class', async () => {
const { app: { $refs } } = window
expect($refs.show).toHaveClass('invalid-feedback')
it('should not contain class invalid-feedback when tooltip is set', async () => {
const feedback = mount(Feedback, {
context: {
props: {
tooltip: true
}
}
})
expect(feedback.classes()).not.toContain('invalid-feedback')
})

it('show should contain class d-block', async () => {
const { app: { $refs } } = window
expect($refs.show).toHaveClass('d-block')
it('should have children in the default slot when supplied', async () => {
const feedback = mount(Feedback, {
context: {
children: ['foo', '<span>bar</span>']
}
})
expect(feedback.text()).toContain('foo')
expect(feedback.text()).toContain('bar')
})
})
11 changes: 9 additions & 2 deletions src/components/form/form-valid-feedback.js
Expand Up @@ -9,6 +9,10 @@ export const props = {
type: String,
default: 'div'
},
tooltip: {
type: Boolean,
default: false
},
forceShow: {
type: Boolean,
default: false
Expand All @@ -22,8 +26,11 @@ export default {
return h(
props.tag,
mergeData(data, {
staticClass: 'valid-feedback',
class: { 'd-block': props.forceShow },
class: {
'valid-feedback': !props.tooltip,
'valid-tooltip': props.tooltip,
'd-block': props.forceShow
},
attrs: { id: props.id }
}),
children
Expand Down
89 changes: 61 additions & 28 deletions src/components/form/form-valid-feedback.spec.js
@@ -1,51 +1,84 @@
import { loadFixture, testVM } from '../../../tests/utils'
import Feedback from './form-valid-feedback'
import { mount } from '@vue/test-utils'

describe('form-valid-feedback', async () => {
beforeEach(loadFixture(__dirname, 'form-valid-feedback'))
testVM()

it('default should have tag div', async () => {
const { app: { $refs } } = window
expect($refs.default).toBeElement('div')
const feedback = mount(Feedback)
expect(feedback.is('div')).toBe(true)
})

it('default should contain base class', async () => {
const { app: { $refs } } = window
expect($refs.default).toHaveClass('valid-feedback')
const feedback = mount(Feedback)
expect(feedback.classes()).toContain('valid-feedback')
})

it('default should not have class d-block', async () => {
const { app: { $refs } } = window
expect($refs.default).not.toHaveClass('d-block')
const feedback = mount(Feedback)
expect(feedback.classes()).not.toContain('d-block')
})

it('default should not have class valid-tooltip', async () => {
const feedback = mount(Feedback)
expect(feedback.classes()).not.toContain('valid-tooltip')
})

it('default should have id', async () => {
const { app: { $refs } } = window
expect($refs.default.getAttribute('id')).toBe('default')
it('default should not have id', async () => {
const feedback = mount(Feedback)
expect(feedback.attributes('id')).not.toBeDefined()
})

it('tag should have tag small', async () => {
const { app: { $refs } } = window
expect($refs.tag).toBeElement('small')
it('default should have user supplied id', async () => {
const feedback = mount(Feedback, {
context: {
props: {
id: 'foobar'
}
}
})
expect(feedback.attributes('id')).toBe('foobar')
})

it('tag should contain base class', async () => {
const { app: { $refs } } = window
expect($refs.tag).toHaveClass('valid-feedback')
it('should have tag small when tag=small', async () => {
const feedback = mount(Feedback, {
context: {
props: {
tag: 'small'
}
}
})
expect(feedback.is('small')).toBe(true)
})

it('show should have tag div', async () => {
const { app: { $refs } } = window
expect($refs.show).toBeElement('div')
it('should contain class d-block when force-show is set', async () => {
const feedback = mount(Feedback, {
context: {
props: {
forceShow: true
}
}
})
expect(feedback.classes()).toContain('d-block')
})

it('show should contain base class', async () => {
const { app: { $refs } } = window
expect($refs.show).toHaveClass('valid-feedback')
it('should contain class valid-tooltip when tooltip is set', async () => {
const feedback = mount(Feedback, {
context: {
props: {
tooltip: true
}
}
})
expect(feedback.classes()).toContain('valid-tooltip')
})

it('show should contain class d-block', async () => {
const { app: { $refs } } = window
expect($refs.show).toHaveClass('d-block')
it('should not contain class alid-feedback when tooltip is set', async () => {
const feedback = mount(Feedback, {
context: {
props: {
tooltip: true
}
}
})
expect(feedback.classes()).not.toContain('valid-feedback')
})
})

0 comments on commit 5203436

Please sign in to comment.