Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const HomePage = () => {
## API
* [IntercomProvider](#intercomprovider)
* [useIntercom](#useintercom)
* [IntercomProps](#intercomprops)

### IntercomProvider
`IntercomProvider` is used to initialize the `window.Intercom` instance. It makes sure the initialization is only done once. If any listeners are passed, the `IntercomProvider` will make sure these are attached.
Expand Down Expand Up @@ -177,6 +178,24 @@ const HomePage = () => {
);
};
```
### IntercomProps
All the Intercom default attributes/props are camel cased (`appId` instead of `app_id`) in `react-use-intercom`, see [IntercomProps](src/types.ts) to see what attributes you can pass to `boot` or `update`. Or check the Intercom [docs](https://developers.intercom.com/installing-intercom/docs/javascript-api-attributes-objects)
to see all the available attributes/props.

**Remark** - all the listed Intercom attributes [here](https://developers.intercom.com/installing-intercom/docs/javascript-api-attributes-objects) are snake cased, in `react-use-intercom` are these camel cased.

#### Custom attributes
Still want to pass custom attributes to Intercom? Whether `boot` or `update` is used, you can add your custom properties by passing these through `customAttributes` in the `boot` or `update` method.

**Remark** - the keys of the `customAttributes` object should be snake cased. They are rawly passed to Intercom.
```javascript
const { boot } = useIntercom();

boot({
name: 'Russo',
customAttributes: { custom_attribute_key: 'hi there' },
})
```

## Playground
Example playground to showcase the functionalities of `react-use-intercom`.
Expand Down
89 changes: 89 additions & 0 deletions cypress/integration/mappers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/// <reference types="cypress" />

describe('mappers', () => {
// TODO: 'contain' does not work for objects (like avatar...)
it('should map passed props to raw propped on `boot`', () => {
cy.visit('/useIntercom');

cy.window().should('not.have.property', 'intercomSettings');

cy.get('[data-cy=boot-extended-seeded]').click();

cy.window().should('have.property', 'Intercom');

cy.window()
.its('intercomSettings')
.should('be.an', 'object')
.and('contain', {
app_id: 'jcabc7e3',
name: 'Russo',
action_color: 'red',
utm_content: 'content',
vertical_padding: 10,
alignment: 'left',
background_color: 'green',
created_at: 'now',
custom_launcher_selector: '.id',
hide_default_launcher: false,
horizontal_padding: 10,
language_override: 'en',
phone: '0470',
session_duration: 1000,
unsubscribed_from_emails: false,
last_request_at: 'now',
utm_campaign: 'campaign',
utm_source: 'source',
utm_medium: 'medium',
utm_term: 'term',
user_id: '12345',
my_custom_attribute: 'custom_attribute_value',
my_second_custom_attribute: 'second_custom_attribute_value',
})
.and('not.contain', {
my_fifth_custom_attribute: 'custom_attribute_value',
});

cy.window()
.its('intercomSettings')
.its('avatar')
.should('be.an', 'object')
.and('deep.equal', {
type: 'image',
image_url: 'https://github.com/devrnt/react-use-intercom',
});

cy.window()
.its('intercomSettings')
.its('company')
.should('be.an', 'object')
.and('deep.equal', {
company_id: 'company',
created_at: 'now',
industry: 'industry',
monthly_spend: 10,
name: 'name',
plan: 'plan',
size: 12,
user_count: 100,
website: 'https://github.com/devrnt/react-use-intercom',
});

cy.window()
.its('intercomSettings')
.its('companies')
.should('be.an', 'array')
.and('deep.equal', [
{
company_id: 'company',
created_at: 'now',
industry: 'industry',
monthly_spend: 10,
name: 'name',
plan: 'plan',
size: 12,
user_count: 100,
website: 'https://github.com/devrnt/react-use-intercom',
},
]);
});
});
2 changes: 1 addition & 1 deletion cypress/integration/visitorId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe('getVisitorId', () => {
cy.get('[data-cy=boot]').click();

cy.get('[data-cy="visitorIdValue"]').should('not.be.visible');
cy.wait(1500);
cy.wait(2000);

cy.get('[data-cy="visitorId"]').click();
cy.get('[data-cy="visitorIdValue"]').should('be.visible');
Expand Down
93 changes: 87 additions & 6 deletions example/modules/useIntercom/useIntercom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,80 @@ const RawUseIntercomPage = () => {
showMessages,
showNewMessages,
getVisitorId,
startTour,
trackEvent,
} = useIntercom();
const handleBoot = React.useCallback(() => boot(), [boot]);

const handleBoot = React.useCallback(() => boot({ name: 'Russo' }), [boot]);
const handleSeededBoot = React.useCallback(() => boot({ name: 'Russo' }), [
boot,
]);

const handleExtendedSeededBoot = React.useCallback(
() =>
boot({
name: 'Russo',
actionColor: 'red',
email: 'russo@email.com',
utmContent: 'content',
verticalPadding: 10,
alignment: 'left',
avatar: {
type: 'image',
imageUrl: 'https://github.com/devrnt/react-use-intercom',
},
company: {
companyId: 'company',
createdAt: 'now',
industry: 'industry',
monthlySpend: 10,
name: 'name',
plan: 'plan',
size: 12,
userCount: 100,
website: 'https://github.com/devrnt/react-use-intercom',
},
companies: [
{
companyId: 'company',
createdAt: 'now',
industry: 'industry',
monthlySpend: 10,
name: 'name',
plan: 'plan',
size: 12,
userCount: 100,
website: 'https://github.com/devrnt/react-use-intercom',
},
],
backgroundColor: 'green',
createdAt: 'now',
customLauncherSelector: '.id',
hideDefaultLauncher: false,
horizontalPadding: 10,
languageOverride: 'en',
phone: '0470',
sessionDuration: 1000,
unsubscribedFromEmails: false,
userHash: '123',
lastRequestAt: 'now',
utmCampaign: 'campaign',
utmSource: 'source',
utmMedium: 'medium',
utmTerm: 'term',
userId: '12345',
customAttributes: {
my_custom_attribute: 'custom_attribute_value',
my_second_custom_attribute: 'second_custom_attribute_value',
},
}),
[boot],
);

const handleUpdate = React.useCallback(() => {
update();
}, [update]);

const handleSeededUpdate = React.useCallback(() => {
update({ name: 'ponas' });
}, [update]);

Expand Down Expand Up @@ -75,13 +142,27 @@ const RawUseIntercomPage = () => {
boots the Intercom instance, not needed if <code>autoBoot</code> in{' '}
<code>IntercomProvider</code> is <code>true</code>
</p>
<Button label="Boot" data-cy="boot" onClick={boot} />
<Button label="Boot" data-cy="boot" onClick={handleBoot} />
</Item>
<Item>
<p>
boots the Intercom instance with given <code>props</code>
</p>
<Button label="Boot props" data-cy="boot-seeded" onClick={handleBoot} />
<Button
label="Boot props"
data-cy="boot-seeded"
onClick={handleSeededBoot}
/>
</Item>
<Item>
<p>
boots the Intercom instance with given extended <code>props</code>
</p>
<Button
label="Boot extended props"
data-cy="boot-extended-seeded"
onClick={handleExtendedSeededBoot}
/>
</Item>
<Item>
<p>shuts down the Intercom instance</p>
Expand All @@ -101,7 +182,7 @@ const RawUseIntercomPage = () => {
</Item>
<Item>
<p>Initiates a 'ping'</p>
<Button label="Update" data-cy="update" onClick={update} />
<Button label="Update" data-cy="update" onClick={handleUpdate} />
</Item>
<Item>
<p>
Expand All @@ -110,7 +191,7 @@ const RawUseIntercomPage = () => {
<Button
label="Update with props"
data-cy="update-seeded"
onClick={handleUpdate}
onClick={handleSeededUpdate}
/>
</Item>
<Item>
Expand Down
6 changes: 4 additions & 2 deletions src/mappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,21 @@ export const mapDataAttributesToRawDataAttributes = (
unsubscribed_from_emails: attributes.unsubscribedFromEmails,
language_override: attributes.languageOverride,
utm_campaign: attributes.utmCampaign,
utm_content: attributes.utmContent,
utm_medium: attributes.utmMedium,
utm_source: attributes.utm_source,
utm_source: attributes.utmSource,
utm_term: attributes.utmTerm,
avatar:
attributes.avatar &&
mapDataAttributesAvatarToRawDataAttributesAvatar(attributes.avatar),
user_hash: attributes.userHash,
company:
attributes.company &&
mapDataAttributesCompanyToRawDataAttributesCompany(attributes.c),
mapDataAttributesCompanyToRawDataAttributesCompany(attributes.company),
companies: attributes.companies?.map(
mapDataAttributesCompanyToRawDataAttributesCompany,
),
...attributes.customAttributes,
});

export const mapRawIntercomPropsToIntercomProps = (
Expand Down
12 changes: 10 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export type RawDataAttributes = {
user_hash?: string;
company?: RawDataAttributesCompany;
companies?: RawDataAttributesCompany[];
[custom_property: string]: any;
customAttributes?: Record<string, any>;
};

export type DataAttributes = {
Expand Down Expand Up @@ -210,11 +210,19 @@ export type DataAttributes = {
companies?: DataAttributesCompany[];
/**
* You can do this anytime by adding additional key/value pairs to your intercomSettings code snippet
* These should be raw snake_cased
*
* @example
* ```
* customAttributes={
* my_custom_attibute: 'my attribute value'
* }
* ```
*
* @see {@link https://www.intercom.com/help/en/articles/179-send-custom-user-attributes-to-intercom}
* @remarks The key is the attribute name. The value is a placeholder for the data you’ll track
*/
[customProperty: string]: any;
customAttributes?: Record<string, any>;
};

export type IntercomMethod =
Expand Down