Skip to content

Commit

Permalink
Add new HOC for instrumenting generic components
Browse files Browse the repository at this point in the history
  • Loading branch information
salockhart committed Sep 27, 2021
1 parent d1dd84a commit a3de057
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 0 deletions.
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
39 changes: 39 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';

import { HeapIgnore, HeapIgnoreTargetText } from './js/autotrack/heapIgnore';

Expand Down Expand Up @@ -118,6 +120,43 @@ export function resetIdentity(): void;
*/
export function getUserId(): Promise<string>;

/**
* 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): 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>;
export function getUserId(): Promise<string>;

/**
* 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 component with specific HeapIgnore properties.
*
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 @@ -90,6 +91,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
57 changes: 57 additions & 0 deletions js/autotrack/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
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) => {
class HeapAutocapture extends React.Component {
trackEvent() {
const autotrackProps = getBaseComponentProps(this);

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

track('touch', 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;
};

0 comments on commit a3de057

Please sign in to comment.