Skip to content

Commit

Permalink
fix(hydrate): partially revert #5838
Browse files Browse the repository at this point in the history
  • Loading branch information
christian-bromann committed Jul 1, 2024
1 parent de88943 commit 1e2793e
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 7 deletions.
12 changes: 5 additions & 7 deletions src/runtime/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,10 @@ export const registerStyle = (scopeId: string, cssText: string, allowCS: boolean
* @returns the scope ID for the component of interest
*/
export const addStyle = (
styleContainerNode: Element | Document | ShadowRoot,
styleContainerNode: any,
cmpMeta: d.ComponentRuntimeMeta,
mode?: string,
) => {
const styleContainerDocument = styleContainerNode as Document;
const styleContainerShadowRoot = styleContainerNode as ShadowRoot;
const scopeId = getScopeId(cmpMeta, mode);
const style = styles.get(scopeId);

Expand All @@ -66,7 +64,7 @@ export const addStyle = (

if (style) {
if (typeof style === 'string') {
styleContainerNode = styleContainerDocument.head || (styleContainerNode as HTMLElement);
styleContainerNode = styleContainerNode.head || (styleContainerNode as HTMLElement);
let appliedStyles = rootAppliedStyles.get(styleContainerNode);
let styleElm;
if (!appliedStyles) {
Expand All @@ -75,7 +73,7 @@ export const addStyle = (
if (!appliedStyles.has(scopeId)) {
if (
BUILD.hydrateClientSide &&
styleContainerShadowRoot.host &&
styleContainerNode.host &&
(styleElm = styleContainerNode.querySelector(`[${HYDRATED_STYLE_ID}="${scopeId}"]`))
) {
// This is only happening on native shadow-dom, do not needs CSS var shim
Expand Down Expand Up @@ -106,8 +104,8 @@ export const addStyle = (
appliedStyles.add(scopeId);
}
}
} else if (BUILD.constructableCSS && !styleContainerDocument.adoptedStyleSheets.includes(style)) {
styleContainerDocument.adoptedStyleSheets = [...styleContainerDocument.adoptedStyleSheets, style];
} else if (BUILD.constructableCSS && !styleContainerNode.adoptedStyleSheets.includes(style)) {
styleContainerNode.adoptedStyleSheets = [...styleContainerNode.adoptedStyleSheets, style];
}
}
return scopeId;
Expand Down
60 changes: 60 additions & 0 deletions test/end-to-end/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ export namespace Components {
*/
"mode"?: any;
}
interface ScopedCarDetail {
"car": CarData;
}
/**
* Component that helps display a list of cars
*/
interface ScopedCarList {
"cars": CarData[];
"selected": CarData;
}
interface SlotCmp {
}
interface SlotCmpContainer {
Expand All @@ -130,6 +140,10 @@ export interface EventCmpCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLEventCmpElement;
}
export interface ScopedCarListCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLScopedCarListElement;
}
declare global {
interface HTMLAnotherCarDetailElement extends Components.AnotherCarDetail, HTMLStencilElement {
}
Expand Down Expand Up @@ -322,6 +336,32 @@ declare global {
prototype: HTMLPropCmpElement;
new (): HTMLPropCmpElement;
};
interface HTMLScopedCarDetailElement extends Components.ScopedCarDetail, HTMLStencilElement {
}
var HTMLScopedCarDetailElement: {
prototype: HTMLScopedCarDetailElement;
new (): HTMLScopedCarDetailElement;
};
interface HTMLScopedCarListElementEventMap {
"carSelected": CarData;
}
/**
* Component that helps display a list of cars
*/
interface HTMLScopedCarListElement extends Components.ScopedCarList, HTMLStencilElement {
addEventListener<K extends keyof HTMLScopedCarListElementEventMap>(type: K, listener: (this: HTMLScopedCarListElement, ev: ScopedCarListCustomEvent<HTMLScopedCarListElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof HTMLScopedCarListElementEventMap>(type: K, listener: (this: HTMLScopedCarListElement, ev: ScopedCarListCustomEvent<HTMLScopedCarListElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}
var HTMLScopedCarListElement: {
prototype: HTMLScopedCarListElement;
new (): HTMLScopedCarListElement;
};
interface HTMLSlotCmpElement extends Components.SlotCmp, HTMLStencilElement {
}
var HTMLSlotCmpElement: {
Expand Down Expand Up @@ -372,6 +412,8 @@ declare global {
"path-alias-cmp": HTMLPathAliasCmpElement;
"prerender-cmp": HTMLPrerenderCmpElement;
"prop-cmp": HTMLPropCmpElement;
"scoped-car-detail": HTMLScopedCarDetailElement;
"scoped-car-list": HTMLScopedCarListElement;
"slot-cmp": HTMLSlotCmpElement;
"slot-cmp-container": HTMLSlotCmpContainerElement;
"slot-parent-cmp": HTMLSlotParentCmpElement;
Expand Down Expand Up @@ -455,6 +497,17 @@ declare namespace LocalJSX {
*/
"mode"?: any;
}
interface ScopedCarDetail {
"car"?: CarData;
}
/**
* Component that helps display a list of cars
*/
interface ScopedCarList {
"cars"?: CarData[];
"onCarSelected"?: (event: ScopedCarListCustomEvent<CarData>) => void;
"selected"?: CarData;
}
interface SlotCmp {
}
interface SlotCmpContainer {
Expand Down Expand Up @@ -490,6 +543,8 @@ declare namespace LocalJSX {
"path-alias-cmp": PathAliasCmp;
"prerender-cmp": PrerenderCmp;
"prop-cmp": PropCmp;
"scoped-car-detail": ScopedCarDetail;
"scoped-car-list": ScopedCarList;
"slot-cmp": SlotCmp;
"slot-cmp-container": SlotCmpContainer;
"slot-parent-cmp": SlotParentCmp;
Expand Down Expand Up @@ -531,6 +586,11 @@ declare module "@stencil/core" {
"path-alias-cmp": LocalJSX.PathAliasCmp & JSXBase.HTMLAttributes<HTMLPathAliasCmpElement>;
"prerender-cmp": LocalJSX.PrerenderCmp & JSXBase.HTMLAttributes<HTMLPrerenderCmpElement>;
"prop-cmp": LocalJSX.PropCmp & JSXBase.HTMLAttributes<HTMLPropCmpElement>;
"scoped-car-detail": LocalJSX.ScopedCarDetail & JSXBase.HTMLAttributes<HTMLScopedCarDetailElement>;
/**
* Component that helps display a list of cars
*/
"scoped-car-list": LocalJSX.ScopedCarList & JSXBase.HTMLAttributes<HTMLScopedCarListElement>;
"slot-cmp": LocalJSX.SlotCmp & JSXBase.HTMLAttributes<HTMLSlotCmpElement>;
"slot-cmp-container": LocalJSX.SlotCmpContainer & JSXBase.HTMLAttributes<HTMLSlotCmpContainerElement>;
"slot-parent-cmp": LocalJSX.SlotParentCmp & JSXBase.HTMLAttributes<HTMLSlotParentCmpElement>;
Expand Down
46 changes: 46 additions & 0 deletions test/end-to-end/src/declarative-shadow-dom/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,52 @@
<!-- Auto Generated Below -->


## Overview

Component that helps display a list of cars

## Properties

| Property | Attribute | Description | Type | Default |
| ---------- | --------- | ----------- | ----------- | ----------- |
| `cars` | -- | | `CarData[]` | `undefined` |
| `selected` | -- | | `CarData` | `undefined` |


## Events

| Event | Description | Type |
| ------------- | ----------- | ---------------------- |
| `carSelected` | | `CustomEvent<CarData>` |


## Slots

| Slot | Description |
| ---------- | -------------------------------- |
| `"header"` | The slot for the header content. |


## Shadow Parts

| Part | Description |
| ------- | ------------------------------------------- |
| `"car"` | The shadow part to target to style the car. |


## Dependencies

### Depends on

- [another-car-detail](.)

### Graph
```mermaid
graph TD;
scoped-car-list --> another-car-detail
style scoped-car-list fill:#f9f,stroke:#333,stroke-width:4px
```

----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
24 changes: 24 additions & 0 deletions test/end-to-end/src/declarative-shadow-dom/scoped-car-detail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Component, h, Prop } from '@stencil/core';

import { CarData } from '../car-list/car-data';

@Component({
tag: 'scoped-car-detail',
styleUrl: 'another-car-detail.css',
scoped: true,
})
export class CarDetail {
@Prop() car: CarData;

render() {
if (!this.car) {
return null;
}

return (
<section>
{this.car.year} {this.car.make} {this.car.model}
</section>
);
}
}
46 changes: 46 additions & 0 deletions test/end-to-end/src/declarative-shadow-dom/scoped-car-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Component, Event, EventEmitter, h, Prop } from '@stencil/core';

import { CarData } from '../car-list/car-data';

/**
* Component that helps display a list of cars
* @slot header - The slot for the header content.
* @part car - The shadow part to target to style the car.
*/
@Component({
tag: 'scoped-car-list',
styleUrl: 'another-car-list.css',
scoped: true,
})
export class CarList {
@Prop() cars: CarData[];
@Prop({ mutable: true }) selected: CarData;
@Event() carSelected: EventEmitter<CarData>;

componentWillLoad() {
return new Promise((resolve) => setTimeout(resolve, 20));
}

selectCar(car: CarData) {
this.selected = car;
this.carSelected.emit(car);
}

render() {
if (!Array.isArray(this.cars)) {
return null;
}

return (
<ul>
{this.cars.map((car) => {
return (
<li class={car === this.selected ? 'selected' : ''}>
<another-car-detail car={car}></another-car-detail>
</li>
);
})}
</ul>
);
}
}
39 changes: 39 additions & 0 deletions test/end-to-end/src/miscellaneous/renderToString.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { renderToString } from '../../hydrate';
import { CarData } from '../car-list/car-data';

const vento = new CarData('VW', 'Vento', 2024);
const beetle = new CarData('VW', 'Beetle', 2023);

describe('renderToString', () => {
it('allows to hydrate whole HTML page', async () => {
const { html } = await renderToString(
`<html>
<head>
<link rel="stylesheet" href="whatever.css" >
</head>
<body>
<div class="__next">
<main>
<car-list cars=${JSON.stringify([vento, beetle])}></car-list>
</main>
</div>
<script type="module">
import { defineCustomElements } from "./static/loader/index.js";
defineCustomElements().catch(console.error);
</script>
</body>
</html>`,
{ fullDocument: true, serializeShadowRoot: false },
);
/**
* starts with a DocType and HTML tag
*/
expect(html.startsWith('<!doctype html><html ')).toBeTruthy();
/**
* renders hydration styles and custom link tag within the head tag
*/
expect(html).toContain('selected.sc-car-list{font-weight:bold;background:rgb(255, 255, 210)}</style><link rel="stylesheet" href="whatever.css"> </head> <body>')
});
});

0 comments on commit 1e2793e

Please sign in to comment.