-
Notifications
You must be signed in to change notification settings - Fork 0
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
[Analytics] Adds typings for react-tracking and specific to Artsy. #1
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,41 @@ | ||
import React from "react" | ||
import track from "react-tracking" | ||
import styled from "styled-components" | ||
import Icon from "../icon" | ||
|
||
import { track as _track, TrackingInfo, trackWithoutProps, trackWithProps } from "../../utils/track" | ||
|
||
interface ShareProps extends React.HTMLProps<HTMLDivElement> { | ||
url: string | ||
title: string | ||
} | ||
|
||
/** | ||
* An example of a typed `track` function that does not have types for the props used by this component and only uses | ||
* the default global tracking-info types. | ||
* | ||
* @note This weird `import { track as _track } from "../../utils/track"` dance is only done to have this comment in the | ||
* same format as the ones below. | ||
*/ | ||
const track = _track | ||
|
||
/** | ||
* An example of a typed `track` function that does not have types for the props used by this component, but does allow | ||
* types for the tracking-info. | ||
*/ | ||
// const track = trackWithoutProps<TrackingInfo.Shareable>() | ||
|
||
/** | ||
* An example of a typed `track` function that has types for the props used by this component, but only uses the default | ||
* global tracking-info types. | ||
*/ | ||
// const track = trackWithProps<ShareProps>() | ||
|
||
/** | ||
* An example of a typed `track` function that has types for the props used by this component and specifies extended | ||
* types for tracking-info, in this case the centralized types shareable tracking-info. | ||
*/ | ||
// const track = trackWithProps<ShareProps, TrackingInfo.Shareable>() | ||
|
||
@track() | ||
class Share extends React.Component<ShareProps, null> { | ||
constructor(props) { | ||
|
@@ -16,7 +44,8 @@ class Share extends React.Component<ShareProps, null> { | |
this.trackShare = this.trackShare.bind(this) | ||
} | ||
|
||
@track({ action: "share article" }) | ||
// FIXME: This is obviously wrong, a slug is not a URL, but it’s just for illustration purposes. | ||
@track(props => ({ action: "share article", slug: props.url })) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another example that’s supposed to be cleaned up. |
||
trackShare(e) { | ||
e.preventDefault() | ||
window.open(e.currentTarget.attributes.href.value, "Share", "width = 600,height = 300") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Type definitions for react-tracking 4.2 | ||
// Project: https://github.com/NYTimes/react-tracking | ||
// Definitions by: Eloy Durán <https://github.com/alloy> | ||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped | ||
|
||
declare module "react-tracking" { | ||
import * as React from "react" | ||
|
||
export interface TrackingProp { | ||
trackEvent: ({}) => any | ||
|
||
/** | ||
* This method returns all of the contextual tracking data up until this point in the component hierarchy. | ||
*/ | ||
getTrackingData: () => {} | ||
} | ||
|
||
type Falsy = false | null | undefined | "" | ||
|
||
interface Options<T> { | ||
/** | ||
* By default, data tracking objects are pushed to `window.dataLayer[]`. This is a good default if you use Google | ||
* Tag Manager. You can override this by passing in a dispatch function as a second parameter to the tracking | ||
* decorator `{ dispatch: fn() }` on some top-level component high up in your app (typically some root-level | ||
* component that wraps your entire app). | ||
*/ | ||
dispatch?: (data: T) => any | ||
|
||
/** | ||
* To dispatch tracking data when a component mounts, you can pass in `{ dispatchOnMount: true }` as the second | ||
* parameter to `@track()`. This is useful for dispatching tracking data on "Page" components. | ||
* | ||
* If you pass in a function, the function will be called with all of the tracking data from the app's context when | ||
* the component mounts. The return value of this function will be dispatched in `componentDidMount()`. The object | ||
* returned from this function call will be merged with the context data and then dispatched. A use case for this | ||
* would be that you want to provide extra tracking data without adding it to the context. | ||
*/ | ||
dispatchOnMount?: boolean | ((contextData: T) => T) | ||
|
||
/** | ||
* When there's a need to implicitly dispatch an event with some data for every component, you can define an | ||
* `options.process` function. This function should be declared once, at some top-level component. It will get | ||
* called with each component's tracking data as the only argument. The returned object from this function will be | ||
* merged with all the tracking context data and dispatched in `componentDidMount()`. If a falsy value is returned | ||
* (`false`, `null`, `undefined`, ...), nothing will be dispatched. | ||
* | ||
* A common use case for this is to dispatch a `pageview` event for every component in the application that has a | ||
* `page` property on its `trackingData`. | ||
*/ | ||
process?: (ownTrackingData: T) => T | Falsy | ||
} | ||
|
||
export type TrackingInfo<T, P> = T | ((props: P) => T) | ||
|
||
// Duplicated from ES6 lib to remove the `void` typing, otherwise `track` can’t be used as a HOC function that passes | ||
// through a JSX component that be used wihtout casting. | ||
type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | ||
type MethodDecorator = <T>( | ||
target: Object, | ||
propertyKey: string | symbol, | ||
descriptor: TypedPropertyDescriptor<T> | ||
) => TypedPropertyDescriptor<T> | ||
type Decorator = ClassDecorator & MethodDecorator | ||
|
||
/** | ||
* This is the type of the `track` function. It’s declared as an interface so that consumers can extend the typing and | ||
* specify defaults, such as a global analytics schema for the tracking-info. | ||
* | ||
* For examples of such extensions see: https://github.com/artsy/reaction/blob/master/src/utils/track.ts | ||
*/ | ||
export interface Track<T = {}, P = {}> { | ||
(trackingInfo?: TrackingInfo<Partial<T>, P>, options?: Options<Partial<T>>): Decorator | ||
} | ||
|
||
export const track: Track | ||
export default track | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These is the ‘ambient’ declaration for the |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import { Track, track as _track } from "react-tracking" | ||
|
||
// tslint:disable-next-line:no-namespace | ||
export namespace TrackingInfo { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explain the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I got it :) |
||
/** | ||
* The global tracking-info keys in Artsy’s schema. | ||
*/ | ||
export interface Global { | ||
/** | ||
* The name of an event. | ||
*/ | ||
action: string | ||
|
||
/** | ||
* The root container component should specify this as the screen context. | ||
*/ | ||
page: string | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even though you would use either |
||
|
||
export interface Shareable extends Global { | ||
/** | ||
* The public slug for this entity. | ||
*/ | ||
slug: string | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mennenia This is an example of what the tracking-info schema could be for items that are ‘shareable’. Please update it to reflect our actual schema. |
||
} | ||
|
||
/** | ||
* A typed tracking-info and props alias of the default react-tracking `track` function. | ||
* | ||
* Use this version when you’re going to use a callback function to generate the tracking info. | ||
* | ||
* @example | ||
* | ||
* import { trackWithProps } from "src/utils/track" | ||
* | ||
* interface Props { | ||
* artist: { | ||
* slug: string | ||
* } | ||
* } | ||
* | ||
* const track = trackWithProps<Props>() | ||
* | ||
* @track() | ||
* class Artist extends React.Component<Props, null> { | ||
* render() { | ||
* return ( | ||
* <div onClick={this.handleFollow.bind(this)}> | ||
* ... | ||
* </div> | ||
* ) | ||
* } | ||
* | ||
* @track(props => ({ action: "Follow Artist", slug: props.slug })) | ||
* handleFollow() { | ||
* // ... | ||
* } | ||
* } | ||
* | ||
*/ | ||
export function trackWithProps<P, T extends TrackingInfo.Global = TrackingInfo.Global>(): Track<T, P> { | ||
return _track | ||
} | ||
|
||
/** | ||
* A typed tracking-info alias of the default react-tracking `track` function. | ||
* | ||
* Use this version when you don’t use a callback function to generate the tracking info based on props, but you do want | ||
* to extend the tracking-info schema. | ||
* | ||
* @example | ||
* | ||
* import { trackWithoutProps as track } from "src/utils/track" | ||
* | ||
* interface TrackingInfo { | ||
* slug: string | ||
* } | ||
* | ||
* const track = trackWithoutProps<TrackingInfo>() | ||
* | ||
* @track() | ||
* class Artist extends React.Component<{}, null> { | ||
* render() { | ||
* return ( | ||
* <div onClick={this.handleFollow.bind(this)}> | ||
* ... | ||
* </div> | ||
* ) | ||
* } | ||
* | ||
* @track({ action: "Follow Artist", slug: "banksy" }) | ||
* handleFollow() { | ||
* // ... | ||
* } | ||
* } | ||
*/ | ||
export function trackWithoutProps<T extends TrackingInfo.Global = TrackingInfo.Global>(): Track<T, any> { | ||
return _track | ||
} | ||
|
||
/** | ||
* A typed tracking-info alias of the default react-tracking `track` function. | ||
* | ||
* Use this version when you don’t use a callback function to generate the tracking info based on props. You can also | ||
* use this if you don’t want to deal with props types, but you want to be cool too, right? | ||
* | ||
* @example | ||
* | ||
* import { track } from "src/utils/track" | ||
* | ||
* @track() | ||
* class Artist extends React.Component<{}, null> { | ||
* render() { | ||
* return ( | ||
* <div onClick={this.handleFollow.bind(this)}> | ||
* ... | ||
* </div> | ||
* ) | ||
* } | ||
* | ||
* @track({ action: "Follow Artist" }) | ||
* handleFollow() { | ||
* // ... | ||
* } | ||
* } | ||
*/ | ||
export const track = trackWithoutProps() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me know if the documentation and examples is clear enough. |
||
|
||
export default track |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are all just examples of the various possible permutations with out own
track
module, which augments the genericreact-tracking
types.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@craigspaeth So when you add actual types for all publishing components, please remove these 🙏