Skip to content

Commit

Permalink
Small type clean up for v4 (#471)
Browse files Browse the repository at this point in the history
* use union of metric types more broadly

* add attribution in return type

* revert Metric name

* attribution object

* update readme

* object.assign attributions

* Move isInvalidTimestamp into getNavigationEntry

* Explicitly type attribution return values

* Remove missed file

* Update README

* Apply suggestions from code review

Co-authored-by: Barry Pollard <barrypollard@google.com>

* Remove src files links in README

* Fix README error

* Add one more note to README

---------

Co-authored-by: Philip Walton <philip@philipwalton.com>
Co-authored-by: Philip Walton <philipwalton@users.noreply.github.com>
Co-authored-by: Barry Pollard <barrypollard@google.com>
  • Loading branch information
4 people authored May 3, 2024
1 parent a6bed46 commit fd8b0c3
Show file tree
Hide file tree
Showing 22 changed files with 271 additions and 361 deletions.
129 changes: 73 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ To load the "attribution" build, change any `import` statements that reference `
+ import {onLCP, onINP, onCLS} from 'web-vitals/attribution';
```

Usage for each of the imported function is identical to the standard build, but when importing from the attribution build, the [`Metric`](#metric) object will contain an additional [`attribution`](#metricwithattribution) property.
Usage for each of the imported function is identical to the standard build, but when importing from the attribution build, the [metric](#metric) objects will contain an additional [`attribution`](#attribution) property.

See [Send attribution data](#send-attribution-data) for usage examples, and the [`attribution` reference](#attribution) for details on what values are added for each metric.

Expand Down Expand Up @@ -492,6 +492,8 @@ For guidance on how to collect and use real-user data to debug performance issue

#### `Metric`

All metrics types inherit from the following base interface:

```ts
interface Metric {
/**
Expand Down Expand Up @@ -532,7 +534,7 @@ interface Metric {
* The array may also be empty if the metric value was not based on any
* entries (e.g. a CLS value of 0 given no layout shifts).
*/
entries: (PerformanceEntry | LayoutShift)[];
entries: PerformanceEntry[];

/**
* The type of navigation.
Expand All @@ -558,36 +560,61 @@ interface Metric {

Metric-specific subclasses:

- [`CLSMetric`](/src/types/cls.ts#:~:text=interface%20CLSMetric)
- [`FCPMetric`](/src/types/fcp.ts#:~:text=interface%20FCPMetric)
- [`FIDMetric`](/src/types/fid.ts#:~:text=interface%20FIDMetric)
- [`INPMetric`](/src/types/inp.ts#:~:text=interface%20INPMetric)
- [`LCPMetric`](/src/types/lcp.ts#:~:text=interface%20LCPMetric)
- [`TTFBMetric`](/src/types/ttfb.ts#:~:text=interface%20TTFBMetric)
##### `CLSMetric`

#### `MetricWithAttribution`
```ts
interface CLSMetric extends Metric {
name: 'CLS';
entries: LayoutShift[];
}
```

See the [attribution build](#attribution-build) section for details on how to use this feature.
##### `FCPMetric`

```ts
interface MetricWithAttribution extends Metric {
/**
* An object containing potentially-helpful debugging information that
* can be sent along with the metric value for the current page visit in
* order to help identify issues happening to real-users in the field.
*/
attribution: {[key: string]: unknown};
interface FCPMetric extends Metric {
name: 'FCP';
entries: PerformancePaintTiming[];
}
```

Metric-specific subclasses:
##### `FIDMetric`

- [`CLSMetricWithAttribution`](/src/types/cls.ts#:~:text=interface%20CLSMetricWithAttribution)
- [`FCPMetricWithAttribution`](/src/types/fcp.ts#:~:text=interface%20FCPMetricWithAttribution)
- [`FIDMetricWithAttribution`](/src/types/fid.ts#:~:text=interface%20FIDMetricWithAttribution)
- [`INPMetricWithAttribution`](/src/types/inp.ts#:~:text=interface%20INPMetricWithAttribution)
- [`LCPMetricWithAttribution`](/src/types/lcp.ts#:~:text=interface%20LCPMetricWithAttribution)
- [`TTFBMetricWithAttribution`](/src/types/ttfb.ts#:~:text=interface%20TTFBMetricWithAttribution)
_This interface is deprecated and will be removed in next major release_

```ts
interface FIDMetric extends Metric {
name: 'FID';
entries: PerformanceEventTiming[];
}
```

##### `INPMetric`

```ts
interface INPMetric extends Metric {
name: 'INP';
entries: PerformanceEventTiming[];
}
```

##### `LCPMetric`

```ts
interface LCPMetric extends Metric {
name: 'LCP';
entries: LargestContentfulPaint[];
}
```

##### `TTFBMetric`

```ts
interface TTFBMetric extends Metric {
name: 'TTFB';
entries: PerformanceNavigationTiming[];
}
```

#### `MetricRatingThresholds`

Expand All @@ -604,28 +631,11 @@ The thresholds of metric's "good", "needs improvement", and "poor" ratings.
| > [1] | "poor" |

```ts
export type MetricRatingThresholds = [number, number];
type MetricRatingThresholds = [number, number];
```

_See also [Rating Thresholds](#rating-thresholds)._

#### `ReportCallback`

```ts
interface ReportCallback {
(metric: Metric): void;
}
```

Metric-specific subclasses:

- [`CLSReportCallback`](/src/types/cls.ts#:~:text=interface%20CLSReportCallback)
- [`FCPReportCallback`](/src/types/fcp.ts#:~:text=interface%20FCPReportCallback)
- [`FIDReportCallback`](/src/types/fid.ts#:~:text=interface%20FIDReportCallback)
- [`INPReportCallback`](/src/types/inp.ts#:~:text=interface%20INPReportCallback)
- [`LCPReportCallback`](/src/types/lcp.ts#:~:text=interface%20LCPReportCallback)
- [`TTFBReportCallback`](/src/types/ttfb.ts#:~:text=interface%20TTFBReportCallback)

#### `ReportOpts`

```ts
Expand Down Expand Up @@ -667,7 +677,7 @@ type LoadState =
#### `onCLS()`

```ts
type onCLS = (callback: CLSReportCallback, opts?: ReportOpts) => void;
function onCLS(callback: (metric: CLSMetric) => void, opts?: ReportOpts): void;
```

Calculates the [CLS](https://web.dev/articles/cls) value for the current page and calls the `callback` function once the value is ready to be reported, along with all `layout-shift` performance entries that were used in the metric value calculation. The reported value is a [double](https://heycam.github.io/webidl/#idl-double) (corresponding to a [layout shift score](https://web.dev/articles/cls#layout_shift_score)).
Expand All @@ -679,17 +689,17 @@ _**Important:** CLS should be continually monitored for changes throughout the e
#### `onFCP()`

```ts
type onFCP = (callback: FCPReportCallback, opts?: ReportOpts) => void;
function onFCP(callback: (metric: FCPMetric) => void, opts?: ReportOpts): void;
```

Calculates the [FCP](https://web.dev/articles/fcp) value for the current page and calls the `callback` function once the value is ready, along with the relevant `paint` performance entry used to determine the value. The reported value is a [`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp).

#### `onFID()`

_Deprecated and will be removed in next major release_
_This function is deprecated and will be removed in next major release_

```ts
type onFID = (callback: FIDReportCallback, opts?: ReportOpts) => void;
function onFID(callback: (metric: FIDMetric) => void, opts?: ReportOpts): void;
```

Calculates the [FID](https://web.dev/articles/fid) value for the current page and calls the `callback` function once the value is ready, along with the relevant `first-input` performance entry used to determine the value. The reported value is a [`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp).
Expand All @@ -699,7 +709,7 @@ _**Important:** since FID is only reported after the user interacts with the pag
#### `onINP()`

```ts
type onINP = (callback: INPReportCallback, opts?: ReportOpts) => void;
function onINP(callback: (metric: INPMetric) => void, opts?: ReportOpts): void;
```

Calculates the [INP](https://web.dev/articles/inp) value for the current page and calls the `callback` function once the value is ready, along with the `event` performance entries reported for that interaction. The reported value is a [`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp).
Expand All @@ -713,7 +723,7 @@ _**Important:** INP should be continually monitored for changes throughout the e
#### `onLCP()`

```ts
type onLCP = (callback: LCPReportCallback, opts?: ReportOpts) => void;
function onLCP(callback: (metric: LCPMetric) => void, opts?: ReportOpts): void;
```

Calculates the [LCP](https://web.dev/articles/lcp) value for the current page and calls the `callback` function once the value is ready (along with the relevant `largest-contentful-paint` performance entry used to determine the value). The reported value is a [`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp).
Expand All @@ -723,7 +733,10 @@ If the `reportAllChanges` [configuration option](#reportopts) is set to `true`,
#### `onTTFB()`

```ts
type onTTFB = (callback: TTFBReportCallback, opts?: ReportOpts) => void;
function onTTFB(
callback: (metric: TTFBMetric) => void,
opts?: ReportOpts,
): void;
```

Calculates the [TTFB](https://web.dev/articles/ttfb) value for the current page and calls the `callback` function once the page has loaded, along with the relevant `navigation` performance entry used to determine the value. The reported value is a [`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp).
Expand Down Expand Up @@ -760,15 +773,17 @@ console.log(INPThresholds); // [ 200, 500 ]
console.log(LCPThresholds); // [ 2500, 4000 ]
```

_**Note:** It's typically not necessary (or recommended) to manually calculate metric value ratings using these thresholds. Use the [`Metric['rating']`](#metric) supplied by the [`ReportCallback`](#reportcallback) functions instead._
_**Note:** It's typically not necessary (or recommended) to manually calculate metric value ratings using these thresholds. Use the [`Metric['rating']`](#metric) instead._

### Attribution:

The following objects contain potentially-helpful debugging information that can be sent along with the metric values for the current page visit in order to help identify issues happening to real-users in the field.

When using the attribution build, these objects are found as an `attribution` property on each metric.

See the [attribution build](#attribution-build) section for details on how to use this feature.

#### CLS `attribution`:
#### `CLSAttribution`

```ts
interface CLSAttribution {
Expand Down Expand Up @@ -809,7 +824,7 @@ interface CLSAttribution {
}
```

#### FCP `attribution`:
#### `FCPAttribution`

```ts
interface FCPAttribution {
Expand Down Expand Up @@ -841,7 +856,9 @@ interface FCPAttribution {
}
```

#### FID `attribution`:
#### `FIDAttribution`

_This interface is deprecated and will be removed in next major release_

```ts
interface FIDAttribution {
Expand Down Expand Up @@ -873,7 +890,7 @@ interface FIDAttribution {
}
```

#### INP `attribution`:
#### `INPAttribution`

```ts
interface INPAttribution {
Expand Down Expand Up @@ -964,7 +981,7 @@ interface INPAttribution {
}
```

#### LCP `attribution`:
#### `LCPAttribution`

```ts
interface LCPAttribution {
Expand Down Expand Up @@ -1019,7 +1036,7 @@ interface LCPAttribution {
}
```

#### TTFB `attribution`:
#### `TTFBAttribution`

```ts
export interface TTFBAttribution {
Expand Down
33 changes: 18 additions & 15 deletions src/attribution/onCLS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ import {getLoadState} from '../lib/getLoadState.js';
import {getSelector} from '../lib/getSelector.js';
import {onCLS as unattributedOnCLS} from '../onCLS.js';
import {
CLSReportCallback,
CLSReportCallbackWithAttribution,
CLSAttribution,
CLSMetric,
CLSMetricWithAttribution,
ReportOpts,
Expand All @@ -33,26 +32,33 @@ const getLargestLayoutShiftSource = (sources: LayoutShiftAttribution[]) => {
return sources.find((s) => s.node && s.node.nodeType === 1) || sources[0];
};

const attributeCLS = (metric: CLSMetric): void => {
const attributeCLS = (metric: CLSMetric): CLSMetricWithAttribution => {
// Use an empty object if no other attribution has been set.
let attribution: CLSAttribution = {};

if (metric.entries.length) {
const largestEntry = getLargestLayoutShiftEntry(metric.entries);
if (largestEntry && largestEntry.sources && largestEntry.sources.length) {
const largestSource = getLargestLayoutShiftSource(largestEntry.sources);
if (largestSource) {
(metric as CLSMetricWithAttribution).attribution = {
attribution = {
largestShiftTarget: getSelector(largestSource.node),
largestShiftTime: largestEntry.startTime,
largestShiftValue: largestEntry.value,
largestShiftSource: largestSource,
largestShiftEntry: largestEntry,
loadState: getLoadState(largestEntry.startTime),
};
return;
}
}
}
// Set an empty object if no other attribution has been set.
(metric as CLSMetricWithAttribution).attribution = {};

// Use Object.assign to set property to keep tsc happy.
const metricWithAttribution: CLSMetricWithAttribution = Object.assign(
metric,
{attribution},
);
return metricWithAttribution;
};

/**
Expand All @@ -77,14 +83,11 @@ const attributeCLS = (metric: CLSMetric): void => {
* during the same page load._
*/
export const onCLS = (
onReport: CLSReportCallbackWithAttribution,
onReport: (metric: CLSMetricWithAttribution) => void,
opts?: ReportOpts,
) => {
unattributedOnCLS(
((metric: CLSMetricWithAttribution) => {
attributeCLS(metric);
onReport(metric);
}) as CLSReportCallback,
opts,
);
unattributedOnCLS((metric: CLSMetric) => {
const metricWithAttribution = attributeCLS(metric);
onReport(metricWithAttribution);
}, opts);
};
Loading

0 comments on commit fd8b0c3

Please sign in to comment.