Skip to content

Commit de05640

Browse files
committed
Add message Class
1 parent efc6901 commit de05640

4 files changed

Lines changed: 135 additions & 65 deletions

File tree

js/src/forms/field.js

Lines changed: 42 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,26 @@
77
import { typeCheckConfig } from '../util/index'
88
import Messages from './messages'
99
import Manipulator from '../dom/manipulator'
10+
import EventHandler from '../dom/event-handler'
1011

1112
const NAME = 'field'
12-
const TYPE_PLACEHOLDER = '{type}'
13-
const CLASS_ERROR = `invalid-${TYPE_PLACEHOLDER}`
14-
const CLASS_INFO = `info-${TYPE_PLACEHOLDER}`
15-
const CLASS_SUCCESS = `valid-${TYPE_PLACEHOLDER}`
13+
const DATA_KEY = 'bs.field'
14+
const EVENT_KEY = `.${DATA_KEY}`
15+
const EVENT_INPUT = `input.${EVENT_KEY}`
16+
const CLASS_PREFIX_ERROR = 'invalid'
17+
const CLASS_PREFIX_INFO = 'info'
18+
const CLASS_PREFIX_SUCCESS = 'valid'
1619
const ARIA_DESCRIBED_BY = 'aria-describedby'
1720
const Default = {
1821
name: null,
1922
type: 'feedback', // or tooltip
20-
template: `<div class="field-${TYPE_PLACEHOLDER}"></div>`,
2123
valid: '', // valid message to add
2224
invalid: '' // invalid message to add
2325
}
2426

2527
const DefaultType = {
2628
name: 'string',
2729
type: 'string',
28-
template: 'string',
2930
valid: 'string',
3031
invalid: 'string'
3132
}
@@ -37,25 +38,31 @@ class Field {
3738
throw new TypeError(`field with id:${this._config.name} not found`)
3839
}
3940

40-
this._errorMessages = new Messages()
41-
this._helpMessages = new Messages()
42-
this._successMessages = new Messages()
4341
this._config = this._getConfig(config)
42+
43+
this._errorMessages = this._getNewMessagesCollection(CLASS_PREFIX_ERROR)
44+
this._helpMessages = this._getNewMessagesCollection(CLASS_PREFIX_INFO)
45+
this._successMessages = this._getNewMessagesCollection(CLASS_PREFIX_SUCCESS)
46+
47+
this._initializeMessageCollections()
4448
this._initialDescriptedBy = this._element.getAttribute(ARIA_DESCRIBED_BY)
45-
this._appended = null
49+
this._appendedFeedback = null
50+
EventHandler.on(this._element, EVENT_INPUT, () => {
51+
this.clearAppended()
52+
})
4653
}
4754

4855
getElement() {
4956
return this._element
5057
}
5158

5259
clearAppended() {
53-
if (!this._appended) {
60+
if (!this._appendedFeedback) {
5461
return
5562
}
5663

57-
this._appended.remove()
58-
this._appended = null
64+
this._appendedFeedback.remove()
65+
this._appendedFeedback = null
5966
if (this._initialDescriptedBy) {
6067
this._element.setAttribute(ARIA_DESCRIBED_BY, this._initialDescriptedBy)
6168
} else {
@@ -81,70 +88,54 @@ class Field {
8188
return this._successMessages
8289
}
8390

84-
appendFirstErrorMsg() {
85-
return this.appendFeedback(this.errorMessages().getFirst(), CLASS_ERROR)
86-
}
87-
88-
appendFirstHelpMsg() {
89-
return this.appendFeedback(this.helpMessages().getFirst(), CLASS_INFO)
90-
}
91-
92-
appendFirstSuccessMsg() {
93-
return this.appendFeedback(this.successMessages().getFirst(), CLASS_SUCCESS)
94-
}
95-
9691
_getConfig(config) {
9792
config = {
9893
...Default,
9994
...Manipulator.getDataAttributes(this._element),
10095
...(typeof config === 'object' ? config : {})
10196
}
10297

103-
if (config.invalid) {
104-
this.errorMessages().add(config.invalid)
105-
}
106-
107-
if (config.valid) {
108-
this.successMessages().add(config.valid)
109-
}
110-
11198
typeCheckConfig(NAME, config, DefaultType)
11299
return config
113100
}
114101

115-
appendFeedback(text, classAttr = '') {
102+
_appendFeedback(htmlElement) {
116103
this.clearAppended()
117-
if (!text) {
104+
if (!htmlElement) {
118105
return
119106
}
120107

121-
const feedbackElement = this._makeFeedbackElement(text, classAttr)
108+
const feedbackElement = htmlElement
122109

123-
this._appended = feedbackElement
110+
this._appendedFeedback = feedbackElement
124111

125112
this._element.parentNode.insertBefore(feedbackElement, this._element.nextSibling)
126-
113+
feedbackElement.id = this._getId()
127114
const describedBy = this._initialDescriptedBy ? `${this._initialDescriptedBy} ` : ''
128115
this._element.setAttribute(ARIA_DESCRIBED_BY, `${describedBy}${feedbackElement.id}`)
129116
}
130117

131-
_makeFeedbackElement(text, classAttr) {
132-
const element = document.createElement('div')
133-
element.innerHTML = this._setProperClassType(this._config.template)
134-
const feedback = element.children[0]
135-
feedback.classList.add(this._setProperClassType(classAttr))
136-
feedback.id = this._getId()
137-
feedback.innerHTML = text
138-
139-
return feedback
118+
_getId() {
119+
return `${this._config.name}-formTip`
140120
}
141121

142-
_setProperClassType(classPrefix) {
143-
return classPrefix.replaceAll(TYPE_PLACEHOLDER, this._config.type)
122+
_getNewMessagesCollection(classPrefix) {
123+
const config = {
124+
appendFunction: html => this._appendFeedback(html),
125+
type: this._config.type,
126+
classPrefix
127+
}
128+
return new Messages(config)
144129
}
145130

146-
_getId() {
147-
return `${this._config.name}-formTip`
131+
_initializeMessageCollections() {
132+
if (this._config.invalid) {
133+
this.errorMessages().add(this._config.invalid)
134+
}
135+
136+
if (this._config.valid) {
137+
this.successMessages().add(this._config.valid)
138+
}
148139
}
149140
}
150141

js/src/forms/form-validation.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ class FormValidation extends BaseComponent {
4040
this._config = this._getConfig(config)
4141

4242
this._addEventListeners()
43-
this._formElements = [...this._element.elements] // the DOM elements
4443
this._formFields = null // Our fields
4544
}
4645

@@ -62,7 +61,7 @@ class FormValidation extends BaseComponent {
6261

6362
appendErrors() {
6463
this.getFields().forEach(field => {
65-
field.appendFirstErrorMsg()
64+
field.errorMessages().appendFirst()
6665
})
6766
}
6867

@@ -73,7 +72,7 @@ class FormValidation extends BaseComponent {
7372
})
7473
}
7574

76-
autoValidate() {
75+
validate() {
7776
if (this._element.checkValidity()) {
7877
this.clear()
7978
return
@@ -82,15 +81,15 @@ class FormValidation extends BaseComponent {
8281
this.getFields().forEach(field => {
8382
const element = field.getElement()
8483
if (element.checkValidity()) {
85-
field.appendFirstSuccessMsg()
84+
field.successMessages().appendFirst()
8685
return
8786
}
8887

8988
if (!field.errorMessages().has()) { // if hasn't custom message, try to put the default
9089
field.errorMessages().add(element.validationMessage)
9190
}
9291

93-
field.appendFirstErrorMsg()
92+
field.errorMessages().appendFirst()
9493
})
9594

9695
this._element.classList.add(CLASS_VALIDATED)
@@ -115,7 +114,7 @@ class FormValidation extends BaseComponent {
115114

116115
_initializeFields() {
117116
const arrayFields = new Map()
118-
this._formElements.forEach(element => {
117+
Array.from(this._element.elements).forEach(element => {
119118
let { id } = element
120119
if (!id) {
121120
id = getUID(NAME)
@@ -134,13 +133,13 @@ class FormValidation extends BaseComponent {
134133

135134
EventHandler.on(document, EVENT_SUBMIT, SELECTOR_DATA_TOGGLE, event => {
136135
const { target } = event
137-
const data = FormValidation.getInstance(target) || new FormValidation(target)
136+
const data = FormValidation.getOrCreateInstance(target)
138137
if (!target.checkValidity()) {
139138
event.preventDefault()
140139
event.stopPropagation()
141140
}
142141

143-
data.autoValidate()
142+
data.validate()
144143
})
145144

146145
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {

js/src/forms/message.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { DefaultAllowlist, sanitizeHtml } from '../util/sanitizer'
2+
import { typeCheckConfig } from '../util/index'
3+
4+
/**
5+
* --------------------------------------------------------------------------
6+
* Bootstrap (v5.0.1): util/message.js
7+
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
8+
* --------------------------------------------------------------------------
9+
*/
10+
11+
const TYPE_PLACEHOLDER = '{type}'
12+
13+
const Default = {
14+
appendFunction: null,
15+
classPrefix: null,
16+
template: `<div class="field-${TYPE_PLACEHOLDER}"></div>`,
17+
text: null,
18+
type: null
19+
}
20+
21+
const DefaultType = {
22+
appendFunction: 'function',
23+
classPrefix: 'string',
24+
template: 'string',
25+
text: 'string',
26+
type: 'string'
27+
}
28+
29+
class Message {
30+
constructor(config) {
31+
this._config = this._getConfig(config)
32+
}
33+
34+
getText() {
35+
return this._config.message
36+
}
37+
38+
getHtml() {
39+
const element = document.createElement('div')
40+
element.innerHTML = this._setProperClassType(this._config.template)
41+
const feedback = element.children[0]
42+
const className = `${this._config.classPrefix}-${TYPE_PLACEHOLDER}`
43+
feedback.classList.add(this._setProperClassType(className))
44+
feedback.innerHTML = this._config.text
45+
46+
return feedback
47+
}
48+
49+
append() {
50+
this._config.appendFunction(this.getHtml())
51+
}
52+
53+
_setProperClassType(classPrefix) {
54+
return classPrefix.replaceAll(TYPE_PLACEHOLDER, this._config.type)
55+
}
56+
57+
_getConfig(config) {
58+
config = {
59+
...Default,
60+
...(typeof config === 'object' ? config : {})
61+
}
62+
config.text = sanitizeHtml(config.text, DefaultAllowlist, null)
63+
typeCheckConfig(Message, config, DefaultType)
64+
return config
65+
}
66+
}
67+
68+
export default Message

js/src/forms/messages.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
import { sanitizeHtml, DefaultAllowlist } from '../util/sanitizer'
2-
31
/**
42
* --------------------------------------------------------------------------
53
* Bootstrap (v5.0.1): util/messages.js
64
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
75
* --------------------------------------------------------------------------
86
*/
7+
import Message from './message'
98

109
class Messages {
11-
constructor() {
10+
constructor(messageConfig) {
11+
this._messageConfig = messageConfig
1212
this._messages = new Set()
1313
}
1414

1515
add(message) {
16-
this._messages.add(sanitizeHtml(message, DefaultAllowlist, null))
16+
const config = { ...this._messageConfig, ...{ text: message } }
17+
this._messages.add(new Message(config))
1718
}
1819

1920
has() {
@@ -25,11 +26,22 @@ class Messages {
2526
}
2627

2728
getAll() {
28-
return [...this._messages]
29+
return this._getAllMessagesAsArray().map(message => message.getText())
2930
}
3031

3132
getFirst() {
32-
return this.getAll()[0] || null
33+
return this._getAllMessagesAsArray()[0] || null
34+
}
35+
36+
appendFirst() {
37+
const message = this.getFirst()
38+
if (message) {
39+
message.append()
40+
}
41+
}
42+
43+
_getAllMessagesAsArray() {
44+
return [...this._messages]
3345
}
3446
}
3547

0 commit comments

Comments
 (0)