-
Notifications
You must be signed in to change notification settings - Fork 84
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
Use MWC for search input #1422
base: import-page
Are you sure you want to change the base?
Use MWC for search input #1422
Changes from all commits
b1c39ae
c729f4e
53d3c31
a538e8d
e0f0946
a8d80b7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,8 +11,105 @@ import {html} from 'lit'; | |
import {Readable} from 'stream'; | ||
import Router from '@koa/router'; | ||
|
||
import {LitElementRenderer} from '@lit-labs/ssr/lib/lit-element-renderer.js'; | ||
import {ElementRenderer} from '@lit-labs/ssr/lib/element-renderer.js'; | ||
|
||
import '@webcomponents/internal-site-client/lib/pages/catalog/wco-catalog-page.js'; | ||
|
||
type Interface<T> = { | ||
[P in keyof T]: T[P]; | ||
}; | ||
|
||
// TODO (justinfagnani): Update Lit SSR to use this type for | ||
// ElementRendererConstructor | ||
export type ElementRendererConstructor = (new ( | ||
tagName: string | ||
) => Interface<ElementRenderer>) & | ||
typeof ElementRenderer; | ||
|
||
// Excludes the given tag names from being handled by the given renderer. | ||
// Returns a subclass of the renderer that returns `false` for matches() | ||
// for any element in the list of tag names. | ||
const excludeElements = ( | ||
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. cc @kevinpschaaf @augustjk we might want this utility in lit-ssr 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. Could we move this SSR utility stuff to another module? |
||
renderer: ElementRendererConstructor, | ||
excludedTagNames: Array<string> | ||
) => { | ||
return class ExcludeElementRenderer extends renderer { | ||
static matchesClass( | ||
ceClass: typeof HTMLElement, | ||
tagName: string, | ||
attributes: Map<string, string> | ||
) { | ||
console.log('matchesClass', tagName, !excludedTagNames.includes(tagName)); | ||
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. Remove log |
||
return excludedTagNames.includes(tagName) | ||
? false | ||
: super.matchesClass(ceClass, tagName, attributes); | ||
} | ||
}; | ||
}; | ||
|
||
const replacements = { | ||
'&': '&', | ||
'<': '<', | ||
'>': '>', | ||
'"': '"', | ||
// Note ' was not defined in the HTML4 spec, and is not supported by very | ||
// old browsers like IE8, so a codepoint entity is used instead. | ||
"'": ''', | ||
}; | ||
|
||
/** | ||
* Replaces characters which have special meaning in HTML (&<>"') with escaped | ||
* HTML entities ("&", "<", etc.). | ||
*/ | ||
const escapeHtml = (str: string) => | ||
str.replace( | ||
/[&<>"']/g, | ||
(char) => replacements[char as keyof typeof replacements] | ||
); | ||
|
||
// The built-in FallbackRenderer incorrectly causes a | ||
// shadow root to be rendered, which breaks hydration | ||
class FallbackRenderer extends ElementRenderer { | ||
static matchesClass( | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
_ceClass: typeof HTMLElement, | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
_tagName: string, | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
_attributes: Map<string, string> | ||
) { | ||
return true; | ||
} | ||
private readonly _attributes: {[name: string]: string} = {}; | ||
|
||
override setAttribute(name: string, value: string) { | ||
this._attributes[name] = value; | ||
} | ||
|
||
override *renderAttributes() { | ||
for (const [name, value] of Object.entries(this._attributes)) { | ||
if (value === '' || value === undefined || value === null) { | ||
yield ` ${name}`; | ||
} else { | ||
yield ` ${name}="${escapeHtml(value)}"`; | ||
} | ||
} | ||
} | ||
|
||
connectedCallback() { | ||
// do nothing | ||
} | ||
attributeChangedCallback() { | ||
// do nothing | ||
} | ||
*renderLight() { | ||
// do nothing | ||
} | ||
|
||
declare renderShadow: ElementRenderer['renderShadow']; | ||
} | ||
|
||
export const handleCatalogRoute = async ( | ||
context: ParameterizedContext< | ||
DefaultState, | ||
|
@@ -25,11 +122,19 @@ export const handleCatalogRoute = async ( | |
globalThis.location = new URL(context.URL.href) as unknown as Location; | ||
|
||
context.body = Readable.from( | ||
renderPage({ | ||
title: `Web Components Catalog`, | ||
initScript: '/js/catalog/boot.js', | ||
content: html`<wco-catalog-page></wco-catalog-page>`, | ||
}) | ||
renderPage( | ||
{ | ||
title: `Web Components Catalog`, | ||
initScript: '/js/catalog/boot.js', | ||
content: html`<wco-catalog-page></wco-catalog-page>`, | ||
}, | ||
{ | ||
elementRenderers: [ | ||
excludeElements(LitElementRenderer, ['md-outlined-text-field']), | ||
FallbackRenderer, | ||
], | ||
} | ||
) | ||
); | ||
context.type = 'html'; | ||
context.status = 200; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,4 +34,13 @@ test('Finds a button', async () => { | |
assert.ok(result.elements.length > 2); | ||
}); | ||
|
||
test('Search page SSRs', async () => { | ||
const response = await request('/catalog'); | ||
assert.equal(response.status, 200); | ||
const result = await response.text(); | ||
|
||
// If the page SSR's, we'll have declarative shadow roots in it. | ||
assert.match(result, '<template shadowroot="open">'); | ||
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 we do more in the test? Like test some functionality? |
||
}); | ||
|
||
test.run(); |
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.
I think we need
=
here, because we could get a breaking change at any time while we're onpre
, right?