Skip to content
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
4 changes: 2 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ If you embed via HTML, you need to pass optinos as attributes with `data-tf-` pr
| hideFooter | boolean | hide form progress bar and navigation buttons (does not apply to Chat UI) | `false` |
| hideHeaders | boolean | hide header that appears when you have a question group, or a long question (does not apply to Chat UI) | `false` |
| opacity | number | form background opacity, number from 0 (fully transparent) 100 (fully opaque) | `100` |
| disableAutoFocus | boolean | disable form auto focus when loaded | `false` |
| autoFocus | boolean | enable form auto focus when loaded | `false` |
| open | string | open embed based on user action (see below) | `undefined` |
| openValue | number | based on `open` (see below) | `undefined` |
| enableSandbox | boolean | enable [sandbox mode](https://help.typeform.com/hc/en-us/articles/360029295952) (disables submissions and tracking) | `false` |
Expand All @@ -74,7 +74,7 @@ If you embed via HTML, you need to pass optinos as attributes with `data-tf-` pr

## Options in plain HTML embed

To embed via HTML without writing JavaScript code, use `data-tf-widget="<form-id>"` for widget embed. You can define options as data attributes with `data-tf-` prefix and dashes in name (eg. `disableAutoFocus` becomes `data-tf-disable-auto-focus`). For example:
To embed via HTML without writing JavaScript code, use `data-tf-widget="<form-id>"` for widget embed. You can define options as data attributes with `data-tf-` prefix and dashes in name (eg. `autoFocus` becomes `data-tf-auto-focus`). For example:

```html
<div data-tf-popup="<form-id>" data-tf-size="50" data-tf-hide-footer></div>
Expand Down
54 changes: 54 additions & 0 deletions packages/demo-html/public/widget-focus.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!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>
#widget-wrapper {
width: 100%;
max-width: 600px;
height: 400px;
margin: 0 auto;
}
#popup-focus {
position: fixed;
top: 10px;
left: 10px;
z-index: 99999;
}
p {
text-align: center;
}
</style>
<link rel="stylesheet" href="./lib/css/widget.css" />
<link rel="stylesheet" href="./lib/css/popup.css" />
</head>
<body>
<h1>Auto focus and manual <code>focus()</code></h1>
<p>You can <button id="popup-button">open popup</button> and then use the button in top left to focus the typeform (once it looses focus).</p>
<button id="popup-focus">popup focus</button>

<div id="widget-wrapper"></div>
<p>You can <button id="widget-focus">focus the widget above</button> (once it looses focus).</p>

<script src="./lib/embed-next.js"></script>
<script>
const widget = window.tf.createWidget('Cqrg7cgL', {
container: document.getElementById('widget-wrapper'),
autoFocus: true,
})
document.getElementById('widget-focus').onclick = () => {
widget.focus()
}

const popup = window.tf.createPopup('Cqrg7cgL')
document.getElementById('popup-button').onclick = () => {
popup.toggle()
}
document.getElementById('popup-focus').onclick = () => {
popup.focus()
}
</script>
</body>
</html>
4 changes: 2 additions & 2 deletions packages/embed/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Closing and opening a typeform in modal window will restart the progress from th
| hideFooter | boolean | hide form progress bar and navigation buttons (does not apply to Chat UI) | `false` |
| hideHeaders | boolean | hide header that appears when you have a question group, or a long question (does not apply to Chat UI) | `false` |
| opacity | number | form background opacity, number from 0 (fully transparent) 100 (fully opaque) | `100` |
| disableAutoFocus | boolean | disable form auto focus when loaded | `false` |
| autoFocus | boolean | enable form auto focus when loaded | `false` |
| open | string | open embed based on user action (see below) | `undefined` |
| openValue | number | based on `open` (see below) | `undefined` |
| enableSandbox | boolean | enable [sandbox mode](https://help.typeform.com/hc/en-us/articles/360029295952) (disables submissions and tracking) | `false` |
Expand All @@ -155,7 +155,7 @@ Closing and opening a typeform in modal window will restart the progress from th
### Options in plain HTML embed

- to embed via HTML without writing JavaScript code, use `data-tf-widget="<form-id>"` for widget embed (see example above)
- define options as data attributes with `data-tf-` prefix and dashes in name (eg. `disableAutoFocus` becomes `data-tf-disable-auto-focus`)
- define options as data attributes with `data-tf-` prefix and dashes in name (eg. `autoFocus` becomes `data-tf-auto-focus`)
- set a boolean property to `true` by omitting attribute value, (eg. `<div ... data-tf-disable-footer></div>`
- pass function name for callbacks, eg. `data-tf-on-ready="myReadyFunction"` if this function is available on global scope (eg. `window`)
- to pass `string[]` use comma-separated string, eg. `transitiveSearchParams: ['foo', 'bar']` becomes `data-tf-transitive-search-params="foo,bar"`
Expand Down
12 changes: 12 additions & 0 deletions packages/embed/src/base/embed-types.ts
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
export type EmbedType = 'widget' | 'popup' | 'slider' | 'popover' | 'side-tab'

export type EmbedWidget = {
unmount: () => void
refresh: () => void
focus: () => void
}

export type EmbedPopup = EmbedWidget & {
open: () => void
close: () => void
toggle: () => void
}
6 changes: 0 additions & 6 deletions packages/embed/src/base/url-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,6 @@ export type UrlOptions = {
* @type {string | boolean}
*/
shareGaInstance?: string | boolean
/**
* Disables form auto focusing.
*
* @type {boolean}
*/
disableAutoFocus?: boolean
/**
* Enable sandbox mode for the form.
* Allow testing without adding an entry to results or affecting metrics.
Expand Down
12 changes: 4 additions & 8 deletions packages/embed/src/factories/create-popover/create-popover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,12 @@ import {
makeAutoResize,
} from '../../utils'
import type { RemoveHandler } from '../../utils'
import { EmbedPopup } from '../../base'

import { PopoverOptions } from './popover-options'
import { buildNotificationDot, canBuildNotificationDot, saveNotificationDotHideUntilTime } from './notification-days'

export type Popover = {
open: () => void
close: () => void
toggle: () => void
refresh: () => void
unmount: () => void
}
export type Popover = EmbedPopup

const replaceIcon = (iconToReplace: HTMLElement, newIcon: HTMLElement) => {
const element = iconToReplace.parentNode
Expand Down Expand Up @@ -120,7 +115,7 @@ const defaultOptions = {

export const createPopover = (formId: string, userOptions: PopoverOptions = {}): Popover => {
const options = { ...defaultOptions, ...userOptions }
const { iframe, embedId, refresh } = createIframe(formId, 'popover', options)
const { iframe, embedId, refresh, focus } = createIframe(formId, 'popover', options)

let openHandler: RemoveHandler

Expand Down Expand Up @@ -254,6 +249,7 @@ export const createPopover = (formId: string, userOptions: PopoverOptions = {}):
close,
toggle,
refresh,
focus,
unmount,
}
}
13 changes: 5 additions & 8 deletions packages/embed/src/factories/create-popup/create-popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,11 @@ import {
import type { RemoveHandler } from '../../utils'
import { POPUP_SIZE } from '../../constants'
import { isInPage, isOpen, makeAutoResize } from '../../utils'
import { EmbedPopup } from '../../base'

import { PopupOptions } from './popup-options'

export type Popup = {
open: () => void
close: () => void
toggle: () => void
refresh: () => void
unmount: () => void
}
export type Popup = EmbedPopup

const buildPopup = () => {
const popup = document.createElement('div')
Expand Down Expand Up @@ -66,13 +61,14 @@ export const createPopup = (formId: string, userOptions: PopupOptions = {}): Pop
close: () => {},
toggle: () => {},
refresh: () => {},
focus: () => {},
unmount: () => {},
}
}

const { width, height, size = POPUP_SIZE, onClose, ...options } = userOptions

const { iframe, embedId, refresh } = createIframe(formId, 'popup', options)
const { iframe, embedId, refresh, focus } = createIframe(formId, 'popup', options)
const scrollInitialState = document.body.style.overflow
let openHandler: RemoveHandler

Expand Down Expand Up @@ -153,6 +149,7 @@ export const createPopup = (formId: string, userOptions: PopupOptions = {}): Pop
close,
toggle,
refresh,
focus,
unmount,
}
}
12 changes: 4 additions & 8 deletions packages/embed/src/factories/create-sidetab/create-sidetab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,11 @@ import {
makeAutoResize,
} from '../../utils'
import type { RemoveHandler } from '../../utils'
import { EmbedPopup } from '../../base'

import { SidetabOptions } from './sidetab-options'

export type Sidetab = {
open: () => void
close: () => void
toggle: () => void
refresh: () => void
unmount: () => void
}
export type Sidetab = EmbedPopup

const defaultOptions = {
buttonColor: '#3a7685',
Expand Down Expand Up @@ -103,7 +98,7 @@ const replaceElementChild = (childToReplace: HTMLElement, newChild: HTMLElement)

export const createSidetab = (formId: string, userOptions: SidetabOptions = {}): Sidetab => {
const options = { ...defaultOptions, ...userOptions }
const { iframe, embedId, refresh } = createIframe(formId, 'side-tab', options)
const { iframe, embedId, refresh, focus } = createIframe(formId, 'side-tab', options)
const sidetab = buildSidetab(options.width, options.height)
const wrapper = buildWrapper()
const spinner = buildSpinner()
Expand Down Expand Up @@ -191,6 +186,7 @@ export const createSidetab = (formId: string, userOptions: SidetabOptions = {}):
close,
toggle,
refresh,
focus,
unmount,
}
}
13 changes: 5 additions & 8 deletions packages/embed/src/factories/create-slider/create-slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,11 @@ import {
} from '../../utils'
import type { RemoveHandler } from '../../utils'
import { SLIDER_POSITION, SLIDER_WIDTH } from '../../constants'
import { EmbedPopup } from '../../base'

import { SliderOptions } from './slider-options'

export type Slider = {
open: () => void
close: () => void
toggle: () => void
refresh: () => void
unmount: () => void
}
export type Slider = EmbedPopup

const buildSlider = (position: 'right' | 'left') => {
const slider = document.createElement('div')
Expand Down Expand Up @@ -59,12 +54,13 @@ export const createSlider = (formId: string, userOptions: SliderOptions = {}): S
close: () => {},
toggle: () => {},
refresh: () => {},
focus: () => {},
unmount: () => {},
}
}

const { position = SLIDER_POSITION, width = SLIDER_WIDTH, onClose, ...options } = userOptions
const { iframe, embedId, refresh } = createIframe(formId, 'slider', options)
const { iframe, embedId, refresh, focus } = createIframe(formId, 'slider', options)
const scrollInitialState = document.body.style.overflow
let openHandler: RemoveHandler

Expand Down Expand Up @@ -150,6 +146,7 @@ export const createSlider = (formId: string, userOptions: SliderOptions = {}): S
close,
toggle,
refresh,
focus,
unmount,
}
}
22 changes: 17 additions & 5 deletions packages/embed/src/factories/create-widget/create-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@ import {
} from '../../utils'
import {
getFormHeightChangedHandler,
getFormReadyHandler,
getFormThemeHandler,
getWelcomeScreenHiddenHandler,
} from '../../utils/create-iframe/get-form-event-handler'
import { EmbedWidget } from '../../base'

import { WidgetOptions } from './widget-options'
import { buildWidget } from './elements'

export type Widget = {
refresh: () => void
unmount: () => void
}
export type Widget = EmbedWidget

const buildCloseButton = () => {
const closeButton = document.createElement('a')
Expand All @@ -32,6 +31,7 @@ export const createWidget = (formId: string, options: WidgetOptions): Widget =>
if (!hasDom()) {
return {
refresh: () => {},
focus: () => {},
unmount: () => {},
}
}
Expand All @@ -43,7 +43,7 @@ export const createWidget = (formId: string, options: WidgetOptions): Widget =>
widgetOptions.forceTouch = true
}

const { embedId, iframe, refresh } = createIframe(formId, 'widget', widgetOptions)
const { embedId, iframe, refresh, focus } = createIframe(formId, 'widget', widgetOptions)
const widget = buildWidget(iframe, options.width, options.height)

if (widgetOptions.autoResize) {
Expand All @@ -64,6 +64,17 @@ export const createWidget = (formId: string, options: WidgetOptions): Widget =>
)
}

if (widgetOptions.autoFocus) {
window.addEventListener(
'message',
getFormReadyHandler(embedId, () => {
setTimeout(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setTimeout is necessary because of the loading screen, see #513 (comment)

focus()
}, 1000)
})
)
}

const appendWidget = () => options.container.append(widget)

options.container.innerHTML = ''
Expand Down Expand Up @@ -122,6 +133,7 @@ export const createWidget = (formId: string, options: WidgetOptions): Widget =>

return {
refresh,
focus,
unmount: () => unmountElement(widget),
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,10 @@ export type WidgetOptions = BaseOptions &
* @type {boolean}
*/
lazy?: boolean
/**
* Enabled form auto focus.
*
* @type {boolean}
*/
autoFocus?: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const buildOptionsFromAttributes = (element: HTMLElement) => {
forceTouch: 'boolean',
enableFullscreen: 'boolean',
inlineOnMobile: 'boolean',
disableAutoFocus: 'boolean',
autoFocus: 'boolean',
tracking: 'record',
redirectTarget: 'string',
iframeProps: 'record',
Expand Down
2 changes: 0 additions & 2 deletions packages/embed/src/utils/build-iframe-src.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ const mapOptionsToQueryParams = (
opacity,
disableTracking,
enableSandbox,
disableAutoFocus,
shareGaInstance,
forceTouch,
enableFullscreen,
Expand All @@ -58,7 +57,6 @@ const mapOptionsToQueryParams = (
'embed-hide-headers': hideHeaders ? 'true' : undefined,
'embed-opacity': opacity,
'disable-tracking': disableTracking || enableSandbox ? 'true' : undefined,
'disable-auto-focus': disableAutoFocus ? 'true' : undefined,
'__dangerous-disable-submissions': enableSandbox ? 'true' : undefined,
'share-ga-instance': shareGaInstance ? 'true' : undefined,
'force-touch': forceTouch ? 'true' : undefined,
Expand Down
2 changes: 0 additions & 2 deletions packages/embed/src/utils/build-iframe.src.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ describe('build-iframe-src', () => {
hideHeaders: true,
opacity: 50,
disableTracking: true,
disableAutoFocus: true,
hidden: {
foo: 'foo value',
bar: '@bar&value?',
Expand All @@ -91,7 +90,6 @@ describe('build-iframe-src', () => {
'&embed-hide-headers=true' +
'&embed-opacity=50' +
'&disable-tracking=true' +
'&disable-auto-focus=true' +
'&__dangerous-disable-submissions=true' +
'&typeform-embed-auto-resize=true' +
'&utm_foo=utm+foo+value&foobar=foobar%26value' +
Expand Down
Loading