Skip to content

Commit

Permalink
feat(TU-13163): Add the duplicate-detected callback (#660)
Browse files Browse the repository at this point in the history
* feat(TU-13163): Add the duplicate-detected callback

* feat(TU-13163): Update developer portal onDuplicateDetected docs

* feat(TU-13163): Update developer portal onDuplicateDetected docs

* feat(TU-13163): Add duplicate prevention callback example
  • Loading branch information
gierlas authored Aug 26, 2024
1 parent 4c9aea1 commit 90135e7
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 0 deletions.
32 changes: 32 additions & 0 deletions docs/callbacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Available callbacks:
- **onQuestionChanged** fires when user navigates between form questions
- **onHeightChanged** fires when height of currently displayed question changes
- **onEndingButtonClick** fires when user clicks on the button on your typeform ending screen (it also disables the redirect functionality)
- **onDuplicateDetected** fires when the respondent reaches the quota of responses defined in [the duplicate prevention setting](https://www.typeform.com/help/a/prevent-duplicate-responses-27917825492244/)

Each callback receives a payload object with `formId` to identify the typeform that sent the event.
Depending on the callback there might be more data in the payload - see examples below.
Expand Down Expand Up @@ -246,6 +247,37 @@ Or in HTML:
</script>
```

## onDuplicateDetected

The `onDuplicateDetected` callback will execute whenever we detect the respondent reached the quota of responses
defined in [the duplicate prevention setting](https://www.typeform.com/help/a/prevent-duplicate-responses-27917825492244/).

In JavaScript:

```javascript
import { createSlider } from '@typeform/embed'
import '@typeform/embed/build/css/slider.css'

createSlider('<form-id>', {
onDuplicateDetected: ({ formId }) => {
console.log(`Duplicate detected for form ${formId}`)
},
})
```

Or in HTML:

```html
<button data-tf-slider="<form-id>" data-tf-on-duplicate-detected="duplicateDetected">open</button>
<script src="//embed.typeform.com/next/embed.js"></script>
<script>
// this function needs to be available on global scope (window)
function duplicateDetected({ formId }) {
console.log(`Duplicate detected for form ${formId}`)
}
</script>
```

## What's next?

Learn more about [contributing](/embed/contribute), or see what other open-source developers have created on the [Community projects](/community/) page.
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ If you embed via HTML, you need to pass optinos as attributes with `data-tf-` pr
| onQuestionChanged | function | fires when user navigates between form questions | `undefined` |
| onHeightChanged | function | fires when form question height changes (eg. on navigation between questions or on error message) | `undefined` |
| onEndingButtonClick | function | fires when button on ending screen is clicked | `undefined` |
| onDuplicateDetected | function | fires when the respondent reaches the quota of responses defined in [the duplicate prevention setting](https://www.typeform.com/help/a/prevent-duplicate-responses-27917825492244/) | `undefined` |
| autoResize | string / boolean | resize form to always fit the displayed question height, avoid scrollbars in the form (inline widget only), set min and max height separated by coma, eg. `"200,600"` | `false` |
| shareGaInstance | string / boolean | shares Google Analytics instance of the host page with embedded typeform, you can provide your Google Analytics ID to specify which instance to share (if you have more than one in your page) | `false` |
| inlineOnMobile | boolean | removes placeholder welcome screen in mobile and makes form show inline instead of fullscreen | `false` |
Expand Down
42 changes: 42 additions & 0 deletions packages/demo-html/public/callbacks-duplicate-prevention.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Static HTML Demo</title>
<style>
#wrapper {
width: 100%;
max-width: 600px;
height: 400px;
margin: 0 auto;
}
.element {
position: fixed;
bottom: 0;
right: 0;
background: red;
color: white;
padding: 10px;
width: 200px;
height: 60px;
line-height: 40px;
z-index: 10000;
}
</style>
<link rel="stylesheet" href="./lib/css/widget.css" />
</head>
<body>
<div class="element">I have z-index 10k</div>
<div id="wrapper"></div>
<script src="./lib/embed.js"></script>
<script>
window.tf.createWidget('pfnUIIso', {
container: document.getElementById('wrapper'),
onDuplicateDetected: ({formId}) => {
console.log(`Form ${formId} is already answered`)
},
})
</script>
</body>
</html>
6 changes: 6 additions & 0 deletions packages/embed/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ Closing and opening a typeform in modal window will restart the progress from th
| [onQuestionChanged](https://codesandbox.io/s/github/Typeform/embed-demo/tree/main/demo-html/callbacks) | function | fires when user navigates between form questions | `undefined` |
| [onHeightChanged](https://codesandbox.io/s/github/Typeform/embed-demo/tree/main/demo-html/callbacks) | function | fires when form question height changes (eg. on navigation between questions or on error message) | `undefined` |
| [onEndingButtonClick](https://codesandbox.io/s/github/Typeform/embed-demo/tree/main/demo-html/callbacks) | function | fires when button on ending screen is clicked, disables button redirect functionality | `undefined` |
| onDuplicateDetected | function | fires when the respondent reaches the quota of responses defined in [the duplicate prevention setting](https://www.typeform.com/help/a/prevent-duplicate-responses-27917825492244/) | `undefined` |
| [autoResize](https://codesandbox.io/s/github/Typeform/embed-demo/tree/main/demo-html/widget-autoresize) | string / boolean | resize form to always fit the displayed question height, avoid scrollbars in the form (inline widget only), set min and max height separated by coma, eg. `"200,600"` | `false` |
| [shareGaInstance](https://codesandbox.io/s/github/Typeform/embed-demo/tree/main/demo-html/widget-inline) | string / boolean | shares Google Analytics instance of the host page with embedded typeform, you can provide your Google Analytics ID to specify which instance to share (if you have more than one in your page) | `false` |
| [inlineOnMobile](https://codesandbox.io/s/github/Typeform/embed-demo/tree/main/demo-html/widget-inline) | boolean | removes placeholder welcome screen in mobile and makes form show inline instead of fullscreen | `false` |
Expand Down Expand Up @@ -255,6 +256,9 @@ You can listen to form events by providing callback methods:
// for plans with "Redirect from ending screen" feature you also receive `ref`:
console.log(`Ending button clicked in end screen ${ref}`)
},
onDuplicateDetected: ({ formId }) => {
console.log(`User reached the quote of responses for form ${formId}`)
}
})
document.querySelector('#btn').click = () => {
Expand Down Expand Up @@ -285,6 +289,8 @@ Callback method receive payload object from the form. Each payload contains form
- onEndingButtonClick
- `formId` (string)
- `ref` (string) identifies the end screen (_Note:_ this is available for plans with "Redirect from ending screen" feature only.)
- onDuplicateDetected
- `formId` (string)

See [callbacks example in demo package](../../packages/demo-html/public/callbacks.html).

Expand Down
7 changes: 7 additions & 0 deletions packages/embed/src/base/actionable-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,11 @@ export type ActionableOptions = {
* @param {string} event.ref - End screen ref string (for plans with "Redirect from ending screen" feature).
*/
onEndingButtonClick?: (event: WithFormId & Partial<WithRef>) => void

/**
* Callback function that will be executed once we detect the current user reached the form answer quota.
* @param {Object} event - Event payload.
* @param {string} event.formId - Form ID string.
*/
onDuplicateDetected?: (event: WithFormId) => void
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ describe('build-options-from-attributes', () => {
data-tf-on-submit="onTypeformSubmit"
data-tf-on-question-changed="onTypeformQuestionChanged"
data-tf-on-height-changed="onTypeformHeightChanged"
data-tf-on-duplicate-detected="onDuplicateDetected"
data-tf-auto-resize="100,300"
data-tf-open="exit"
data-tf-open-value="3000"
Expand Down Expand Up @@ -45,6 +46,7 @@ describe('build-options-from-attributes', () => {
win.onTypeformSubmit = jest.fn()
win.onTypeformQuestionChanged = jest.fn()
win.onTypeformHeightChanged = jest.fn()
win.onDuplicateDetected = jest.fn()

const element = wrapper.querySelector('#element') as HTMLElement
const options = buildOptionsFromAttributes(element)
Expand All @@ -62,6 +64,7 @@ describe('build-options-from-attributes', () => {
onSubmit: win.onTypeformSubmit,
onQuestionChanged: win.onTypeformQuestionChanged,
onHeightChanged: win.onTypeformHeightChanged,
onDuplicateDetected: win.onDuplicateDetected,
autoResize: '100,300',
open: 'exit',
openValue: 3000,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const buildOptionsFromAttributes = (element: HTMLElement) => {
onSubmit: 'function',
onQuestionChanged: 'function',
onHeightChanged: 'function',
onDuplicateDetected: 'function',
autoResize: 'stringOrBoolean',
onClose: 'function',
onEndingButtonClick: 'function',
Expand Down
3 changes: 3 additions & 0 deletions packages/embed/src/utils/create-iframe/create-iframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
getThankYouScreenButtonClickHandler,
getFormStartedHandler,
getRedirectHandler,
getDuplicateDetectedHandler,
} from './get-form-event-handler'
import { triggerIframeRedraw } from './trigger-iframe-redraw'
import { dispatchCustomKeyEventFromIframe } from './setup-custom-keyboard-close'
Expand All @@ -35,6 +36,7 @@ export const createIframe = (type: EmbedType, { formId, domain, options }: Creat
onHeightChanged,
onSubmit,
onEndingButtonClick,
onDuplicateDetected,
shareGaInstance,
} = options

Expand Down Expand Up @@ -67,6 +69,7 @@ export const createIframe = (type: EmbedType, { formId, domain, options }: Creat
window.addEventListener('message', getFormThemeHandler(embedId, onTheme))
window.addEventListener('message', getThankYouScreenButtonClickHandler(embedId, onEndingButtonClick))
window.addEventListener('message', getRedirectHandler(embedId, iframe))
window.addEventListener('message', getDuplicateDetectedHandler(embedId, onDuplicateDetected))

if (type !== 'widget') {
window.addEventListener('message', dispatchCustomKeyEventFromIframe)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export const getFormSubmitHandler = (embedId: string, callback?: callbackFn) =>
return getFormEventHandler('form-submit', embedId, callback)
}

export const getDuplicateDetectedHandler = (embedId: string, callback?: callbackFn) => {
return getFormEventHandler('duplicate-detected', embedId, callback)
}

export const getWelcomeScreenHiddenHandler = (embedId: string, callback?: callbackFn) => {
return getFormEventHandler('welcome-screen-hidden', embedId, callback)
}
Expand Down

0 comments on commit 90135e7

Please sign in to comment.