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-3717): Initial version #1

Merged
merged 7 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
max_line_length=120
end_of_line=lf
charset=utf-8
trim_trailing_whitespace=true
insert_final_newline=true
indent_style=space
indent_size=2
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
dist/
commitlint.config.js
27 changes: 27 additions & 0 deletions .github/workflows/ci-standard-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CI Standard Checks
on:
push:
branches:
- main
pull_request:
types:
- opened
- edited
- synchronize
- reopened
- ready_for_review
branches:
- main
jobs:
ci-standard-checks:
runs-on:
- ubuntu-latest
steps:
- name: Check Out Source Code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: CI Standard Checks
uses: Typeform/ci-standard-checks@v1
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
36 changes: 36 additions & 0 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Pull Request
on:
pull_request:
branches:
- main

jobs:
build-lint-test:
runs-on: ubuntu-latest
name: build-lint-test
steps:
- name: Check out Git repository
uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 20

- name: Get yarn cache
uses: actions/cache@v2
id: yarn-cache
with:
path: |
**/node_modules
~/.cache
key: ${{ runner.os }}-repo-${{ github.event.pull_request.head.repo.full_name }}-node-20-yarn-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('.github/workflows/**.yml') }}

- name: Install Node.js dependencies
# run when cache not found or PR is external (build fails for external PRs if dependencies are not installed)
if: steps.yarn-cache.outputs.cache-hit != 'true' || github.event.pull_request.head.repo.full_name != github.repository
run: yarn install --frozen-lockfile

- run: yarn dist
mathio marked this conversation as resolved.
Show resolved Hide resolved
- run: yarn lint
- run: yarn test
58 changes: 58 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# OSX and ide's folder attributes
.DS_Store
Thumbs.db
.cache
.project
.settings
.tmproj
*.esproj
nbproject
*.sublime-project
*.sublime-workspace
*.sublime-*
jsconfig.json
.vscode
.idea
.env

# common node files and folders
# https://github.com/github/gitignore/blob/master/Node.gitignore
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz

pids
logs
results

build/Release # Compiled binary addons (http://nodejs.org/api/addons.html)

# always-ignore extensions
*.diff
*.err
*.orig
*.rej
*.swo
*.swp
*.vi
*~
*.sass-cache

# project specific
mathio marked this conversation as resolved.
Show resolved Hide resolved
.next
node_modules
build
build-aws
types
dist

# tests
**/e2e/screenshots
**/cypress/screenshots
**/cypress/videos
**/cypress/fixtures
7 changes: 7 additions & 0 deletions .huskyrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"hooks": {
"pre-commit": "yarn lint && yarn build",
"pre-push": "yarn test",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# override registry since this is a public repo
@typeform:registry=https://registry.yarnpkg.com
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @Typeform/reach
mathio marked this conversation as resolved.
Show resolved Hide resolved
112 changes: 112 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,115 @@

Integrate Typeform Admin UI in your web app - as an iframe or a popup.

## Usage

As HTML button:

```html
<button data-tf-embed-admin data-tf-embed-admin-callback="handleSelect">select typeform</button>
<script src="dist/button.js"></script>
<script>
// you still need to implement the callback in JavaScript
function handleSelect(action, formId) {
mathio marked this conversation as resolved.
Show resolved Hide resolved
console.log(`you have selected form with id ${formId}`)
}
</script>
```

Or using JavaScript:

```html
<button onclick="selectTypeform()">select form</button>
<script src="dist/button.js"></script>
<script>
const callback = (action, formId) => {
console.log(`you have selected form with id ${formId}`)
}
const selectTypeform = () => {
window.tfEmbedAdmin.open({ callback, type: 'iframe' })
}
</script>
```

## Options

| name | type | description | default value |
| -------- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| type | `"iframe" \| "popup"` | Open embed admin in popup (default) or iframe. **Note:** If you want to implement iframe, please [contant us to get your domain whitelisted](https://www.typeform.com/help/contact/360000510012/). | `"popup"` |
mathio marked this conversation as resolved.
Show resolved Hide resolved
| action | `"select" \| "edit"` | Action to perform. If you want to "edit" you also need to pass form ID as `payload` | `"select"` |
| payload | string | Form ID, required for "edit" action. | `undefined` |
| appName | string | Application name | `window.location.hostname` |
| callback | (action: string, formId: string) => void | Method to be called when a form is selected or edited in Typeform Admin UI. | `undefined` |

You can pass options as object to the `open` method:

```javascript
window.tfEmbedAdmin.open({
type: 'iframe',
action: 'edit',
formId: myTypeformId,
appName: 'my-app',
callback: (action, id) => console.log(`action: ${action}, form id: ${id}`),
})
```

Or you can pass them as attributes to the HTML button with `data-tf-embed-admin-*` prefix:

```html
<button
data-tf-embed-admin
data-tf-embed-admin-type="iframe"
data-tf-embed-admin-action="edit"
data-tf-embed-admin-form-id="123456"
data-tf-embed-admin-app-name="my-app"
data-tf-embed-admin-callback="embedAdminCallback"
>
edit typeform
</button>
<script>
function embedAdminCallback() {
// callback fucntion needs to be available on global scope (window)
}
</script>
```

## Demo

Run:

```shell
yarn start
```

Demo implementation of the library will be served at http://localhost:9090

Or [open the demo in CodeSandbox](https://codesandbox.io/s/github/Typeform/button), directly in your browser.

## Development

Requiremenets:

- node >= 18
- yarn

Install dependencies:

```shell
yarn
```

For local development run:

```shell
yarn watch
```

or with demo:

```shell
yarn start
```

## Helo and Contribution

Feel free to [open a Github Issue with your question](https://github.com/Typeform/button/issues) or [submit a pull request](https://github.com/Typeform/button/pulls).
1 change: 1 addition & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = { extends: ['@commitlint/config-conventional'] }
113 changes: 113 additions & 0 deletions demo/embed.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Typeform Button Demo</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
padding: 20px;
}
h1 {
margin: 10px 0 20px 0;
}
button {
padding: 5px 10px;
cursor: pointer;
}
ul#typeforms {
list-style: none;
display: flex;
flex-direction: row;
}
ul#typeforms li {
width: 300px;
margin: 0 20px 20px 0;
}
ul#typeforms li img {
max-width: 300px;
}
</style>
</head>
<body>
<h1>Typeform Button Demo - with Embed SDK</h1>

<!-- embed SDK -->
<link rel="stylesheet" href="//embed.typeform.com/next/css/popup.css" />
<script src="//embed.typeform.com/next/embed.js"></script>

<!-- typeform button -->
<script src="dist/button.js"></script>

<!-- custom script -->
<script>
const fetchTypeformDetails = async (formId) => {
const result = await fetch(
`https://form.typeform.com/oembed?url=${encodeURIComponent(`https://form.typeform.com/to/${formId}`)}`,
)
if (!result.ok) {
return {}
}
const data = await result.json()
const { title, author_url: url, thumbnail_url: image } = data || {}
return { title, url, image: image?.href ?? image }
}
const onSelect = async (action, { formId }) => {
console.log('selected form:', formId)

const { title, image } = await fetchTypeformDetails(formId)

const container = document.createElement('li')

const heading = document.createElement('h2')
heading.innerText = title
container.append(heading)

const thumbnail = document.createElement('img')
thumbnail.src = image
container.append(thumbnail)

const viewButton = document.createElement('button')
viewButton.onclick = () => {
window.tf.createPopup(formId).open()
}
viewButton.innerText = 'View'
container.append(viewButton)

const editButton = document.createElement('button')
editButton.onclick = () => editTypeform(formId)
editButton.innerText = 'Edit'
container.append(editButton)

const removeButton = document.createElement('button')
removeButton.onclick = () => {
if (window.confirm('Do you want to remove this typeform from the list?')) {
container.remove()
}
}
removeButton.innerText = 'Remove'
container.append(removeButton)

document.querySelector('#typeforms').append(container)
}
const onEdit = (action, { formId }) => {}
const selectTypeform = () => {
window.tfEmbedAdmin.open({ callback: onSelect, type: 'iframe' })
}
const editTypeform = (formId) => {
window.tfEmbedAdmin.open({
callback: onEdit,
type: 'iframe',
action: 'edit',
formId,
})
}
</script>
<button onclick="selectTypeform()">select form</button>
<ul id="typeforms"></ul>
</body>
</html>
Loading
Loading