Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/ci-validate-platforms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:

- name: Install playwright dependencies and browsers
run: |
npx playwright install
npx playwright install --with-deps

- name: Run tests in all Packages
run: npm run test
2 changes: 1 addition & 1 deletion .github/workflows/ci-validate-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:

- name: Install playwright dependencies and browsers
run: |
npx playwright install
npx playwright install --with-deps

- name: Testing unit tests
run: npm run test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat(hydration): add hydrationStarted callback and update documentation",
"packageName": "@microsoft/fast-element",
"email": "863023+radium-v@users.noreply.github.com",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "test(performance-metrics): enhance performance tracking and add hydration metrics",
"packageName": "@microsoft/fast-html",
"email": "863023+radium-v@users.noreply.github.com",
"dependentChangeType": "none"
}
1 change: 1 addition & 0 deletions packages/fast-element/docs/api-report.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,7 @@ export interface HydrationControllerCallbacks<TElement extends HTMLElement = HTM
elementDidHydrate?(source: TElement): void;
elementWillHydrate?(source: TElement): void;
hydrationComplete?(): void;
hydrationStarted?(): void;
}

// @public
Expand Down
100 changes: 45 additions & 55 deletions packages/fast-element/src/components/element-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -830,12 +830,20 @@ export interface HydrationControllerCallbacks<
TElement extends HTMLElement = HTMLElement
> {
/**
* Called before hydration has started
* Called once when the first element enters the hydration pipeline.
* This is the earliest point at which we know a component has been
* async-defined with `defer-and-hydrate`, a template is pending via
* `<f-template>`, and the element has `needs-hydration`.
*/
hydrationStarted?(): void;

/**
* Called before an individual element's hydration begins
*/
elementWillHydrate?(source: TElement): void;

/**
* Called after hydration has finished
* Called after an individual element's hydration has finished
*/
elementDidHydrate?(source: TElement): void;

Expand Down Expand Up @@ -887,6 +895,11 @@ export class HydratableElementController<
*/
public static lifecycleCallbacks: HydrationControllerCallbacks = {};

/**
* Whether the hydrationStarted callback has already been invoked.
*/
private static hydrationStarted: boolean = false;

/**
* An idle callback ID used to track hydration completion
*/
Expand Down Expand Up @@ -946,7 +959,11 @@ export class HydratableElementController<

// If there are no more hydrating instances, invoke the hydrationComplete callback
if (HydratableElementController.hydratingInstances?.size === 0) {
HydratableElementController.notifyHydrationComplete();
try {
HydratableElementController.lifecycleCallbacks.hydrationComplete?.();
} catch {
// A lifecycle callback must never prevent post-hydration cleanup.
}

// Reset to the default strategy after hydration is complete
ElementController.setStrategy(ElementController);
Expand Down Expand Up @@ -986,7 +1003,23 @@ export class HydratableElementController<
return;
}

this.notifyWillHydrate();
if (!HydratableElementController.hydrationStarted) {
HydratableElementController.hydrationStarted = true;

try {
HydratableElementController.lifecycleCallbacks.hydrationStarted?.();
} catch {
// A lifecycle callback must never prevent hydration.
}
}

try {
HydratableElementController.lifecycleCallbacks.elementWillHydrate?.(
this.source
);
} catch {
// A lifecycle callback must never prevent hydration.
}

this.stage = Stages.connecting;

Expand Down Expand Up @@ -1053,11 +1086,17 @@ export class HydratableElementController<
return;
}

try {
HydratableElementController.lifecycleCallbacks.elementDidHydrate?.(
this.source
);
} catch {
// A lifecycle callback must never prevent hydration.
}

const name = this.definition.name;
const instances = HydratableElementController.hydratingInstances.get(name);

this.notifyDidHydrate();

if (instances) {
instances.delete(this.source);

Expand All @@ -1076,55 +1115,6 @@ export class HydratableElementController<
}
}

/**
* Notifies that hydration is about to start for this element.
* Safely invokes the configured elementWillHydrate callback, if any.
*/
private notifyWillHydrate(): void {
const callback =
HydratableElementController.lifecycleCallbacks.elementWillHydrate;

if (callback) {
try {
callback(this.source);
} catch {
// A lifecycle callback must never prevent hydration.
}
}
}

/**
* Notifies that hydration has finished for this element.
* Safely invokes the configured elementDidHydrate callback, if any.
*/
private notifyDidHydrate(): void {
const callback = HydratableElementController.lifecycleCallbacks.elementDidHydrate;

if (callback) {
try {
callback(this.source);
} catch {
// A lifecycle callback must never prevent hydration.
}
}
}

/**
* Notifies that all elements have completed hydration.
* Safely invokes the configured hydrationComplete callback, if any.
*/
private static notifyHydrationComplete(): void {
const callback = HydratableElementController.lifecycleCallbacks.hydrationComplete;

if (callback) {
try {
callback();
} catch {
// A lifecycle callback must never prevent post-hydration cleanup.
}
}
}

/**
* Unregisters the hydration observer when the element is disconnected.
*/
Expand Down
7 changes: 6 additions & 1 deletion packages/fast-html/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { defineConfig } from "@playwright/test";
import { defineConfig, devices } from "@playwright/test";

export default defineConfig({
testDir: ".",
testMatch: "**/*.spec.ts",
retries: 3,
projects: [
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
{ name: "firefox", use: { ...devices["Desktop Firefox"] } },
{ name: "webkit", use: { ...devices["Desktop Safari"] } },
],
webServer: {
command: "npm run test-server",
port: 5173,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
width: 200px;
height: 200px;
border: 1px solid #ccc;

dd {
display: inline-block;
}
}
Loading