Skip to content

Commit

Permalink
feat(DIST-700): Add transitiveSearchParams (#179)
Browse files Browse the repository at this point in the history
feat(DIST-700): Comments review

test(DIST-700): Fix functional tests
  • Loading branch information
Antonio authored Feb 25, 2021
1 parent 9c83413 commit b3d6694
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 31 deletions.
8 changes: 7 additions & 1 deletion packages/demo-html/public/widget-html.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
</style>
</head>
<body>
<div id="wrapper" data-tf-widget="moe6aa" data-tf-medium="unit-test"></div>
<div
id="wrapper"
data-tf-widget="moe6aa"
data-tf-medium="unit-test"
data-tf-transitive-search-params="foo,bar"
data-tf-source="localhost"
></div>
<script src="./lib/embed-next.js"></script>
</body>
</html>
7 changes: 6 additions & 1 deletion packages/demo-html/public/widget-js.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
<div id="wrapper"></div>
<script src="./lib/embed-next.js"></script>
<script>
window.tf.createWidget("moe6aa", { container: document.getElementById("wrapper"), medium: "unit-test" });
window.tf.createWidget("moe6aa", {
container: document.getElementById("wrapper"),
source: "localhost",
transitiveSearchParams: ["foo", "bar"],
medium: "unit-test",
});
</script>
</body>
</html>
3 changes: 2 additions & 1 deletion packages/embed/.prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": true
"singleQuote": true,
"printWidth": 120
}
50 changes: 38 additions & 12 deletions packages/embed/e2e/spec/functional/widget.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,42 @@
describe('Widget', () => {
before(() => {
cy.visit('/widget-js.html')
})
testWidget('js')
testWidget('html')
})

it('should display widget', () => {
cy.get('.typeform-widget iframe').should('be.visible')
cy.get('.typeform-widget iframe').invoke('attr', 'src').should('contain', 'form.typeform.com/to/')
})
function testWidget(type = 'html') {
describe(`Widget ${type}`, () => {
before(() => {
cy.visit(`/widget-${type}.html`)
})

it('should display widget', () => {
cy.get('.typeform-widget iframe').should('be.visible')
cy.get('.typeform-widget iframe').invoke('attr', 'src').should('contain', 'form.typeform.com/to/')
})

it('should pass options as query param', () => {
cy.get('.typeform-widget iframe')
.invoke('attr', 'src')
.should('contain', 'typeform-embed=embed-widget&typeform-source=localhost&typeform-medium=unit-test')
})

it('should pass options as query param', () => {
cy.get('.typeform-widget iframe')
.invoke('attr', 'src')
.should('contain', 'typeform-embed=embed-widget&typeform-source=localhost&typeform-medium=unit-test')
describe(`Widget ${type} With Parameters`, () => {
before(() => {
cy.visit(`/widget-${type}.html?foo=foo&bar=bar&baz=baz`)
})

it('should display widget', () => {
cy.get('.typeform-widget iframe').should('be.visible')
cy.get('.typeform-widget iframe').invoke('attr', 'src').should('contain', 'form.typeform.com/to/')
})

it('should pass params from options to the iframe', () => {
cy.get('.typeform-widget iframe').invoke('attr', 'src').should('contain', 'foo=foo&bar=bar')
})

it('should not pass params not in the list to the iframe', () => {
cy.get('.typeform-widget iframe').invoke('attr', 'src').should('not.contain', 'baz=baz')
})
})
})
})
}
2 changes: 1 addition & 1 deletion packages/embed/src/base/url-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export type UrlOptions = {
*
* @type {string[]}
*/
transferableUrlParameters?: string[]
transitiveSearchParams?: string[]
/**
* Hide typeform footer, that appears showing the progress bar and the navigation buttons.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ describe('build-options-from-attributes', () => {
onReady: 'function',
onSubmit: 'function',
onQuestionChanged: 'function',
transitiveSearchParams: 'array',
})
expect(options).toEqual({
source: 'unit-test-source',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const buildOptionsFromAttributes = (
onReady: 'function',
onSubmit: 'function',
onQuestionChanged: 'function',
transitiveSearchParams: 'array',
...additionalAttributes,
})
}
30 changes: 17 additions & 13 deletions packages/embed/src/utils/build-iframe-src.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { EmbedType, UrlOptions } from '../base'

import { removeUndefinedKeys } from './remove-undefined-keys'
import { isDefined } from './is-defined'
import { getTransitiveSearchParams } from './get-transitive-search-params'

const defaultUrlOptions: UrlOptions = {
source: window?.location?.hostname.replace(/^www\./, ''),
Expand All @@ -20,30 +21,33 @@ const typesToEmbed: Record<EmbedType, string> = {
'side-tab': 'popup-side-panel',
}

const mapOptionsToQueryParams = (type: EmbedType, embedId: string, options: UrlOptions): Record<string, any> => ({
'typeform-embed-id': embedId,
'typeform-embed': typesToEmbed[type],
'typeform-source': options.source,
'typeform-medium': options.medium,
'typeform-medium-version': options.mediumVersion,
'embed-hide-footer': options.hideFooter ? 'true' : undefined,
'embed-hide-headers': options.hideHeaders ? 'true' : undefined,
'embed-opacity': options.opacity,
'disable-tracking': options.disableTracking ? 'true' : undefined,
})
const mapOptionsToQueryParams = (type: EmbedType, embedId: string, options: UrlOptions): Record<string, any> => {
const transitiveParams = getTransitiveSearchParams(options.transitiveSearchParams)
const params = {
'typeform-embed-id': embedId,
'typeform-embed': typesToEmbed[type],
'typeform-source': options.source,
'typeform-medium': options.medium,
'typeform-medium-version': options.mediumVersion,
'embed-hide-footer': options.hideFooter ? 'true' : undefined,
'embed-hide-headers': options.hideHeaders ? 'true' : undefined,
'embed-opacity': options.opacity,
'disable-tracking': options.disableTracking ? 'true' : undefined,
}
return { ...params, ...transitiveParams }
}

export const buildIframeSrc = (params: BuildIframeSrcOptions): string => {
const { formId, type, embedId, options } = params

const queryParams = mapOptionsToQueryParams(type, embedId, addDefaultUrlOptions(options))

const url = new URL(`https://form.typeform.com/to/${formId}`)

Object.entries(queryParams)
.filter(([, paramValue]) => isDefined(paramValue))
.forEach(([paramName, paramValue]) => {
url.searchParams.set(paramName, paramValue)
})

return url.href
}

Expand Down
29 changes: 29 additions & 0 deletions packages/embed/src/utils/get-transitive-search-params.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { getTransitiveSearchParams } from './get-transitive-search-params'

describe('transferUrlParametersToQueryStrings', () => {
const location: Location = window.location

beforeAll(() => {
delete (window as any).location
const search =
'?foo=jason&bar=rachel&utm_medium=cpc&utm_campaign=camp2008&utm_source=instagram&embed-hide-footer=false'

window.location = {
search,
href: `http://localhost/${search}`,
} as any
})

afterAll(() => {
window.location = location
})

it('transfer the parameters of the URL in the query strings', () => {
const urlParameters = ['foo', 'bar']
const queryStringWithTransferedUrlParameters = getTransitiveSearchParams(urlParameters)
expect(queryStringWithTransferedUrlParameters).toEqual({
foo: 'jason',
bar: 'rachel',
})
})
})
14 changes: 14 additions & 0 deletions packages/embed/src/utils/get-transitive-search-params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const getTransitiveSearchParams = (transitiveSearchParams?: string[]) => {
const url = new URL(window.location.href)
const queryParamsWithTransitiveParams = {}

if (transitiveSearchParams && transitiveSearchParams.length > 0) {
transitiveSearchParams.forEach((key: string) => {
if (url.searchParams.has(key)) {
queryParamsWithTransitiveParams[key] = url.searchParams.get(key)
}
})
}

return queryParamsWithTransitiveParams
}
20 changes: 19 additions & 1 deletion packages/embed/src/utils/load-options-from-attributes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('load-options-from-attributes', () => {
})
})

describe('to booolean', () => {
describe('to boolean', () => {
it('should transform "yes" to true', () => {
expect(transformAttributeValue('yes', 'boolean')).toBe(true)
})
Expand Down Expand Up @@ -82,6 +82,24 @@ describe('load-options-from-attributes', () => {
})
})

describe('to array', () => {
it('should transform string as array', () => {
expect(transformAttributeValue('a, b', 'array')).toEqual(['a', 'b'])
})

it('should remove empty spaces around text', () => {
expect(transformAttributeValue('foo , bar , test', 'array')).toEqual(['foo', 'bar', 'test'])
})

it('should transform malformed string in empty array', () => {
expect(transformAttributeValue(',', 'array')).toEqual([])
})

it('should return undefined if no value', () => {
expect(transformAttributeValue('', 'array')).toEqual(undefined)
})
})

describe('#loadOptionsFromAttributes', () => {
const wrapper = document.createElement('div')
wrapper.innerHTML = `<div id="element"
Expand Down
12 changes: 11 additions & 1 deletion packages/embed/src/utils/load-options-from-attributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const camelCaseToKebabCase = (value: string) => {
.join('')
}

export type Transformation = 'string' | 'boolean' | 'integer' | 'function'
export type Transformation = 'string' | 'boolean' | 'integer' | 'function' | 'array'

const transformString = (value: string | null): string | undefined => {
return value || undefined
Expand All @@ -30,6 +30,14 @@ const transformFunction = (value: string | null): Function | undefined => {
return typeof fn === 'function' ? fn : undefined
}

const transformArray = (value: string | null): string[] | undefined => {
const val = value
?.replace(/\s/g, '')
.split(',')
.filter((v) => !!v)
return value ? val : undefined
}

export const transformAttributeValue = (value: string | null, transformation: Transformation) => {
switch (transformation) {
case 'string':
Expand All @@ -40,6 +48,8 @@ export const transformAttributeValue = (value: string | null, transformation: Tr
return transformInteger(value)
case 'function':
return transformFunction(value)
case 'array':
return transformArray(value)
default:
throw new Error(`Invalid attribute transformation ${transformation}`)
}
Expand Down

0 comments on commit b3d6694

Please sign in to comment.