Skip to content

Commit

Permalink
✨ Use data attribute to specify custom trigger
Browse files Browse the repository at this point in the history
  • Loading branch information
BetaHuhn committed Jun 28, 2021
1 parent 2956a68 commit 2d758cb
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 90 deletions.
2 changes: 1 addition & 1 deletion dist/feedback-js.min.js

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
</head>

<body>
<h1>feedback-js Example</h1>
<p>This is an example to show how <a href="https://github.com/BetaHuhn/feedback-js">feedback-js</a> works.</p>
<p>To trigger the widget, click <a href="#" data-feedback-trigger>here</a> or the button in the bottom right corner.</p>
</body>

<script src="../dist/feedback-js.min.js" data-feedback-opts='{ "id": "test", "endpoint": "http://172.21.52.196:6600/form/feedback", "emailField": true }'></script>
<script src="../dist/feedback-js.min.js" data-feedback-opts='{ "id": "test", "endpoint": "http://172.21.52.196:6600/form/feedback", "emailField": true, "forceShowButton": true }'></script>
</html>
191 changes: 106 additions & 85 deletions src/feedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default class Feedback {
endpoint: '',
events: false,
emailField: false,
forceShowButton: false,
btnTitle: 'Feedback',
title: 'Feedback',
contactText: 'Want to chat?',
Expand Down Expand Up @@ -53,9 +54,32 @@ export default class Feedback {

}

_renderButton() {
/**
* Attach feedback styles and button to current page
* @param {boolean} renderButton Render the default button
*/
attach(renderButton = true) {
const div = document.createElement('div')
div.id = 'feedback-root'
document.body.insertBefore(div, document.body.firstChild)

const comment = document.createComment('feedback-js modal code')
document.body.insertBefore(comment, document.body.firstChild)

this.root = div

this._addStyle()

if (renderButton || this.options.forceShowButton) {
this.renderButton()
}
}

renderButton() {
if (!this.root) return

this.showDefaultBtn = true

const html = `
<div class="feedback-btn-wrapper">
<button id="feedback-btn" title="Give feedback">
Expand All @@ -69,11 +93,11 @@ export default class Feedback {

const button = document.getElementById('feedback-btn')
button.addEventListener('click', () => {
this._renderView()
this.renderModal()
})
}

_renderView() {
renderModal() {
if (!this.root) return

const html = `
Expand Down Expand Up @@ -102,26 +126,36 @@ export default class Feedback {

const button = document.getElementById('feedback-close')
button.addEventListener('click', () => {
this._renderButton()
this.closeModal()
})

Object.entries(this.options.types).forEach(([ id, item ]) => {
Object.keys(this.options.types).forEach((id) => {
const elem = document.getElementById(`feedback-item-${ id }`)

elem.onclick = () => {
this._renderForm(id, `${ item.icon } ${ item.text }`)
this.renderForm(id)
}
})
}

_renderForm(type, title) {
closeModal() {
this.root.innerHTML = ''

if (this.showDefaultBtn) {
this.renderButton()
}
}

renderForm(type) {
if (!this.root) return

const feedbackType = this.options.types[type]

const html = `
<div class="feedback-wrapper">
<div class="feedback-main">
<div class="feedback-header">
<p>${ title }</p>
<p>${ feedbackType.icon } ${ feedbackType.text }</p>
</div>
<div class="feedback-content">
${ this.options.emailField ? '<input id="feedback-email" type="email" name="email" placeholder="Email address (optional)">' : '' }
Expand All @@ -147,36 +181,78 @@ export default class Feedback {

const button = document.getElementById('feedback-close')
button.addEventListener('click', () => {
this._renderButton()
this.closeModal()
})

const back = document.getElementById('feedback-back')
back.addEventListener('click', () => {
this._renderView()
this.renderModal()
})

const submit = document.getElementById('feedback-submit')
submit.addEventListener('click', () => {
const message = document.getElementById('feedback-message').value
const email = this.options.emailField ? document.getElementById('feedback-email').value : undefined
this.submitForm()
})
}

const data = {
id: this.options.id,
email: email,
feedbackType: this.current,
url: window.location.href,
message: message
}
submitForm() {
const message = document.getElementById('feedback-message').value
const email = this.options.emailField ? document.getElementById('feedback-email').value : undefined

if (this.options.events) {
const event = new CustomEvent('feedback-submit', { detail: data })
window.dispatchEvent(event)
this._renderSuccess()
return
}
const data = {
id: this.options.id,
email: email,
feedbackType: this.current,
url: window.location.href,
message: message
}

this.send(data.feedbackType, data.message, data.url, data.email)
})
if (this.options.events) {
const event = new CustomEvent('feedback-submit', { detail: data })
window.dispatchEvent(event)
this._renderSuccess()
return
}

this.sendToEndpoint(data.feedbackType, data.message, data.url, data.email)
}

/**
* Send feedback to backend
* @param {string} feedbackType - type of feedback
* @param {string} message - the actual feedback message
* @param {string} [url] - url/page the feedback was collected on
* @param {string} [email] - email of user optional
*/
sendToEndpoint(feedbackType, message, url, email) {
if (!feedbackType || !message) {
if (!this.root) throw new Error('missing parameters')
return
}

const parsedData = {
id: this.options.id,
email: email,
feedbackType: feedbackType,
url: url,
message: message
}

this._renderLoading()

const request = new XMLHttpRequest()
request.open('POST', this.options.endpoint)
request.setRequestHeader('Content-type', 'application/json')
request.send(JSON.stringify(parsedData))
request.onreadystatechange = () => {
if (request.readyState === 4) {
if (request.status === 200) {
return this._renderSuccess()
}

this._renderFailed()
}
}
}

_renderLoading() {
Expand All @@ -190,7 +266,7 @@ export default class Feedback {

const button = document.getElementById('feedback-close')
button.addEventListener('click', () => {
this._renderButton()
this.closeModal()
})
}

Expand All @@ -208,7 +284,7 @@ export default class Feedback {
this.root.innerHTML = html

setTimeout(() => {
this._renderButton()
this.renderButton()
}, 3000)
}

Expand Down Expand Up @@ -237,65 +313,10 @@ export default class Feedback {

const button = document.getElementById('feedback-close')
button.addEventListener('click', () => {
this._renderButton()
this.closeModal()
})
}

/**
* Send feedback to backend
* @param {string} feedbackType - type of feedback
* @param {string} message - the actual feedback message
* @param {string} [url] - url/page the feedback was collected on
* @param {string} [email] - email of user optional
*/
send(feedbackType, message, url, email) {
if (!feedbackType || !message) {
if (!this.root) throw new Error('missing parameters')
return
}

const parsedData = {
id: this.options.id,
email: email,
feedbackType: feedbackType,
url: url,
message: message
}

this._renderLoading()

const request = new XMLHttpRequest()
request.open('POST', this.options.endpoint)
request.setRequestHeader('Content-type', 'application/json')
request.send(JSON.stringify(parsedData))
request.onreadystatechange = () => {
if (request.readyState === 4) {
if (request.status === 200) {
return this._renderSuccess()
}

this._renderFailed()
}
}
}

/**
* Attach feedback button to current page
*/
attach() {
const div = document.createElement('div')
div.id = 'feedback-root'
document.body.insertBefore(div, document.body.firstChild)

const comment = document.createComment('feedback-js modal code')
document.body.insertBefore(comment, document.body.firstChild)

this.root = div

this._addStyle()
this._renderButton()
}

_addStyle() {
const css = `
#feedback-root{
Expand Down
23 changes: 20 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ export default Feedback

const detect = () => {
const optsElem = document.querySelector('[data-feedback-opts]')
const buttonElems = document.querySelectorAll('[data-feedback-trigger]')

// If no attributes are found, assume programmatic usage and attach Feedback class to window
if (!optsElem) {
if (!optsElem && buttonElems.length < 1) {
window.Feedback = Feedback

return
Expand All @@ -18,8 +19,24 @@ const detect = () => {
window.addEventListener('load', () => {
window.feedback = new Feedback(options)

// Attach the feedback button to the page
window.feedback.attach()
const renderDefaultButton = buttonElems.length < 1

// Initalize the feedback widget
window.feedback.attach(renderDefaultButton)

// Attach event listeners to data-drkmd-toggle elements
if (!renderDefaultButton) {
buttonElems.forEach((item) => {
item.addEventListener('click', () => {
window.feedback.renderModal()
})
})

return
}

// Render default button
window.feedback.renderButton()
})
}

Expand Down

0 comments on commit 2d758cb

Please sign in to comment.