Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new HOC for instrumenting components manually #266

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
__BEGIN_UNRELEASED__
## [Unreleased]
### Added
- Added `withHeapAutocapture` to support HOC instrumentation of components that
may have a custom prop
- Added type declarations for HOC instrumentation for `Touchable` and `Pressable`
### Changed
### Deprecated
### Removed
Expand Down
38 changes: 38 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import * as React from 'react';
import { JSXElementConstructor } from 'react';
import { GestureResponderEvent } from 'react-native';

/**
* `setAppId` Initializes Heap tracking and sets the app ID where you'll be sending data. It can be used to switch
Expand Down Expand Up @@ -122,6 +124,42 @@ export function getUserId(): Promise<string>;
*/
export function getSessionId(): Promise<string|null>;

/**
* Returns an HOC of a component that tracks specific specified actions as touches
*
* @param Component the component to autocapture for
*/
export function withHeapAutocapture<
P,
C extends JSXElementConstructor<Partial<P>>
>(Component: C, propName: keyof P, eventType?: 'touch' | 'change'): C;

/**
* Returns an HOC of a component that tracks touches, i.e. calls to `onPress` or
* `onLongPress`
*
* @param TouchableComponent the component to autocapture touches for
*/
export function withHeapTouchableAutocapture<
P extends {
onPress?: (e: GestureResponderEvent) => void;
onLongPress?: (e: GestureResponderEvent) => void;
}
>(TouchableComponent: React.ComponentType<P>): React.ComponentType<P>;

/**
* Returns an HOC of a component that tracks presses, i.e. calls to `onPress` or
* `onLongPress`
*
* @param PressableComponent the component to autocapture presses for
*/
export function withHeapPressableAutocapture<
P extends {
onPress?: (e: GestureResponderEvent) => void;
onLongPress?: (e: GestureResponderEvent) => void;
}
>(PressableComponent: React.ComponentType<P>): React.ComponentType<P>;

/**
* Returns an HOC of a navigation container that autotracks pageviews on navigation change.
*
Expand Down
2 changes: 2 additions & 0 deletions js/Heap.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
autotrackPress,
withHeapTouchableAutocapture,
} from './autotrack/touchables';
import { withHeapAutocapture } from './autotrack/component';
import { withHeapPressableAutocapture } from './autotrack/pressable';
import { autotrackSwitchChange } from './autotrack/switches';
import { autotrackScrollView } from './autotrack/scrollViews';
Expand Down Expand Up @@ -91,6 +92,7 @@ export default {
autotrackPress: bailOnError(autotrackPress(autocaptureTrack)),
withHeapTouchableAutocapture: withHeapTouchableAutocapture(autocaptureTrack),
withHeapPressableAutocapture: withHeapPressableAutocapture(autocaptureTrack),
withHeapAutocapture: withHeapAutocapture(autocaptureTrack),
autotrackSwitchChange: bailOnError(autotrackSwitchChange(autocaptureTrack)),
autocaptureScrollView: bailOnError(autotrackScrollView(autocaptureTrack)),
autocaptureTextInput: bailOnError(
Expand Down
61 changes: 61 additions & 0 deletions js/autotrack/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import hoistNonReactStatic from 'hoist-non-react-statics';
import * as React from 'react';
import { bailOnError } from '../util/bailer';
import { getComponentDisplayName } from '../util/hocUtil';
import { getBaseComponentProps } from './common';

export const withHeapAutocapture = track => (
CapturableComponent,
propName,
eventType = 'touch'
) => {
class HeapAutocapture extends React.Component {
trackEvent() {
const autotrackProps = getBaseComponentProps(this);

if (!autotrackProps) {
// We're not capturing this interaction.
return;
}

track(eventType, autotrackProps);
}

render() {
const {
heapForwardedRef,
[propName]: defaultImplementation,
...rest
} = this.props;

const instrumentedProps = {
[propName]: e => {
bailOnError(() => this.trackEvent())();
defaultImplementation && defaultImplementation(e);
},
};

return (
<CapturableComponent
ref={heapForwardedRef}
{...instrumentedProps}
{...rest}
>
{this.props.children}
</CapturableComponent>
);
}
}

HeapAutocapture.displayName = `withHeapAutocapture(${getComponentDisplayName(
CapturableComponent
)})`;

const forwardRefHoc = React.forwardRef((props, ref) => {
return <HeapAutocapture {...props} heapForwardedRef={ref} />;
});

hoistNonReactStatic(forwardRefHoc, CapturableComponent);

return forwardRefHoc;
};