├── .eslintignore ├── .eslintrc ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── deploy.yml │ └── preview.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── SHIP_WITH_CARE.md ├── demos ├── build │ └── .gitkeep ├── demo-minimal-js │ ├── README.md │ ├── index.html │ ├── index.js │ ├── package-lock.json │ ├── package.json │ └── webpack.config.js ├── demo-react-ts │ ├── .eslintignore │ ├── .eslintrc │ ├── .prettierignore │ ├── .prettierrc.json │ ├── README.md │ ├── babel.config.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── components │ │ │ ├── Alert.tsx │ │ │ ├── App.tsx │ │ │ ├── AvailabilityDropdown.tsx │ │ │ ├── Components.tsx │ │ │ ├── FromNumbersDropdown.tsx │ │ │ ├── Icons.tsx │ │ │ ├── Keypad.tsx │ │ │ └── screens │ │ │ │ ├── CallEndedScreen.tsx │ │ │ │ ├── CallingScreen.tsx │ │ │ │ ├── DialingScreen.tsx │ │ │ │ ├── IncomingScreen.tsx │ │ │ │ ├── KeypadScreen.tsx │ │ │ │ └── LoginScreen.tsx │ │ ├── constants │ │ │ └── buttonIds.ts │ │ ├── hooks │ │ │ ├── useAutoFocus.ts │ │ │ ├── useCti.ts │ │ │ └── useTimer.ts │ │ ├── icons │ │ │ ├── caretDown.svg │ │ │ ├── checkmark.svg │ │ │ ├── deleteLeft.svg │ │ │ ├── externalLink.svg │ │ │ ├── microphone.svg │ │ │ ├── microphoneSlash.svg │ │ │ ├── mobileRetro.svg │ │ │ ├── phone.svg │ │ │ ├── recordVinyl.svg │ │ │ ├── sprocket.svg │ │ │ └── statusDot.svg │ │ ├── index.html │ │ ├── index.tsx │ │ ├── types │ │ │ ├── ScreenTypes.ts │ │ │ └── index.d.ts │ │ ├── utils │ │ │ ├── colors.ts │ │ │ ├── millisecondsToFormattedDuration.ts │ │ │ └── phoneNumberUtils.ts │ │ └── visitor-ui-component-library │ │ │ ├── button │ │ │ ├── VizExButton.stories.tsx │ │ │ ├── VizExButton.tsx │ │ │ ├── VizExCloseButton.js │ │ │ ├── VizExFileButton.js │ │ │ ├── VizExIconButton.tsx │ │ │ ├── VizExLoadingButton.js │ │ │ ├── constants │ │ │ │ ├── ButtonSizes.ts │ │ │ │ ├── ButtonUses.ts │ │ │ │ ├── IconButtonShapes.ts │ │ │ │ ├── IconButtonSizeToIconSize.ts │ │ │ │ ├── IconButtonUses.ts │ │ │ │ └── LoadingButtonUses.ts │ │ │ └── theme │ │ │ │ ├── buttonTheme.ts │ │ │ │ ├── closeButtonThemeOperators.js │ │ │ │ ├── iconButtonTheme.ts │ │ │ │ └── iconButtonThemeOperators.ts │ │ │ ├── card │ │ │ └── VizExCard.js │ │ │ ├── constants │ │ │ ├── keyCodes.ts │ │ │ └── sizes.ts │ │ │ ├── input │ │ │ ├── VizExCheckbox.js │ │ │ ├── VizExExpandingInput.js │ │ │ ├── VizExInput.js │ │ │ ├── constants │ │ │ │ ├── ExpandingInputVariations.ts │ │ │ │ └── InputVariations.ts │ │ │ └── theme │ │ │ │ ├── checkboxThemeOperators.js │ │ │ │ ├── expandingInputThemeOperators.js │ │ │ │ └── inputThemeOperators.js │ │ │ ├── link │ │ │ ├── VizExExternalLink.tsx │ │ │ ├── VizExLink.tsx │ │ │ ├── constants │ │ │ │ └── LinkVariations.ts │ │ │ └── theme │ │ │ │ ├── linkTheme.ts │ │ │ │ └── linkThemeOperators.ts │ │ │ ├── list │ │ │ ├── VizExList.stories.tsx │ │ │ ├── VizExList.tsx │ │ │ ├── VizExListItemButton.tsx │ │ │ └── theme │ │ │ │ ├── listItemButtonTheme.ts │ │ │ │ └── listTheme.ts │ │ │ ├── ratings │ │ │ ├── VizExCsatRating.js │ │ │ ├── constants │ │ │ │ └── RatingSizes.ts │ │ │ └── theme │ │ │ │ └── VizExCsatRatingThemeOperator.ts │ │ │ ├── theme │ │ │ ├── ColorConstants.ts │ │ │ ├── VizExThemeProvider.tsx │ │ │ ├── createTheme.ts │ │ │ ├── createThemeV2.ts │ │ │ ├── defaultTheme.ts │ │ │ ├── defaultThemeOperators.ts │ │ │ └── styled.d.ts │ │ │ ├── tooltip │ │ │ ├── VizExTooltip.js │ │ │ ├── VizExTooltipArrow.js │ │ │ ├── VizExTooltipBody.js │ │ │ ├── constants │ │ │ │ └── PlacementConstants.ts │ │ │ ├── theme │ │ │ │ └── tooltipThemeOperators.ts │ │ │ └── utils │ │ │ │ ├── getArrowSpacing.js │ │ │ │ ├── getBodySpacing.js │ │ │ │ └── getPlacement.js │ │ │ ├── typography │ │ │ ├── VizExSmall.js │ │ │ ├── constants │ │ │ │ └── SmallVariations.ts │ │ │ └── utils │ │ │ │ ├── getBodyTypographyStyles.js │ │ │ │ ├── getHeadingStyles.js │ │ │ │ └── getSmallStyles.js │ │ │ └── utils │ │ │ ├── SyntheticEvent.ts │ │ │ ├── adjustLuminance.ts │ │ │ ├── aria-live │ │ │ ├── AriaLiveAnnouncer.ts │ │ │ ├── AriaLiveContext.stories.tsx │ │ │ └── AriaLiveContext.tsx │ │ │ ├── browserTest.js │ │ │ ├── callIfValid.ts │ │ │ ├── curryable.ts │ │ │ ├── get.ts │ │ │ ├── getContrastRatio.ts │ │ │ ├── getTextColorFromBgColor.ts │ │ │ ├── hexToRGB.ts │ │ │ ├── hexToRgba.ts │ │ │ ├── isUnsafeUrl.ts │ │ │ ├── mergeDeep.ts │ │ │ ├── mixins.ts │ │ │ ├── pipe.ts │ │ │ ├── stripHTML.js │ │ │ ├── themePropType.ts │ │ │ └── types.ts │ ├── test │ │ ├── render.tsx │ │ ├── spec │ │ │ ├── components │ │ │ │ ├── App-test.tsx │ │ │ │ ├── FromNumbersDropdown-test.tsx │ │ │ │ └── screens │ │ │ │ │ ├── CallEndedScreen-test.tsx │ │ │ │ │ ├── CallingScreen-test.tsx │ │ │ │ │ ├── DialingScreen-test.tsx │ │ │ │ │ ├── IncomingScreen-test.tsx │ │ │ │ │ ├── KeypadScreen-test.tsx │ │ │ │ │ └── LoginScreen-test.tsx │ │ │ └── run-test.ts │ │ └── support │ │ │ └── jasmine-browser.json │ ├── tsconfig.json │ ├── webpack-test.config.js │ └── webpack.config.js ├── package-lock.json ├── package.json └── src │ └── index.html ├── docs └── images │ ├── InitializeCallWidgetIFrame.png │ └── OutboundCallSequenceDiagram.png ├── index.ts ├── package-lock.json ├── package.json ├── src ├── CallingExtensions.ts ├── Constants.ts ├── IFrameManager.ts └── types.ts ├── test ├── .eslintrc ├── spec │ ├── CallingExtensions-test.ts │ └── IFrameManager-test.ts └── support │ └── jasmine-browser.json ├── tsconfig.cjs.json ├── tsconfig.esm.json ├── webpack-test.config.js ├── webpack.cjs.config.js ├── webpack.common.js ├── webpack.config.js └── webpack.esm.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | demo/build 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "globals": { 4 | "window": true, 5 | "document": true 6 | }, 7 | "rules": { 8 | "no-multi-spaces": 0, 9 | "no-underscore-dangle": [0], 10 | "consistent-return": 0, 11 | "no-unused-expressions": [2, { "allowShortCircuit": true }], 12 | "no-param-reassign": 0, 13 | "func-names": 0, 14 | "space-before-function-paren": [2, "never"], 15 | "comma-dangle": 1, 16 | "no-shadow": 0, 17 | "guard-for-in": 0, 18 | "no-restricted-syntax": [2, "WithStatement"], 19 | "newline-per-chained-call": [2, { "ignoreChainWithDepth": 5 }], 20 | "space-in-parens": 0, 21 | "key-spacing": 0, 22 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 23 | "max-len": 1, 24 | "padded-blocks": 0, 25 | "no-console": 0, 26 | "no-continue": 0, 27 | "import/no-extraneous-dependencies": 0, 28 | "import/newline-after-import": 0, 29 | "no-mixed-operators": 0, 30 | "quotes": ["warn", "double"], 31 | "arrow-parens": ["error", "as-needed"], 32 | "import/prefer-default-export": 0, 33 | "arrow-body-style": 0, 34 | "prefer-object-spread": 0, 35 | "operator-linebreak": 0 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in the repo. 2 | # Visit the following for more information: 3 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 4 | * @HubSpot/calling-extensions-fe 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | **Checklist** 13 | - [ ] I have read the [FAQs](https://github.com/HubSpot/calling-extensions-sdk#faqs). 14 | - [ ] I have checked the [issue tracker](https://github.com/HubSpot/calling-extensions-sdk/issues) for duplicate issues. 15 | 16 | **Description** 17 | 18 | 19 | 20 | **Expected behavior** 21 | 22 | 23 | **To Reproduce** 24 | 25 | 1. Go to '...' 26 | 2. Click on '....' 27 | 3. Scroll down to '....' 28 | 4. See error 29 | 30 | **Screenshots/source code** 31 | 32 | 33 | **Device information** 34 | 35 | Device: [e.g. iPhone6] 36 | OS: [e.g. iOS8.1] 37 | Browser: [e.g. stock browser, safari] 38 | Browser Version: [e.g. 22] 39 | 40 | **Additional context** 41 | 42 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature request 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | **Checklist** 13 | - [ ] I have read the [FAQs](https://github.com/HubSpot/calling-extensions-sdk#faqs). 14 | - [ ] I have checked the [issue tracker](https://github.com/HubSpot/calling-extensions-sdk/issues) for duplicate issues. 15 | 16 | **Is your feature request related to a problem? Please describe.** 17 | 18 | I'm always frustrated when [...] 19 | 20 | **Describe the solution you'd like** 21 | 22 | 23 | **Screenshots/source code/links** 24 | 25 | 26 | **Additional context** 27 | 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Jira Ticket: CALL-xxxx 4 | 5 | 6 | 7 | ## Merge Checklist 8 | 9 | | Q | A 10 | | ------------------------ | -------------- 11 | | Adds Documentation? | 12 | | Any Dependency Changes? | 13 | | Patch: Bug Fix? | 14 | | Minor: New Feature? | 15 | | Major: Breaking Change? | 16 | 17 | [BRAVE Checklist](https://github.com/HubSpot/calling-extensions-sdk/blob/master/SHIP_WITH_CARE.md) 18 | 19 | - [ ] I have read the BRAVE checklist and confirmed if the following is necessary. 20 | 21 | 22 | 23 | | Q | A 24 | | ------------------------------ | -------------- 25 | | Backwards Compatible? | 26 | | Rollout/Rollback Plan? | 27 | | Automated test coverage? | 28 | | Verified that changes work? | 29 | | Expect Dependencies to Fail? | 30 | 31 | 32 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy demo apps to GH Pages 2 | concurrency: ci-${{ github.ref }} 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | paths: 8 | - 'demos/**' 9 | permissions: 10 | contents: write 11 | defaults: 12 | run: 13 | working-directory: demos 14 | jobs: 15 | build-and-deploy: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout repository 🛎️ 19 | uses: actions/checkout@v3 20 | - name: Install and build 🔧 21 | run: npm run build:gh 22 | - name: Run tests ✅ 23 | run: npm run test 24 | - name: Deploy 🚀 25 | uses: JamesIves/github-pages-deploy-action@v4 26 | with: 27 | folder: demos/build 28 | branch: gh-pages 29 | clean-exclude: pr-preview 30 | -------------------------------------------------------------------------------- /.github/workflows/preview.yml: -------------------------------------------------------------------------------- 1 | name: Deploy PR previews for demo apps 2 | run-name: ${{ github.actor }} is deploying a PR 3 | concurrency: preview-${{ github.ref }} 4 | on: 5 | pull_request: 6 | types: 7 | - opened 8 | - reopened 9 | - synchronize 10 | paths: 11 | - "demos/**" 12 | defaults: 13 | run: 14 | working-directory: demos 15 | jobs: 16 | deploy-preview: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout repository 🛎️ 20 | uses: actions/checkout@v3 21 | - name: Install and build 🔧 22 | run: npm run build:gh 23 | - name: Run tests ✅ 24 | run: npm run test 25 | - name: Deploy preview 🚀 26 | uses: rossjrw/pr-preview-action@v1 27 | with: 28 | source-dir: demos/build 29 | preview-branch: gh-pages 30 | umbrella-dir: pr-preview 31 | action: auto 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | dist 4 | bin 5 | *.pem 6 | coverage 7 | .DS_STORE 8 | lib 9 | demos/build/* 10 | !demos/build/.gitkeep 11 | dist-test 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.pem 2 | .eslintignore 3 | .eslintrc 4 | node_modules 5 | circle.yml 6 | webpack.config.js 7 | coverage 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry = https://registry.npmjs.org/ -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-test 3 | node_modules 4 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { "arrowParens": "avoid", "trailingComma": "all" } 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 HubSpot 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | [![calling-extensions-sdk on npm](https://img.shields.io/npm/v/@hubspot/calling-extensions-sdk.svg?style=flat-square)](http://npmjs.com/@hubspot/calling-extensions-sdk) 4 | 5 | The Calling Extensions SDK allows apps to provide a custom calling option to HubSpot users directly from a record in the CRM. 6 | 7 | A calling extension consists of three main components: 8 | 9 | - The Calling Extensions SDK, a JavaScript SDK that enables communication between your app and HubSpot. 10 | - The calling settings endpoints, which are used to set the calling settings for your app. Each HubSpot account that connects to your app will use these settings. 11 | - The calling iframe, which is where your app appears to HubSpot users and is configured using the calling settings endpoints. 12 | 13 | ## Getting Started 14 | 15 | 1. Create an app from your [app developer account](https://developers.hubspot.com/beta-docs/getting-started/account-types#app-developer-accounts), and create a [developer test account](https://developers.hubspot.com/beta-docs/getting-started/account-types#developer-test-accounts) to test your app. 16 | 1. [Install the Calling Extensions SDK](https://developers.hubspot.com/beta-docs/guides/api/crm/extensions/calling-sdk#install-the-calling-extensions-sdk-on-your-calling-app) on your calling app. Alternatively, you can [run the demos](https://developers.hubspot.com/beta-docs/guides/api/crm/extensions/calling-sdk#run-the-demo-calling-app) if you'd like to see the SDK in action first. 17 | 1. Learn how to [use the Calling Extensions SDK](https://developers.hubspot.com/beta-docs/guides/api/crm/extensions/calling-sdk#using-the-calling-extensions-sdk). 18 | 1. View the [available calling events](https://developers.hubspot.com/beta-docs/guides/api/crm/extensions/calling-sdk#events). 19 | 1. [Test your app](https://developers.hubspot.com/beta-docs/guides/api/crm/extensions/calling-sdk#test-your-app). 20 | 1. [Get your app ready for production](https://developers.hubspot.com/beta-docs/guides/api/crm/extensions/calling-sdk#get-your-app-ready-for-production). 21 | 1. [Publish your app to the HubSpot marketplace](https://developers.hubspot.com/beta-docs/guides/api/crm/extensions/calling-sdk#publish-your-calling-app-to-the-hubspot-marketplace). 22 | 23 | ## Getting Help 24 | 25 | 1. [Review the FAQs](https://developers.hubspot.com/beta-docs/guides/api/crm/extensions/calling-sdk#calling-sdk-%7C-frequently-asked-questions). 26 | 1. Learn how to [get help with HubSpot](https://knowledge.hubspot.com/help-and-resources/get-help-with-hubspot?_gl=1*7nomik*_gcl_au*OTQ3MjgyMDk4LjE3MzA4MzkyOTE.*_ga*MjA4MzEyMjM0Mi4xNzMwODM5Mjkx*_ga_LXTM6CQ0XK*MTczMTQyOTg1Mi40LjEuMTczMTQzMDQ1My42MC4wLjA.&_ga=2.181003488.658339848.1731429852-2083122342.1730839291). 27 | -------------------------------------------------------------------------------- /SHIP_WITH_CARE.md: -------------------------------------------------------------------------------- 1 | Ship with Care - how to deploy changes safely 2 | 3 | **Reliability is a fundamental feature** that customers expect when choosing HubSpot. We are all responsible for meeting this expectation as we continue to delight our customers by shipping code. To help provide guidance on how to achieve this, we have compiled a handful of best practices we call the **BRAVE** checklist. These guidelines aren’t meant to stop you from shipping, but to make it easier and more efficient to ship changes without fear of what might break whether you're deploying something new, changing config, ungating a feature, refactoring code, or running a migration. 4 | 5 | Make your changes **Backward compatible**. There's no such thing as an atomic deployment at HubSpot. You will have multiple versions of your code running at once even if it's only for a short time. Prefer incremental and backward compatible rollouts of risky changes over "deploy the world" changesets. While sometimes this requires additional time and effort, the benefits of avoiding potential disruptions and preserving customer experience make it the preferred approach. The best way to move fast is to take your time and do the job right - Slow is smooth and smooth is fast. We will continue to build infrastructure and tooling to make these types of changes faster and simpler. 6 | 7 | Have a **Rollout (and Rollback!) plan**. The act of thinking through the rollout and anticipating a rollback will help you understand and manage the risk. Consider rolling out incrementally to reduce the customer impact of an issue with your changes. Roll-outs should be slow, roll-backs fast. Put your plan in your PR description if the rollout and rollback are non-obvious or non-trivial for your change. In case of non-obvious or non-trivial rollbacks, consider testing your rollbacks. Rollback early and often, it is better to have a false rollback than a critsit. 8 | 9 | Write sufficient **Automated test coverage to validate your changes**. Use unit, integration, and acceptance tests appropriately. Use automated tests to validate backward compatibility. Think through and test the edge cases. If you are working in a part of a codebase without much test coverage, add tests to cover existing behavior before making changes. 10 | 11 | Manually **Verify the change works as expected**. This is especially important after you make changes to incorporate PR feedback. If your branch has fallen out of date with the main branch, rebase in changes and verify. If manual validation catches an issue while tests are passing, update the automated tests. Look for two signals across the old and new versions: “the change is working” vs “the change is not working.” Don't deploy and walk away. Keep an eye on dashboards and be ready to respond. 12 | 13 | **Expect your dependencies to fail**. Think through what dependencies you're introducing and how the app will behave if they fail. Consider strategies like graceful degradation to handle dependency failures, where your app/feature falls back to an experience that while not optimal still delivers essential content and functionality. 14 | 15 | Reviewers should help use the information provided in this checklist to inform their review. Reviewers should be encouraged to read the PR description and validate that the code meets the expectations set out in the **BRAVE** checklist. 16 | -------------------------------------------------------------------------------- /demos/build/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HubSpot/calling-extensions-sdk/b4612190c5ff8dd511509b1233b57e8d262104eb/demos/build/.gitkeep -------------------------------------------------------------------------------- /demos/demo-minimal-js/README.md: -------------------------------------------------------------------------------- 1 | # Demo Widget - Minimal JS 2 | 3 | Features a minimal implementation of the Calling Extensions SDK using JavaScript, HTML, and CSS. 4 | 5 | Please note: this demo app isn't a fully functional calling app and uses mock data to provide a more realistic experience. 6 | -------------------------------------------------------------------------------- /demos/demo-minimal-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "calling-extensions-sdk-demo-minimal-js", 3 | "version": "1.0.0", 4 | "description": "HubSpot calling extension sdk demo minimal js", 5 | "private": true, 6 | "scripts": { 7 | "build": "webpack", 8 | "build:gh": "npm ci && cross-env NODE_ENV=production npm run build", 9 | "serve": "webpack serve", 10 | "start": "webpack serve --open" 11 | }, 12 | "license": "MIT", 13 | "engines": { 14 | "node": ">=14" 15 | }, 16 | "devDependencies": { 17 | "cross-env": "^7.0.3", 18 | "html-webpack-plugin": "^5.5.0", 19 | "webpack": "^5.94.0", 20 | "webpack-cli": "^5.0.1", 21 | "webpack-dev-server": "^4.11.1" 22 | }, 23 | "dependencies": { 24 | "uuid": "^10.0.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demos/demo-minimal-js/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 3 | module.exports = { 4 | entry: "./index.js", 5 | mode: "development", 6 | plugins: [new HtmlWebpackPlugin({ 7 | template: "./index.html", 8 | inject: false, 9 | filename: "demo-minimal-js.html", 10 | })], 11 | output: { 12 | libraryTarget: "umd", 13 | path: path.resolve(__dirname, "bin"), 14 | filename: "demo-minimal-js.bundle.js", 15 | clean: true, 16 | }, 17 | devServer: { 18 | https: true, 19 | port: 9025, 20 | static: { 21 | directory: path.resolve(__dirname), 22 | }, 23 | historyApiFallback: { 24 | index: "index.html", 25 | }, 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /demos/demo-react-ts/.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | visitor-ui-component-library 3 | -------------------------------------------------------------------------------- /demos/demo-react-ts/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:@typescript-eslint/recommended", 4 | "plugin:import/errors", 5 | "plugin:import/warnings", 6 | "plugin:import/typescript", 7 | "plugin:react-hooks/recommended", 8 | "eslint:recommended" 9 | ], 10 | "parser": "@typescript-eslint/parser", 11 | "plugins": ["@typescript-eslint"], 12 | "env": { "browser": true, "jasmine": true }, 13 | "globals": { 14 | "NodeJS": true 15 | }, 16 | "rules": { 17 | "import/extensions": [ 18 | "error", 19 | "ignorePackages", 20 | { 21 | "js": "never", 22 | "jsx": "never", 23 | "ts": "never", 24 | "tsx": "never" 25 | } 26 | ], 27 | "react/jsx-filename-extension": [1, { "extensions": [".tsx", ".ts"] }], 28 | "react/react-in-jsx-scope": 0, 29 | "react/jsx-one-expression-per-line": 0, 30 | "object-curly-newline": 0, 31 | "@typescript-eslint/ban-types": 1, 32 | "arrow-parens": 0, 33 | "react/jsx-props-no-spreading": 0, 34 | "space-before-function-paren": 0, 35 | "@typescript-eslint/no-empty-function": 0, 36 | "implicit-arrow-linebreak": 0, 37 | "function-paren-newline": 0, 38 | "@typescript-eslint/no-unused-vars": [ 39 | 1, 40 | { 41 | "argsIgnorePattern": "^_" 42 | } 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /demos/demo-react-ts/.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts: 2 | dist 3 | node_modules 4 | visitor-ui-component-library 5 | -------------------------------------------------------------------------------- /demos/demo-react-ts/.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /demos/demo-react-ts/README.md: -------------------------------------------------------------------------------- 1 | # Demo Widget - React TypeScript 2 | 3 | Features a real-life implementation of the Calling Extensions SDK using React, TypeScript, and Styled Components to act as a blueprint for your app. 4 | 5 | Please note: this demo app isn't a fully functional calling app and uses mock data to provide a more realistic experience. 6 | -------------------------------------------------------------------------------- /demos/demo-react-ts/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ["babel-plugin-styled-components"], 3 | presets: [ 4 | "@babel/preset-typescript", 5 | "@babel/preset-react", 6 | "@babel/preset-env", 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /demos/demo-react-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "calling-integration-sdk-demo-react-ts", 3 | "version": "1.0.0", 4 | "description": "HubSpot calling integration sdk demo react ts", 5 | "private": true, 6 | "scripts": { 7 | "build": "webpack", 8 | "build:gh": "npm ci && cross-env NODE_ENV=production npm run build", 9 | "serve": "webpack serve", 10 | "start": "webpack serve --open", 11 | "test:build": "cross-env NODE_ENV=test npx webpack --config webpack-test.config.js --mode development", 12 | "test:watch": "cross-env NODE_ENV=test npx webpack --config webpack-test.config.js --mode development --watch", 13 | "test": "npm run test:build && jasmine-browser-runner runSpecs --config=test/support/jasmine-browser.json --browser=headlessChrome", 14 | "test:serve": "npm run test:watch & jasmine-browser-runner serve --config=test/support/jasmine-browser.json" 15 | }, 16 | "license": "MIT", 17 | "engines": { 18 | "node": ">=14" 19 | }, 20 | "dependencies": { 21 | "@hubspot/calling-extensions-sdk": "^0.9.3", 22 | "react": "^18.2.0", 23 | "react-aria": "^3.22.0", 24 | "react-dom": "^18.2.0", 25 | "styled-components": "^5.3.6", 26 | "uuid": "^10.0.0" 27 | }, 28 | "devDependencies": { 29 | "@babel/core": "^7.20.12", 30 | "@babel/preset-env": "^7.20.2", 31 | "@babel/preset-react": "^7.18.6", 32 | "@babel/preset-typescript": "^7.18.6", 33 | "@svgr/webpack": "^6.5.1", 34 | "@testing-library/jasmine-dom": "^1.3.3", 35 | "@testing-library/react": "^14.0.0", 36 | "@types/jasmine": "^4.3.1", 37 | "@types/react": "^18.0.27", 38 | "@types/react-dom": "^18.0.10", 39 | "@types/styled-components": "^5.1.26", 40 | "@types/testing-library__jasmine-dom": "^1.3.0", 41 | "@types/uuid": "^10.0.0", 42 | "babel-loader": "^9.1.2", 43 | "babel-plugin-styled-components": "^2.0.7", 44 | "babel-preset-react-app": "^10.0.1", 45 | "cross-env": "^7.0.3", 46 | "html-webpack-plugin": "^5.5.0", 47 | "jasmine-browser-runner": "^1.3.0", 48 | "jasmine-core": "^4.5.0", 49 | "prettier": "2.8.4", 50 | "prop-types": "^15.8.1", 51 | "webpack": "^5.94.0", 52 | "webpack-cli": "^5.0.1", 53 | "webpack-dev-server": "^4.11.1" 54 | }, 55 | "resolutions": { 56 | "styled-components": "^5.3.6" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /demos/demo-react-ts/src/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, MouseEventHandler } from "react"; 2 | import { BATTLESHIP, OLAF } from "../utils/colors"; 3 | import { LinkButton } from "./Components"; 4 | 5 | function Alert({ 6 | title, 7 | onConfirm, 8 | }: { 9 | title: string | ReactElement; 10 | onConfirm: MouseEventHandler; 11 | }) { 12 | return ( 13 |
22 | {title} 23 | 24 | × 25 | 26 |
27 | ); 28 | } 29 | 30 | export default Alert; 31 | -------------------------------------------------------------------------------- /demos/demo-react-ts/src/components/FromNumbersDropdown.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo, useCallback } from "react"; 2 | 3 | import { 4 | FromNumberTooltip, 5 | FromNumberToggleButton, 6 | FromNumberButton, 7 | } from "./Components"; 8 | import { CaretDownSvg } from "./Icons"; 9 | import { 10 | FROM_NUMBER_ONE, 11 | FROM_NUMBER_TWO, 12 | getFormattedFromNumber, 13 | } from "../utils/phoneNumberUtils"; 14 | 15 | function FromNumbersDropdown({ 16 | fromNumber, 17 | setFromNumber, 18 | setToggleFromNumbers, 19 | toggleFromNumbers, 20 | }: { 21 | fromNumber: string; 22 | setFromNumber: Function; 23 | setToggleFromNumbers: Function; 24 | toggleFromNumbers: boolean; 25 | }) { 26 | const handleFromNumber = useCallback( 27 | (phoneNumber: string) => { 28 | setFromNumber(phoneNumber); 29 | setToggleFromNumbers(false); 30 | }, 31 | [setFromNumber, setToggleFromNumbers] 32 | ); 33 | 34 | const FromNumbers = useMemo(() => { 35 | return ( 36 |
37 |
38 | handleFromNumber(FROM_NUMBER_ONE)} 41 | > 42 | My US Number 43 | {getFormattedFromNumber(FROM_NUMBER_ONE)} 44 | 45 |
46 |
47 | handleFromNumber(FROM_NUMBER_TWO)} 50 | > 51 | My UK Number 52 | {getFormattedFromNumber(FROM_NUMBER_TWO)} 53 | 54 |
55 |
56 | ); 57 | }, [handleFromNumber]); 58 | 59 | return ( 60 | <> 61 | From number: 62 | 67 | setToggleFromNumbers(!toggleFromNumbers)} 70 | > 71 | {getFormattedFromNumber(fromNumber)} {CaretDownSvg} 72 | 73 | 74 | 75 | ); 76 | } 77 | 78 | export default FromNumbersDropdown; 79 | -------------------------------------------------------------------------------- /demos/demo-react-ts/src/components/Icons.tsx: -------------------------------------------------------------------------------- 1 | import Phone from "../icons/phone.svg"; 2 | import Record from "../icons/recordVinyl.svg"; 3 | import DeleteLeft from "../icons/deleteLeft.svg"; 4 | import Mute from "../icons/microphone.svg"; 5 | import Unmute from "../icons/microphoneSlash.svg"; 6 | import Keypad from "../icons/mobileRetro.svg"; 7 | import CaretDown from "../icons/caretDown.svg"; 8 | import StatusDot from "../icons/statusDot.svg"; 9 | import Checkmark from "../icons/checkmark.svg"; 10 | import ExternalLink from "../icons/externalLink.svg"; 11 | import Sprocket from "../icons/sprocket.svg"; 12 | import { 13 | CALYPSO, 14 | SLINKY, 15 | OZ_DARK, 16 | CANDY_APPLE, 17 | EERIE, 18 | LORAX, 19 | } from "../utils/colors"; 20 | 21 | export const StartCallSvg = ; 22 | 23 | export const EndCallSvg = ( 24 | 25 | ); 26 | 27 | export const StartRecordSvg = ( 28 |