Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(TU-8694): Pass option to prefill first question #640

Merged
merged 1 commit into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ If you embed via HTML, you need to pass optinos as attributes with `data-tf-` pr
| keepSession | boolean | preserve form state when modal window is closed (and re-opened) | `false` |
| redirectTarget | string | target for [typeforms with redirect](https://www.typeform.com/help/a/redirect-on-completion-or-redirect-through-endings-360060589532/), valid values are `_self`, `_top`, `_blank` or `_parent` ([see docs on anchor target](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-target)) | `_parent` |
| disableScroll | boolean | disable navigation between questions via scrolling and swiping | `false` |
| preselect | object | preselect answer to the first question ([more info in help center](https://www.typeform.com/help/a/preselect-answers-through-typeform-links-for-advanced-users-4410202791060/)) | `undefined` |

## Options in plain HTML embed

Expand Down
41 changes: 41 additions & 0 deletions packages/demo-html/public/preselect-html.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!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>
</head>
<body>
<div class="element">I have z-index 10k</div>
<p>This embed preselects the answer to <strong>6</strong> and continues to next question.</p>
<p><a href="https://www.typeform.com/help/a/preselect-answers-through-typeform-links-for-advanced-users-4410202791060/">More info in this help center article.</a></p>
<div
id="wrapper"
data-tf-widget="HLjqXS5W"
data-tf-medium="demo-test"
data-tf-iframe-props="title=Foo Bar"
data-tf-preselect="37ca107f130fef3a=6"
></div>
<script src="./lib/embed.js"></script>
</body>
</html>
1 change: 1 addition & 0 deletions packages/embed/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ Closing and opening a typeform in modal window will restart the progress from th
| disableScroll | boolean | disable navigation between questions via scrolling and swiping | `false` |
| hubspot | boolean | enable HubSpot source tracking - for details see article [Set up source tracking for HubSpot](https://www.typeform.com/help/a/set-up-source-tracking-for-hub-spot-4413167079316/) | `false` |
| [fullScreen](https://codesandbox.io/s/github/Typeform/embed-demo/tree/main/demo-html/widget-full-screen-html) | boolean | enable full screen view, set `<body>` size, resize on screen resize - also when browser navbars are displayed on mobile | `false` |
| [preselect] (https://codesandbox.io/s/github/Typeform/embed-demo/tree/main/demo-html/preselect-html) | object | preselect answer to the first question ([more info in help center](https://www.typeform.com/help/a/preselect-answers-through-typeform-links-for-advanced-users-4410202791060/)) | `undefined` |

### Options in plain HTML embed

Expand Down
4 changes: 4 additions & 0 deletions packages/embed/src/base/url-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,8 @@ export type UrlOptions = {
* Do not use H1 tags inside the form
*/
noHeading?: boolean
/**
* Preselect first question (ref) with answer (ref)
*/
preselect?: Record<string, string>
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,6 @@ export const buildOptionsFromAttributes = (element: HTMLElement) => {
disableScroll: 'boolean',
fullScreen: 'boolean',
noHeading: 'boolean',
preselect: 'record',
})
}
8 changes: 7 additions & 1 deletion packages/embed/src/utils/build-iframe-src.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ describe('build-iframe-src', () => {
hubspot: true,
autoResize: true,
onEndingButtonClick: () => {},
noHeading: true,
preselect: {
question1: 'value2',
},
}
expect(buildIframeSrc({ formId: 'some-id', type: 'widget', embedId: 'embed-id', options })).toBe(
'https://form.typeform.com/to/some-id' +
Expand All @@ -134,9 +138,11 @@ describe('build-iframe-src', () => {
'&typeform-embed-handles-redirect=1' +
'&typeform-embed-auto-resize=true' +
'&typeform-embed-handle-ending-button-click=true' +
'&typeform-embed-no-heading=true' +
'&utm_foo=utm+foo+value&foobar=foobar%26value' +
'#foo=foo+value&bar=%40bar%26value%3F' +
'&hubspot_page_name=page+title&hubspot_page_url=http%3A%2F%2Flocalhost%2F'
'&hubspot_page_name=page+title&hubspot_page_url=http%3A%2F%2Flocalhost%2F' +
'&answers-question1=value2'
)
})

Expand Down
58 changes: 41 additions & 17 deletions packages/embed/src/utils/build-iframe-src.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,46 @@ const getBaseUrl = (formString: string, domain = DEFAULT_DOMAIN): URL => {
return new URL(`https://${domain}/to/${formString}`)
}

const makePreselectParam = (preselect?: Record<string, string>) => {
if (!preselect) {
return null
}
const questionRef = Object.keys(preselect).at(0)
const answerRef = (questionRef && preselect[questionRef]) || undefined

if (questionRef === undefined || answerRef === undefined) {
return null
}
return {
key: `answers-${questionRef}`,
value: answerRef,
}
}

const buildHashParams = (url: URL, options: BaseOptions & UrlOptions): string => {
const hashParams = new URLSearchParams()

if (options.hidden) {
Object.entries(options.hidden)
.filter(([, paramValue]) => isDefined(paramValue) && paramValue !== '')
.forEach(([paramName, paramValue]) => {
// if transitive params is true, make hidden field values take priority over transitive params
if (typeof options.transitiveSearchParams === 'boolean') {
url.searchParams.delete(paramName)
}
hashParams.set(paramName, paramValue)
})
}

const preselectParam = makePreselectParam(options.preselect)
if (preselectParam) {
const { key, value } = preselectParam
hashParams.set(key, value)
}

return hashParams.toString()
}

export const buildIframeSrc = (params: BuildIframeSrcOptions): string => {
const { domain, formId, type, embedId, options } = params
const queryParams = mapOptionsToQueryParams(type, embedId, addDefaultUrlOptions(options))
Expand All @@ -104,23 +144,7 @@ export const buildIframeSrc = (params: BuildIframeSrcOptions): string => {
options.hidden = { ...options.hidden, ...hubspotHiddenFields }
}

if (options.hidden) {
const searchParams = new URLSearchParams()

Object.entries(options.hidden)
.filter(([, paramValue]) => isDefined(paramValue) && paramValue !== '')
.forEach(([paramName, paramValue]) => {
// if transitive params is true, make hidden field values take priority over transitive params
if (typeof options.transitiveSearchParams === 'boolean') {
url.searchParams.delete(paramName)
}
searchParams.set(paramName, paramValue)
})
const hiddenFields = searchParams.toString()
if (hiddenFields) {
url.hash = hiddenFields
}
}
url.hash = buildHashParams(url, options)

return url.href
}
Expand Down
Loading