├── .changeset └── config.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── codeql-analysis.yml │ ├── deploy.yml │ ├── e2e-testing.yml │ ├── main.yml │ ├── pull-request.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .nvmrc ├── .prettierrc.js ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── apps ├── examples │ ├── README.md │ ├── gatsby │ │ ├── .gitignore │ │ ├── README.md │ │ ├── gatsby-browser.js │ │ ├── gatsby-config.js │ │ ├── gatsby-ssr.js │ │ ├── package.json │ │ ├── src │ │ │ ├── images │ │ │ │ └── icon.png │ │ │ └── pages │ │ │ │ ├── 404.js │ │ │ │ └── index.js │ │ └── wrap-with-provider.jsx │ ├── nextjs-app-router │ │ ├── .gitignore │ │ ├── README.md │ │ ├── app │ │ │ ├── intercom.jsx │ │ │ ├── layout.jsx │ │ │ └── page.jsx │ │ └── package.json │ ├── nextjs-page-router │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── pages │ │ │ ├── _app.js │ │ │ ├── api │ │ │ │ └── hello.js │ │ │ └── index.js │ │ ├── public │ │ │ ├── favicon.ico │ │ │ └── vercel.svg │ │ └── styles │ │ │ ├── Home.module.css │ │ │ └── globals.css │ └── vite │ │ ├── .gitignore │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── index.html │ │ ├── package.json │ │ ├── src │ │ ├── app.jsx │ │ └── main.jsx │ │ └── vite.config.js └── playground │ ├── .gitignore │ ├── assets │ └── images │ │ ├── githubLogo.svg │ │ └── logo.png │ ├── cypress.config.ts │ ├── cypress │ ├── e2e │ │ ├── boot.ts │ │ ├── hide.ts │ │ ├── initializeDelay.ts │ │ ├── mappers.ts │ │ ├── provider.ts │ │ ├── show.ts │ │ ├── shutdown.ts │ │ ├── startSurvey.ts │ │ ├── update.ts │ │ └── visitorId.ts │ ├── support │ │ └── e2e.ts │ └── tsconfig.json │ ├── favicon.ico │ ├── global.d.ts │ ├── index.html │ ├── index.tsx │ ├── package.json │ ├── src │ ├── app.tsx │ ├── modules │ │ ├── common │ │ │ ├── button.tsx │ │ │ ├── index.ts │ │ │ ├── page.tsx │ │ │ └── style.tsx │ │ ├── index.ts │ │ ├── provider │ │ │ ├── index.ts │ │ │ ├── provider.tsx │ │ │ ├── providerApi.tsx │ │ │ ├── providerAutoBootProps.tsx │ │ │ └── providerEvents.tsx │ │ └── useIntercom │ │ │ ├── index.ts │ │ │ ├── useIntercom.tsx │ │ │ ├── useIntercomTour.tsx │ │ │ └── useIntercomWithDelay.tsx │ └── vite-env.d.ts │ ├── tsconfig.json │ └── vite.config.ts ├── assets └── logo.png ├── eslint.config.js ├── package.json ├── packages └── react-use-intercom │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.cjs │ ├── package.json │ ├── src │ ├── api.ts │ ├── context.ts │ ├── global.d.ts │ ├── index.ts │ ├── initialize.ts │ ├── logger.ts │ ├── mappers.ts │ ├── provider.tsx │ ├── types.ts │ ├── useIntercom.ts │ └── utils.ts │ ├── test │ ├── config.ts │ ├── intercomProvider.test.tsx │ ├── test-env.d.ts │ └── useIntercom.test.tsx │ ├── tsconfig.json │ └── tsup.config.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── turbo.json /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "ignore": ["playground", "*-example"], 6 | "access": "public", 7 | "updateInternalDependencies": "patch", 8 | "baseBranch": "main", 9 | "bumpVersionsWithWorkspaceProtocolOnly": true 10 | } 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: devrnt 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **StackBlitz link** 21 | Include a StackBLitz will help us to investigate the issue quicker. 22 | 23 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz_small.svg)](https://stackblitz.com/fork/github/devrnt/react-use-intercom/) 24 | 25 | **Expected behavior** 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Screenshots** 29 | If applicable, add screenshots to help explain your problem. 30 | 31 | **Desktop (please complete the following information):** 32 | - OS: [e.g. iOS] 33 | - Browser [e.g. chrome, safari] 34 | - Version [e.g. 22] 35 | 36 | **Smartphone (please complete the following information):** 37 | - Device: [e.g. iPhone6] 38 | - OS: [e.g. iOS8.1] 39 | - Browser [e.g. stock browser, safari] 40 | - Version [e.g. 22] 41 | 42 | **Additional context** 43 | Add any other context about the problem here. 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: devrnt 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '45 18 * * 3' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-24.04 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'javascript' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout 38 | uses: actions/checkout@v3 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to Github Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-24.04 11 | strategy: 12 | matrix: 13 | node-version: [22.13.1] 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Install pnpm 19 | uses: pnpm/action-setup@v3 20 | 21 | - name: Use Node ${{ matrix.node-version }} 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | cache: 'pnpm' 26 | 27 | - name: Install dependencies 28 | run: pnpm install --frozen-lockfile 29 | 30 | - name: Build playground 31 | run: pnpm build:playground 32 | 33 | - name: Deploy 34 | uses: peaceiris/actions-gh-pages@v3 35 | with: 36 | github_token: ${{ secrets.GITHUB_TOKEN }} 37 | publish_dir: ./apps/playground/dist 38 | -------------------------------------------------------------------------------- /.github/workflows/e2e-testing.yml: -------------------------------------------------------------------------------- 1 | name: End-to-end tests 2 | on: [pull_request] 3 | jobs: 4 | cypress-run: 5 | runs-on: ubuntu-24.04 6 | strategy: 7 | matrix: 8 | node-version: [22.13.1] 9 | 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | 14 | - name: Install pnpm 15 | uses: pnpm/action-setup@v3 16 | 17 | - name: Use Node ${{ matrix.node-version }} 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | cache: 'pnpm' 22 | 23 | - name: Install dependencies 24 | run: pnpm install --frozen-lockfile 25 | 26 | - name: Build 27 | run: pnpm build 28 | 29 | - name: Cypress install 30 | run: ./node_modules/cypress/bin/cypress install 31 | 32 | - name: Cypress run 33 | uses: cypress-io/github-action@v6 34 | with: 35 | install: false 36 | start: pnpm dev 37 | command: pnpm e2e 38 | wait-on: 'http://localhost:5173' 39 | project: ./apps/playground 40 | 41 | # after the test run completes 42 | # store videos and any screenshots 43 | # NOTE: screenshots will be generated only if E2E test failed 44 | # thus we store screenshots only on failures 45 | # Alternative: create and commit an empty cypress/screenshots folder 46 | # to always have something to upload 47 | - name: Upload Cypress artifacts 48 | uses: actions/upload-artifact@v4 49 | if: failure() 50 | with: 51 | name: cypress-screenshots 52 | path: ./apps/playground/cypress/screenshots 53 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-24.04 6 | strategy: 7 | matrix: 8 | node-version: [22.13.1] 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v4 12 | 13 | - name: Install pnpm 14 | uses: pnpm/action-setup@v3 15 | 16 | - name: Use Node ${{ matrix.node-version }} 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | cache: 'pnpm' 21 | 22 | - name: Install dependencies 23 | run: pnpm install --frozen-lockfile 24 | 25 | - name: Lint 26 | run: pnpm lint 27 | 28 | - name: Test 29 | run: pnpm test 30 | 31 | - name: Build 32 | run: pnpm build 33 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Compressed Size 2 | on: [pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-24.04 6 | strategy: 7 | matrix: 8 | node-version: [22.13.1] 9 | 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | 14 | - name: Install pnpm 15 | uses: pnpm/action-setup@v3 16 | 17 | - name: Use Node ${{ matrix.node-version }} 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | cache: 'pnpm' 22 | 23 | - name: Install dependencies 24 | run: pnpm install --frozen-lockfile 25 | 26 | - name: Build 27 | run: pnpm build 28 | 29 | - uses: preactjs/compressed-size-action@v2 30 | with: 31 | repo-token: '${{ secrets.GITHUB_TOKEN }}' 32 | pattern: "packages/**/dist/**/*.?(c)js" 33 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | concurrency: ${{ github.workflow }}-${{ github.ref }} 9 | 10 | jobs: 11 | release: 12 | name: Release 13 | runs-on: ubuntu-24.04 14 | strategy: 15 | matrix: 16 | node-version: [22.13.1] 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | 22 | - name: Install pnpm 23 | uses: pnpm/action-setup@v3 24 | 25 | - name: Use Node ${{ matrix.node-version }} 26 | uses: actions/setup-node@v4 27 | with: 28 | node-version: ${{ matrix.node-version }} 29 | cache: 'pnpm' 30 | 31 | - name: Install dependencies 32 | run: pnpm install --frozen-lockfile 33 | 34 | - name: Create Release Pull Request or Publish to npm 35 | id: changesets 36 | uses: changesets/action@v1 37 | with: 38 | # This expects you to have a script called release which does a build for your packages and calls changeset publish 39 | publish: pnpm release 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | .cache 5 | dist 6 | 7 | .turbo 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers = true 2 | node-linker=hoisted 3 | # This allows us to use workspace packages if available, otherwise pull from the remote registry 4 | link-workspace-packages = true -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.13.1 -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | export default { 2 | printWidth: 80, 3 | semi: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | arrowParens: 'always', 7 | }; 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | See [releases](https://github.com/devrnt/react-use-intercom/releases) -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to react-use-intercom 2 | 3 | ## Preparing 4 | 5 | 1. Install pnpm: 6 | 7 | ```sh 8 | npm install -g pnpm@10.2 9 | ``` 10 | 11 | 2. Fork and clone the repository 12 | 3. Install dependencies: 13 | 14 | ```sh 15 | pnpm install 16 | ``` 17 | 18 | 4. The project uses a monorepo structure with pnpm workspaces: 19 | 20 | - `/packages/react-use-intercom`: Main library package 21 | - `/apps/playground`: Development playground 22 | - `/apps/examples`: Example applications 23 | 24 | ## Development Workflow 25 | 26 | 1. Create a new branch for your feature/fix: 27 | 28 | ```sh 29 | git checkout -b your-feature-name 30 | ``` 31 | 32 | 2. Make your changes in the appropriate package 33 | 34 | 3. To test your changes: 35 | 36 | - Start the playground: `pnpm dev` 37 | - Run the test suite: `pnpm test` 38 | - Run E2E tests: `pnpm test:e2e` 39 | 40 | 4. Create a changeset to document your changes: 41 | 42 | ```sh 43 | pnpm changeset 44 | ``` 45 | 46 | Follow the prompts to describe your changes. This step is required for any user-facing changes. 47 | 48 | 5. Before submitting your PR: 49 | - Ensure all tests pass 50 | - Add or update tests as needed 51 | - Follow the existing code style 52 | - Update documentation if necessary 53 | - Verify your changeset accurately describes the changes 54 | 55 | ## Pull Request 56 | 57 | 1. Update the README.md with details of changes if applicable 58 | 2. Ensure your PR includes a changeset if it includes user-facing changes 59 | 60 | ## Testing 61 | 62 | - Write unit tests for new features 63 | - Update existing tests when modifying features 64 | - Ensure all tests pass before submitting a PR 65 | - Add E2E tests for new user-facing features 66 | 67 | ## Documentation 68 | 69 | - Update the README.md if you add or modify features 70 | - Include JSDoc comments for public APIs 71 | - Keep documentation clear and concise 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 devrnt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ./packages/react-use-intercom/README.md -------------------------------------------------------------------------------- /apps/examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | * [gatsby](https://stackblitz.com/github/devrnt/react-use-intercom/tree/main/apps/examples/gatsby) 4 | * [nextjs-page-router](https://stackblitz.com/github/devrnt/react-use-intercom/tree/main/apps/examples/nextjs-page-router) 5 | * [nextjs-app-router](https://stackblitz.com/github/devrnt/react-use-intercom/tree/main/apps/examples/nextjs-app-router) -------------------------------------------------------------------------------- /apps/examples/gatsby/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .cache/ 3 | public 4 | -------------------------------------------------------------------------------- /apps/examples/gatsby/README.md: -------------------------------------------------------------------------------- 1 | # react-use-intercom in Gatsby 2 | 3 | Replace `INTERCOM_APP_ID` with your Intercom app id. 4 | -------------------------------------------------------------------------------- /apps/examples/gatsby/gatsby-browser.js: -------------------------------------------------------------------------------- 1 | import wrapWithProvider from './wrap-with-provider'; 2 | 3 | export const wrapRootElement = wrapWithProvider; 4 | -------------------------------------------------------------------------------- /apps/examples/gatsby/gatsby-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('gatsby').GatsbyConfig} 3 | */ 4 | module.exports = { 5 | siteMetadata: { 6 | siteUrl: `https://www.yourdomain.tld`, 7 | }, 8 | plugins: [], 9 | }; 10 | -------------------------------------------------------------------------------- /apps/examples/gatsby/gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | import wrapWithProvider from './wrap-with-provider'; 2 | 3 | export const wrapRootElement = wrapWithProvider; 4 | -------------------------------------------------------------------------------- /apps/examples/gatsby/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-example", 3 | "private": true, 4 | "description": "My Gatsby Site", 5 | "author": "devrnt", 6 | "keywords": [ 7 | "gatsby" 8 | ], 9 | "scripts": { 10 | "dev": "gatsby develop", 11 | "build": "gatsby build", 12 | "serve": "gatsby serve", 13 | "clean": "gatsby clean" 14 | }, 15 | "dependencies": { 16 | "gatsby": "^5.5.0", 17 | "react": "^18.2.0", 18 | "react-dom": "^18.2.0", 19 | "react-use-intercom": "*" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/examples/gatsby/src/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrnt/react-use-intercom/a89e1734584ac8421d8d81681be48e378786116f/apps/examples/gatsby/src/images/icon.png -------------------------------------------------------------------------------- /apps/examples/gatsby/src/pages/404.js: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Link } from "gatsby" 3 | 4 | const pageStyles = { 5 | color: "#232129", 6 | padding: "96px", 7 | fontFamily: "-apple-system, Roboto, sans-serif, serif", 8 | } 9 | const headingStyles = { 10 | marginTop: 0, 11 | marginBottom: 64, 12 | maxWidth: 320, 13 | } 14 | 15 | const paragraphStyles = { 16 | marginBottom: 48, 17 | } 18 | const codeStyles = { 19 | color: "#8A6534", 20 | padding: 4, 21 | backgroundColor: "#FFF4DB", 22 | fontSize: "1.25rem", 23 | borderRadius: 4, 24 | } 25 | 26 | const NotFoundPage = () => { 27 | return ( 28 |
29 |

Page not found

30 |

31 | Sorry 😔, we couldn’t find what you were looking for. 32 |
33 | {process.env.NODE_ENV === "development" ? ( 34 | <> 35 |
36 | Try creating a page in src/pages/. 37 |
38 | 39 | ) : null} 40 |
41 | Go home. 42 |

43 |
44 | ) 45 | } 46 | 47 | export default NotFoundPage 48 | 49 | export const Head = () => Not found 50 | -------------------------------------------------------------------------------- /apps/examples/gatsby/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useIntercom } from 'react-use-intercom'; 3 | 4 | const IndexPage = () => { 5 | const { boot } = useIntercom(); 6 | 7 | return ( 8 |
16 | 17 |
18 | ); 19 | }; 20 | 21 | export default IndexPage; 22 | -------------------------------------------------------------------------------- /apps/examples/gatsby/wrap-with-provider.jsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { IntercomProvider } from 'react-use-intercom'; 3 | 4 | const INTERCOM_APP_ID = 'jcabc7e3'; 5 | 6 | export default ({ element }) => { 7 | // Instantiating store in `wrapRootElement` handler ensures: 8 | // - there is fresh store for each SSR page 9 | // - it will be called only once in browser, when React mounts 10 | return {element}; 11 | }; 12 | -------------------------------------------------------------------------------- /apps/examples/nextjs-app-router/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /apps/examples/nextjs-app-router/README.md: -------------------------------------------------------------------------------- 1 | # react-use-intercom in NextJS (App Router) 2 | 3 | Replace `INTERCOM_APP_ID` with your Intercom app id. -------------------------------------------------------------------------------- /apps/examples/nextjs-app-router/app/intercom.jsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { IntercomProvider } from 'react-use-intercom'; 4 | 5 | const INTERCOM_APP_ID = 'jcabc7e3'; 6 | 7 | export function OurIntercomProvider({ children }) { 8 | return ( 9 | 10 | 11 | 12 | {children} 13 | 14 | 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /apps/examples/nextjs-app-router/app/layout.jsx: -------------------------------------------------------------------------------- 1 | import { OurIntercomProvider } from './intercom'; 2 | 3 | export default function RootLayout({ children }) { 4 | return ( 5 | 6 | 7 | 8 | {children} 9 | 10 | 11 | 12 | ); 13 | } -------------------------------------------------------------------------------- /apps/examples/nextjs-app-router/app/page.jsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useIntercom } from "react-use-intercom"; 4 | 5 | export default function Home() { 6 | const { boot } = useIntercom(); 7 | 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } -------------------------------------------------------------------------------- /apps/examples/nextjs-app-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-app-router-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev --port 3001", 7 | "build": "next build", 8 | "start": "next start" 9 | }, 10 | "dependencies": { 11 | "next": "^14.0.0", 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0", 14 | "react-use-intercom": "*" 15 | } 16 | } -------------------------------------------------------------------------------- /apps/examples/nextjs-page-router/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /apps/examples/nextjs-page-router/README.md: -------------------------------------------------------------------------------- 1 | # react-use-intercom in NextJS (Page Router) 2 | 3 | Replace `INTERCOM_APP_ID` with your Intercom app id. 4 | -------------------------------------------------------------------------------- /apps/examples/nextjs-page-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "nextjs-page-router-example", 4 | "scripts": { 5 | "dev": "next dev", 6 | "build": "next build", 7 | "start": "next start" 8 | }, 9 | "dependencies": { 10 | "next": "13.4.7", 11 | "react": "^18.2.0", 12 | "react-dom": "^18.2.0", 13 | "react-use-intercom": "*" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/examples/nextjs-page-router/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css'; 2 | 3 | import { IntercomProvider } from 'react-use-intercom'; 4 | 5 | const INTERCOM_APP_ID = 'jcabc7e3'; 6 | 7 | function MyApp({ Component, pageProps }) { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export default MyApp; 16 | -------------------------------------------------------------------------------- /apps/examples/nextjs-page-router/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default (req, res) => { 4 | res.statusCode = 200; 5 | res.json({ name: 'John Doe' }); 6 | }; 7 | -------------------------------------------------------------------------------- /apps/examples/nextjs-page-router/pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import { useIntercom } from 'react-use-intercom'; 3 | 4 | import styles from '../styles/Home.module.css'; 5 | 6 | export default function Home() { 7 | const { boot } = useIntercom(); 8 | 9 | return ( 10 |
11 | 12 | Create Next App 13 | 14 | 15 | 16 |
17 | 18 |
19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /apps/examples/nextjs-page-router/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrnt/react-use-intercom/a89e1734584ac8421d8d81681be48e378786116f/apps/examples/nextjs-page-router/public/favicon.ico -------------------------------------------------------------------------------- /apps/examples/nextjs-page-router/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /apps/examples/nextjs-page-router/styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | min-height: 100vh; 3 | padding: 0 0.5rem; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .main { 11 | padding: 5rem 0; 12 | flex: 1; 13 | display: flex; 14 | flex-direction: column; 15 | justify-content: center; 16 | align-items: center; 17 | } 18 | 19 | .footer { 20 | width: 100%; 21 | height: 100px; 22 | border-top: 1px solid #eaeaea; 23 | display: flex; 24 | justify-content: center; 25 | align-items: center; 26 | } 27 | 28 | .footer img { 29 | margin-left: 0.5rem; 30 | } 31 | 32 | .footer a { 33 | display: flex; 34 | justify-content: center; 35 | align-items: center; 36 | } 37 | 38 | .title a { 39 | color: #0070f3; 40 | text-decoration: none; 41 | } 42 | 43 | .title a:hover, 44 | .title a:focus, 45 | .title a:active { 46 | text-decoration: underline; 47 | } 48 | 49 | .title { 50 | margin: 0; 51 | line-height: 1.15; 52 | font-size: 4rem; 53 | } 54 | 55 | .title, 56 | .description { 57 | text-align: center; 58 | } 59 | 60 | .description { 61 | line-height: 1.5; 62 | font-size: 1.5rem; 63 | } 64 | 65 | .code { 66 | background: #fafafa; 67 | border-radius: 5px; 68 | padding: 0.75rem; 69 | font-size: 1.1rem; 70 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 71 | Bitstream Vera Sans Mono, Courier New, monospace; 72 | } 73 | 74 | .grid { 75 | display: flex; 76 | align-items: center; 77 | justify-content: center; 78 | flex-wrap: wrap; 79 | max-width: 800px; 80 | margin-top: 3rem; 81 | } 82 | 83 | .card { 84 | margin: 1rem; 85 | flex-basis: 45%; 86 | padding: 1.5rem; 87 | text-align: left; 88 | color: inherit; 89 | text-decoration: none; 90 | border: 1px solid #eaeaea; 91 | border-radius: 10px; 92 | transition: color 0.15s ease, border-color 0.15s ease; 93 | } 94 | 95 | .card:hover, 96 | .card:focus, 97 | .card:active { 98 | color: #0070f3; 99 | border-color: #0070f3; 100 | } 101 | 102 | .card h3 { 103 | margin: 0 0 1rem 0; 104 | font-size: 1.5rem; 105 | } 106 | 107 | .card p { 108 | margin: 0; 109 | font-size: 1.25rem; 110 | line-height: 1.5; 111 | } 112 | 113 | .logo { 114 | height: 1em; 115 | } 116 | 117 | @media (max-width: 600px) { 118 | .grid { 119 | width: 100%; 120 | flex-direction: column; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /apps/examples/nextjs-page-router/styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | } 8 | 9 | a { 10 | color: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | * { 15 | box-sizing: border-box; 16 | } 17 | -------------------------------------------------------------------------------- /apps/examples/vite/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/examples/vite/README.md: -------------------------------------------------------------------------------- 1 | # react-use-intercom in a Vite app 2 | 3 | Replace `INTERCOM_APP_ID` with your Intercom app id. 4 | -------------------------------------------------------------------------------- /apps/examples/vite/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import react from 'eslint-plugin-react' 4 | import reactHooks from 'eslint-plugin-react-hooks' 5 | import reactRefresh from 'eslint-plugin-react-refresh' 6 | 7 | export default [ 8 | { ignores: ['dist'] }, 9 | { 10 | files: ['**/*.{js,jsx}'], 11 | languageOptions: { 12 | ecmaVersion: 2020, 13 | globals: globals.browser, 14 | parserOptions: { 15 | ecmaVersion: 'latest', 16 | ecmaFeatures: { jsx: true }, 17 | sourceType: 'module', 18 | }, 19 | }, 20 | settings: { react: { version: '18.3' } }, 21 | plugins: { 22 | react, 23 | 'react-hooks': reactHooks, 24 | 'react-refresh': reactRefresh, 25 | }, 26 | rules: { 27 | ...js.configs.recommended.rules, 28 | ...react.configs.recommended.rules, 29 | ...react.configs['jsx-runtime'].rules, 30 | ...reactHooks.configs.recommended.rules, 31 | 'react/jsx-no-target-blank': 'off', 32 | 'react-refresh/only-export-components': [ 33 | 'warn', 34 | { allowConstantExport: true }, 35 | ], 36 | }, 37 | }, 38 | ] 39 | -------------------------------------------------------------------------------- /apps/examples/vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/examples/vite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-example", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint .", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.0.0", 14 | "react-dom": "^19.0.0", 15 | "react-use-intercom": "*" 16 | }, 17 | "devDependencies": { 18 | "@eslint/js": "^9.19.0", 19 | "@types/react": "^19.0.8", 20 | "@types/react-dom": "^19.0.3", 21 | "@vitejs/plugin-react-swc": "^3.5.0", 22 | "eslint": "^9.19.0", 23 | "eslint-plugin-react": "^7.37.4", 24 | "eslint-plugin-react-hooks": "^5.0.0", 25 | "eslint-plugin-react-refresh": "^0.4.18", 26 | "globals": "^15.14.0", 27 | "vite": "^6.1.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /apps/examples/vite/src/app.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { IntercomProvider, useIntercom } from 'react-use-intercom'; 3 | 4 | const INTERCOM_APP_ID = 'jcabc7e3'; 5 | 6 | export function App() { 7 | return ( 8 | alert('show')} 12 | > 13 | 14 | 15 | ); 16 | } 17 | 18 | function OurApp() { 19 | return ( 20 |
21 |

Our App

22 | 23 |
24 | ); 25 | } 26 | 27 | function TrackEvent() { 28 | const { trackEvent, boot, shutdown } = useIntercom(); 29 | 30 | useEffect(() => { 31 | trackEvent('event'); 32 | }, [trackEvent]); 33 | 34 | return ( 35 | <> 36 |

Tracing event in effect

37 | 38 | 39 | 40 | ); 41 | } 42 | 43 | export default App; 44 | -------------------------------------------------------------------------------- /apps/examples/vite/src/main.jsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | 4 | import { App } from './app.jsx'; 5 | 6 | createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ); 11 | -------------------------------------------------------------------------------- /apps/examples/vite/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react-swc'; 3 | 4 | // https://vite.dev/config/ 5 | export default defineConfig({ 6 | server: { 7 | port: 9300, 8 | }, 9 | plugins: [react()], 10 | clearScreen: false, 11 | }); 12 | -------------------------------------------------------------------------------- /apps/playground/.gitignore: -------------------------------------------------------------------------------- 1 | /cypress/videos 2 | /cypress/screenshots 3 | /cypress/fixtures 4 | -------------------------------------------------------------------------------- /apps/playground/assets/images/githubLogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /apps/playground/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrnt/react-use-intercom/a89e1734584ac8421d8d81681be48e378786116f/apps/playground/assets/images/logo.png -------------------------------------------------------------------------------- /apps/playground/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | // Enable cross-domain iframe access 5 | chromeWebSecurity: false, 6 | e2e: { 7 | baseUrl: 'http://localhost:5173/#/', 8 | video: false, 9 | specPattern: 'cypress/e2e/**/*.ts', 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /apps/playground/cypress/e2e/boot.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('boot', () => { 4 | beforeEach(() => { 5 | cy.visit('/useIntercom'); 6 | 7 | cy.intercept('https://api-iam.intercom.io/messenger/web/ping').as( 8 | 'intercomPing', 9 | ); 10 | }); 11 | 12 | afterEach(() => { 13 | cy.get('[data-cy=shutdown]').click(); 14 | }); 15 | 16 | it('should boot when calling `boot`', () => { 17 | cy.window().should('not.have.property', 'intercomSettings'); 18 | 19 | cy.get('[data-cy=boot]').click(); 20 | 21 | // Wait for the route aliased as 'intercomPing' to respond 22 | // without changing or stubbing its response 23 | cy.wait('@intercomPing'); 24 | 25 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('exist'); 26 | cy.window().should('have.property', 'Intercom'); 27 | cy.window().should('have.deep.property', 'intercomSettings', { 28 | app_id: 'jcabc7e3', 29 | }); 30 | }); 31 | 32 | it('should boot with seeded data when calling `boot`', () => { 33 | cy.window().should('not.have.property', 'intercomSettings'); 34 | 35 | cy.get('[data-cy=boot-seeded]').click(); 36 | 37 | cy.wait('@intercomPing'); 38 | 39 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('exist'); 40 | cy.window().should('have.property', 'Intercom'); 41 | cy.window().should('have.deep.property', 'intercomSettings', { 42 | app_id: 'jcabc7e3', 43 | name: 'Russo', 44 | }); 45 | }); 46 | 47 | it('should disable all methods before calling `boot`', () => { 48 | cy.get('[data-cy=update]').click(); 49 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('not.exist'); 50 | cy.get('[data-cy=update-seeded]').click(); 51 | 52 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('not.exist'); 53 | cy.get('[data-cy=show]').click(); 54 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('not.exist'); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /apps/playground/cypress/e2e/hide.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('hide', () => { 4 | it('should hide when calling `hide`', () => { 5 | cy.visit('/useIntercom'); 6 | 7 | cy.get('[data-cy=boot]').click(); 8 | 9 | cy.get('[data-cy=show]').click(); 10 | cy.get('iframe[name="intercom-messenger-frame"]').should('be.visible'); 11 | 12 | cy.get('[data-cy=hide]').click(); 13 | cy.get('iframe[name="intercom-messenger-frame"]').should('not.be.visible'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /apps/playground/cypress/e2e/initializeDelay.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('initialize delay', () => { 4 | it('should not load script on initial load with initialize delay', () => { 5 | cy.visit('/useIntercomWithTimeout'); 6 | 7 | cy.document() //Important, because Cypress has its own Head 8 | .get('head script') 9 | .contains('[src=https://widget.intercom.io/widget/jcabc7e3]') 10 | .should('not.exist'); 11 | 12 | cy.wait(5000); 13 | 14 | cy.document() //Important, because Cypress has its own Head 15 | .get('head script') 16 | .should('have.attr', 'src') 17 | .should('include', 'https://widget.intercom.io/widget/jcabc7e3'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/playground/cypress/e2e/mappers.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('mappers', () => { 4 | // TODO: 'contain' does not work for objects (like avatar...) 5 | it('should map passed props to raw propped on `boot`', () => { 6 | cy.visit('/useIntercom'); 7 | 8 | cy.window().should('not.have.property', 'intercomSettings'); 9 | 10 | cy.get('[data-cy=boot-extended-seeded]').click(); 11 | 12 | cy.window().should('have.property', 'Intercom'); 13 | 14 | cy.window() 15 | .its('intercomSettings') 16 | .should('be.an', 'object') 17 | .and('contain', { 18 | app_id: 'jcabc7e3', 19 | name: 'Russo', 20 | action_color: 'red', 21 | utm_content: 'content', 22 | vertical_padding: 10, 23 | alignment: 'left', 24 | background_color: 'green', 25 | created_at: 'now', 26 | custom_launcher_selector: '.id', 27 | hide_default_launcher: false, 28 | horizontal_padding: 10, 29 | language_override: 'en', 30 | phone: '0470', 31 | session_duration: 1000, 32 | unsubscribed_from_emails: false, 33 | last_request_at: 'now', 34 | utm_campaign: 'campaign', 35 | utm_source: 'source', 36 | utm_medium: 'medium', 37 | utm_term: 'term', 38 | user_id: '12345', 39 | my_custom_attribute: 'custom_attribute_value', 40 | my_second_custom_attribute: 'second_custom_attribute_value', 41 | }) 42 | .and('not.contain', { 43 | my_fifth_custom_attribute: 'custom_attribute_value', 44 | }); 45 | 46 | cy.window() 47 | .its('intercomSettings') 48 | .its('avatar') 49 | .should('be.an', 'object') 50 | .and('deep.equal', { 51 | type: 'image', 52 | image_url: 'https://github.com/devrnt/react-use-intercom', 53 | }); 54 | 55 | cy.window() 56 | .its('intercomSettings') 57 | .its('company') 58 | .should('be.an', 'object') 59 | .and('deep.equal', { 60 | company_id: 'company', 61 | created_at: 'now', 62 | industry: 'industry', 63 | monthly_spend: 10, 64 | name: 'name', 65 | plan: 'plan', 66 | size: 12, 67 | user_count: 100, 68 | website: 'https://github.com/devrnt/react-use-intercom', 69 | }); 70 | 71 | cy.window() 72 | .its('intercomSettings') 73 | .its('companies') 74 | .should('be.an', 'array') 75 | .and('deep.equal', [ 76 | { 77 | company_id: 'company', 78 | created_at: 'now', 79 | industry: 'industry', 80 | monthly_spend: 10, 81 | name: 'name', 82 | plan: 'plan', 83 | size: 12, 84 | user_count: 100, 85 | website: 'https://github.com/devrnt/react-use-intercom', 86 | }, 87 | ]); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /apps/playground/cypress/e2e/provider.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | beforeEach(() => { 4 | cy.intercept('https://api-iam.intercom.io/messenger/web/ping').as( 5 | 'intercomPing', 6 | ); 7 | cy.intercept('https://api-iam.intercom.io/messenger/web/open').as( 8 | 'intercomOpen', 9 | ); 10 | cy.intercept('https://api-iam.intercom.io/messenger/web/metrics').as( 11 | 'intercomMetrics', 12 | ); 13 | cy.intercept('https://api-iam.intercom.io/messenger/web/messages').as( 14 | 'intercomMessages', 15 | ); 16 | cy.intercept('https://api-iam.intercom.io/messenger/web/home').as( 17 | 'intercomHome', 18 | ); 19 | cy.intercept('https://api-iam.intercom.io/messenger/web/conversations').as( 20 | 'intercomConversations', 21 | ); 22 | }); 23 | 24 | describe('provider', () => { 25 | it('should render children', () => { 26 | cy.visit('/provider'); 27 | 28 | cy.get('p').should('be.visible'); 29 | }); 30 | }); 31 | 32 | describe('provider with events', () => { 33 | beforeEach(() => { 34 | cy.visit('/providerEvents'); 35 | }); 36 | 37 | it('should render children', () => { 38 | cy.get('p').should('be.visible'); 39 | }); 40 | 41 | it('should render default text', () => { 42 | cy.contains('default'); 43 | }); 44 | 45 | it('should not execute `onShow` when not booted', () => { 46 | cy.get('[data-cy=onShowText]').should('have.text', 'default'); 47 | cy.get('[data-cy=show]').click(); 48 | 49 | cy.get('[data-cy=onShowText]').should('have.text', 'default'); 50 | }); 51 | 52 | it('should execute `onShow` event when clicking `show`', () => { 53 | cy.get('[data-cy=onShowText]').should('have.text', 'default'); 54 | cy.get('[data-cy=boot]').click(); 55 | cy.get('[data-cy=show]').click(); 56 | 57 | cy.get('[data-cy=onShowText]').should('have.text', 'show was called'); 58 | }); 59 | 60 | it('should not execute `onHide` event when clicking `hide`', () => { 61 | cy.get('[data-cy=onHideText]').should('have.text', 'default'); 62 | cy.get('[data-cy=boot]').click(); 63 | 64 | cy.get('[data-cy=onHideText]').should('have.text', 'default'); 65 | }); 66 | 67 | it('should execute `onHide` event when clicking `hide`', () => { 68 | cy.get('[data-cy=onHideText]').should('have.text', 'default'); 69 | cy.get('[data-cy=boot]').click(); 70 | cy.get('[data-cy=show]').click(); 71 | cy.get('[data-cy=hide]').click(); 72 | 73 | cy.get('[data-cy=onHideText]').should('have.text', 'hide was called'); 74 | }); 75 | 76 | it('should execute `onUserEmailSupplied` event on `onUserEmailSupplied`', () => { 77 | cy.get('[data-cy=onUserEmailSuppliedText]').should('have.text', 'default'); 78 | 79 | cy.get('[data-cy=boot]').click(); 80 | 81 | cy.get('[data-cy=show]').click(); 82 | 83 | cy.get('iframe[name="intercom-messenger-frame"]').then(($iframe) => { 84 | const $body = $iframe.contents().find('body'); 85 | 86 | cy.wrap($body).contains('Send us a message').click(); 87 | 88 | cy.wait('@intercomHome'); 89 | cy.wait('@intercomConversations'); 90 | 91 | cy.wrap($body) 92 | .find('textarea') 93 | .should('exist') 94 | .type('hello') 95 | .type('{enter}'); 96 | 97 | cy.wait('@intercomMessages'); 98 | 99 | cy.wrap($body).contains('Get notified by email', { timeout: 10000 }); 100 | 101 | cy.wrap($body) 102 | .find('input[type="email"]') 103 | .type('hello@email.com') 104 | .type('{enter}'); 105 | 106 | cy.get('[data-cy=onUserEmailSuppliedText]').should( 107 | 'have.text', 108 | 'on user email supplied was called', 109 | ); 110 | }); 111 | }); 112 | }); 113 | 114 | describe('provider with `apiBase`', () => { 115 | beforeEach(() => { 116 | cy.visit('/providerApi'); 117 | }); 118 | 119 | it('should set `api_base` if provided', () => { 120 | cy.get('p').should('be.visible'); 121 | 122 | cy.window().should('have.deep.property', 'intercomSettings', { 123 | app_id: 'jcabc7e3', 124 | api_base: 'https://jcabc7e3.intercom-messenger.com', 125 | }); 126 | }); 127 | }); 128 | 129 | describe('provider with `autoBootProps`', () => { 130 | beforeEach(() => { 131 | cy.visit('/providerAutoBootProps'); 132 | }); 133 | 134 | it('should set properties if passed to `autoBootProps` when `autoBoot` is `true`', () => { 135 | cy.get('p').should('be.visible'); 136 | 137 | cy.get('span') 138 | .invoke('text') 139 | .then((phone) => { 140 | cy.window().should('have.deep.property', 'intercomSettings', { 141 | app_id: 'jcabc7e3', 142 | phone, 143 | }); 144 | }); 145 | }); 146 | }); 147 | -------------------------------------------------------------------------------- /apps/playground/cypress/e2e/show.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | beforeEach(() => { 4 | cy.visit('/useIntercom'); 5 | 6 | cy.intercept('https://api-iam.intercom.io/messenger/web/ping').as( 7 | 'intercomPing', 8 | ); 9 | cy.intercept('https://api-iam.intercom.io/messenger/web/open').as( 10 | 'intercomOpen', 11 | ); 12 | cy.intercept('https://api-iam.intercom.io/messenger/web/metrics').as( 13 | 'intercomMetrics', 14 | ); 15 | }); 16 | 17 | describe('show', () => { 18 | it('should show when calling `show`', () => { 19 | cy.get('[data-cy=boot]').click(); 20 | cy.wait('@intercomPing'); 21 | 22 | cy.get('[data-cy=show]').click(); 23 | cy.wait('@intercomOpen'); 24 | 25 | cy.get('iframe[name="intercom-messenger-frame"]') 26 | .should('be.visible') 27 | .then(($iframe) => { 28 | const $body = $iframe.contents().find('body'); 29 | 30 | cy.wrap($body).contains('Send us a message'); 31 | }); 32 | }); 33 | }); 34 | 35 | describe('showMessages', () => { 36 | it('should show when calling `showMessages`', () => { 37 | cy.get('[data-cy=boot]').click(); 38 | 39 | cy.get('[data-cy=show-messages]').click(); 40 | cy.get('iframe[name="intercom-messenger-frame"]') 41 | .should('be.visible') 42 | .then(($iframe) => { 43 | const $body = $iframe.contents().find('body'); 44 | 45 | cy.wrap($body).contains('Messages from the team will be shown here'); 46 | }); 47 | }); 48 | }); 49 | 50 | describe('showNewMessage', () => { 51 | it('should show new message `showNewMessage`', () => { 52 | cy.get('[data-cy=boot]').click(); 53 | cy.wait('@intercomPing'); 54 | 55 | cy.get('[data-cy=show-new-messages]').click(); 56 | cy.wait('@intercomOpen'); 57 | 58 | cy.get('iframe[name="intercom-messenger-frame"]') 59 | .should('be.visible') 60 | .then(($iframe) => { 61 | const $body = $iframe.contents().find('body'); 62 | 63 | cy.wrap($body).contains('Ask us anything, or share your feedback.'); 64 | cy.wrap($body).find('button[data-testid="go-back"]').should('exist'); 65 | }); 66 | }); 67 | 68 | it('should show new message with content when calling `showNewMessage`', () => { 69 | cy.get('[data-cy=boot]').click(); 70 | cy.wait('@intercomPing'); 71 | 72 | cy.get('[data-cy=show-messages-content]').click(); 73 | cy.wait('@intercomOpen'); 74 | 75 | cy.get('iframe[name="intercom-messenger-frame"]') 76 | .should('be.visible') 77 | .then(($iframe) => { 78 | const $body = $iframe.contents().find('body'); 79 | 80 | cy.wrap($body).find('button[data-testid="go-back"]').should('exist'); 81 | cy.wrap($body) 82 | .find('textarea') 83 | .should('exist') 84 | .should('have.value', 'pre-definded-content'); 85 | }); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /apps/playground/cypress/e2e/shutdown.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('shutdown', () => { 4 | beforeEach(() => { 5 | cy.visit('/useIntercom'); 6 | }); 7 | 8 | afterEach(() => { 9 | cy.get('[data-cy=shutdown]').click(); 10 | }); 11 | 12 | it('should shutdown when calling `shutdown`', () => { 13 | cy.window().should('not.have.property', 'intercomSettings'); 14 | 15 | cy.get('[data-cy=boot]').click(); 16 | cy.get('[data-cy=shutdown]').click(); 17 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('not.exist'); 18 | }); 19 | 20 | it('should remove keep `window.intercomSettings` when calling `shutdown`', () => { 21 | cy.window().should('not.have.property', 'intercomSettings'); 22 | 23 | cy.get('[data-cy=boot]').click(); 24 | cy.get('[data-cy=shutdown]').click(); 25 | cy.window().its('intercomSettings').should('be.undefined'); 26 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('not.exist'); 27 | }); 28 | 29 | it('should remove `app_id` when calling `hardShutdown`', () => { 30 | cy.window().should('not.have.property', 'intercomSettings'); 31 | 32 | cy.get('[data-cy=boot]').click(); 33 | cy.get('[data-cy=shutdown-hard]').click(); 34 | cy.window().its('intercomSettings').should('be.undefined'); 35 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('not.exist'); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /apps/playground/cypress/e2e/startSurvey.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | before(() => { 4 | cy.visit('/useIntercom'); 5 | 6 | cy.intercept('https://api-iam.intercom.io/messenger/web/ping').as( 7 | 'intercomPing', 8 | ); 9 | cy.intercept('https://api-iam.intercom.io/messenger/web/open').as( 10 | 'intercomOpen', 11 | ); 12 | cy.intercept('https://api-iam.intercom.io/messenger/web/metrics').as( 13 | 'intercomMetrics', 14 | ); 15 | cy.intercept('https://api-iam.intercom.io/messenger/web/home_cards').as( 16 | 'intercomHomeCards', 17 | ); 18 | 19 | cy.intercept('https://api-iam.intercom.io/messenger/web/surveys/*/fetch').as( 20 | 'intercomStartSurvey', 21 | ); 22 | }); 23 | 24 | describe('startSurvey', () => { 25 | it('start start the survey', () => { 26 | cy.get('[data-cy=boot]').click(); 27 | cy.wait('@intercomPing'); 28 | 29 | cy.get('[data-cy="start-survey"]').click(); 30 | 31 | cy.wait(2000); 32 | 33 | cy.wait('@intercomStartSurvey'); 34 | 35 | cy.get('iframe[name="intercom-banner-survey-frame"]').then(($iframe) => { 36 | const $body = $iframe.contents().find('body'); 37 | 38 | cy.wrap($body).contains('What is the highest mountain in the world'); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /apps/playground/cypress/e2e/update.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('update', () => { 4 | afterEach(() => { 5 | cy.get('[data-cy=shutdown]').click(); 6 | }); 7 | 8 | it('should update with data when calling `update`', () => { 9 | cy.visit('/useIntercom'); 10 | 11 | cy.get('[data-cy=boot-seeded]').click(); 12 | cy.window().should('have.deep.property', 'intercomSettings', { 13 | app_id: 'jcabc7e3', 14 | name: 'Russo', 15 | }); 16 | cy.get('[data-cy=update-seeded]').click(); 17 | cy.window().should('have.deep.property', 'intercomSettings', { 18 | app_id: 'jcabc7e3', 19 | name: 'ponas', 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /apps/playground/cypress/e2e/visitorId.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | before(() => { 4 | cy.visit('/useIntercom'); 5 | 6 | cy.intercept('https://api-iam.intercom.io/messenger/web/ping').as( 7 | 'intercomPing', 8 | ); 9 | cy.intercept('https://api-iam.intercom.io/messenger/web/open').as( 10 | 'intercomOpen', 11 | ); 12 | cy.intercept('https://api-iam.intercom.io/messenger/web/metrics').as( 13 | 'intercomMetrics', 14 | ); 15 | cy.intercept('https://api-iam.intercom.io/messenger/web/home_cards').as( 16 | 'intercomHomeCards', 17 | ); 18 | }); 19 | 20 | describe('getVisitorId', () => { 21 | it('should get visitor id when calling `getVisitorId`', () => { 22 | cy.get('[data-cy=boot]').click(); 23 | cy.wait('@intercomPing'); 24 | 25 | cy.get('button[data-cy="visitorId"]').click(); 26 | 27 | cy.get('p[data-cy="visitorIdValue"]').should('exist'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /apps/playground/cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // Add e2e commands here 2 | -------------------------------------------------------------------------------- /apps/playground/cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | }, 5 | "files": ["*.ts"] 6 | } 7 | -------------------------------------------------------------------------------- /apps/playground/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devrnt/react-use-intercom/a89e1734584ac8421d8d81681be48e378786116f/apps/playground/favicon.ico -------------------------------------------------------------------------------- /apps/playground/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png' { 2 | const value: any; 3 | export default value; 4 | } 5 | 6 | declare module '*.svg' { 7 | const value: any; 8 | export default value; 9 | } 10 | -------------------------------------------------------------------------------- /apps/playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | react-use-intercom 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /apps/playground/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | import App from './src/app'; 5 | 6 | const Root = () => ; 7 | 8 | ReactDOM.render(, document.getElementById('root')); 9 | -------------------------------------------------------------------------------- /apps/playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playground", 3 | "license": "MIT", 4 | "private": true, 5 | "homepage": "https://devrnt.github.io/react-use-intercom/", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "e2e": "cypress run", 10 | "e2e:open": "cypress open", 11 | "preview": "vite preview --host" 12 | }, 13 | "dependencies": { 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0", 16 | "react-router-dom": "^5.2.0", 17 | "react-use-intercom": "workspace:*", 18 | "styled-components": "^5.3.6", 19 | "styled-normalize": "^8.0.7" 20 | }, 21 | "devDependencies": { 22 | "@types/react": "^18.0.27", 23 | "@types/react-dom": "^18.0.10", 24 | "@types/react-router-dom": "^5.1.5", 25 | "@types/styled-components": "^5.1.2", 26 | "@vitejs/plugin-react": "^3.0.1", 27 | "cypress": "^14.0.0", 28 | "typescript": "^5.7.3", 29 | "vite": "^4.5.14" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /apps/playground/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { HashRouter as Router, NavLink, Route } from 'react-router-dom'; 3 | import styled from 'styled-components'; 4 | 5 | import { 6 | ProviderApiPage, 7 | ProviderAutoBootProps, 8 | ProviderEventsPage, 9 | ProviderPage, 10 | UseIntercomPage, 11 | UseIntercomTourPage, 12 | UseIntercomWithDelay, 13 | } from './modules'; 14 | import { Page, Style } from './modules/common'; 15 | 16 | const Navigation = styled.ul` 17 | padding: 0; 18 | display: grid; 19 | grid-template-columns: 1fr; 20 | grid-row-gap: 1.75rem; 21 | `; 22 | 23 | const Link = styled(NavLink)` 24 | text-decoration: none; 25 | color: var(--dark); 26 | 27 | &:visited, 28 | &:active { 29 | text-decoration: none; 30 | } 31 | 32 | > code { 33 | font-size: 1rem; 34 | } 35 | `; 36 | 37 | const App = () => { 38 | return ( 39 | <> 40 |