-
Notifications
You must be signed in to change notification settings - Fork 407
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
fix(ssr): global attribute disparities #5247
Conversation
...xtures/attribute-global-html/as-component-prop/undeclared/modules/x/component/component.html
Show resolved
Hide resolved
...c/__tests__/fixtures/attribute-global-html/as-component-prop/without-@api-values/config.json
Show resolved
Hide resolved
"entry": "x/component" | ||
"entry": "x/component", | ||
"ssrFiles": { | ||
"error": "error-ssr.txt", |
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.
V1/V2 deviate, where V2 parent overrides the child value (as does CSR). V1 is incorrect.
packages/@lwc/engine-server/src/__tests__/fixtures/attribute-global-html/draggable/config.json
Show resolved
Hide resolved
packages/@lwc/engine-server/src/__tests__/fixtures/attribute-global-html/hidden/config.json
Show resolved
Hide resolved
packages/@lwc/engine-server/src/__tests__/fixtures/attribute-global-html/spellcheck/config.json
Show resolved
Hide resolved
"entry": "x/component", | ||
"ssrFiles": { | ||
"error": "error-ssr.txt", | ||
"expected": "expected-ssr.html" |
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.
V1/V2 deviate, where V2 matches CSR
...ixtures/known-boolean-attributes/default-def-html-attributes/static-on-component/config.json
Show resolved
Hide resolved
@@ -92,13 +87,12 @@ export class LightningElement implements PropsAvailableAtConstruction { | |||
// Avoid setting the following types of properties that should not be set: | |||
// - Properties that are not public. | |||
// - Properties that are not global. | |||
// - Properties that are global but are internally overridden. |
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.
Does not seem correct. In CSR, child definitions do not override parent. We had tests for this, added more.
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.
If we had fixture tests for this, and it wasn't caught, we should add some karma tests to get the real browser behavior.
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.
yeah i'm adding some karma for these
/nucleus ignore --reason "Unrelated __lwc failure in webruntime" |
@@ -92,13 +87,12 @@ export class LightningElement implements PropsAvailableAtConstruction { | |||
// Avoid setting the following types of properties that should not be set: | |||
// - Properties that are not public. | |||
// - Properties that are not global. | |||
// - Properties that are global but are internally overridden. |
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.
If we had fixture tests for this, and it wasn't caught, we should add some karma tests to get the real browser behavior.
// This follows the historical behavior in api.ts: | ||
// https://github.com/salesforce/lwc/blob/f34a347/packages/%40lwc/engine-core/src/framework/api.ts#L193-L211 | ||
const shouldNormalize = parseInt(literalValue) > 0; | ||
literalValue = shouldNormalize ? '0' : literalValue; | ||
} | ||
return b.property('init', key, b.literal(literalValue)); | ||
} else if (value.type === 'Literal' && typeof value.value === 'boolean') { |
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.
What happens if tabindex
is set to a boolean value? Can it be?
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.
Yes in the case of <div tabindex>
. I don't know how that would be treated in the browser. Good thing to check.
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.
Yup, per the strange CSR logic, booleans are preserved, so nothing to do for this case here.
// Global HTML "tabindex" attribute is specially massaged into a stringified number | ||
// This follows the historical behavior in api.ts: | ||
// https://github.com/salesforce/lwc/blob/f34a347/packages/%40lwc/engine-core/src/framework/api.ts#L193-L211 | ||
const shouldNormalize = parseInt(literalValue) > 0; | ||
literalValue = shouldNormalize ? '0' : literalValue; |
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.
// Global HTML "tabindex" attribute is specially massaged into a stringified number | |
// This follows the historical behavior in api.ts: | |
// https://github.com/salesforce/lwc/blob/f34a347/packages/%40lwc/engine-core/src/framework/api.ts#L193-L211 | |
const shouldNormalize = parseInt(literalValue) > 0; | |
literalValue = shouldNormalize ? '0' : literalValue; | |
literalValue = normalizeTabIndex(literalValue); |
If we've got the util, use it!
* This follows the historical behavior in api.ts: | ||
* https://github.com/salesforce/lwc/blob/f34a347/packages/%40lwc/engine-core/src/framework/api.ts#L193-L211 | ||
*/ | ||
export function normalizeTabIndex(value: any): any { |
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.
Add some unit tests for this!
* This follows the historical behavior in api.ts: | ||
* https://github.com/salesforce/lwc/blob/f34a347/packages/%40lwc/engine-core/src/framework/api.ts#L193-L211 | ||
*/ | ||
export function normalizeTabIndex(value: any): any { |
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.
export function normalizeTabIndex(value: any): any { | |
export function normalizeTabIndex(value: number): string | number { |
The original function was more broadly typed than necessary, since this is new we can be more restrictive.
Also, it's weird that this returns either the unmodified number or the string '0'
. We should either always return a number or always return a string. (It's diverging from the original implementation, but not in a meaningful way.)
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.
Yes this is weird behavior. There's probably an actual reason for it though - I wouldn't necessarily assume that the divergence is unmeaningful.
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.
In the end, the attribute is going to end up a string attribute / numeric prop. This particular function is just an intermediate step in that. This particular function copies the business logic from engine-core
, but the rest of the implementation (outside the function) is different, so I don't think it makes much of a difference whether the final cast to string/number happens in or out of the function.
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.
export function normalizeTabIndex(value: any): any { | |
export function normalizeTabIndex<T>(value: T): T | string { |
If you want to allow any type.
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 tried generic but it added issues with the comparison. I wanted to keep it exactly as the original to avoid any discrepancies
...erver/src/__tests__/fixtures/attribute-global-html/tabindex/modules/x/component/component.js
Show resolved
Hide resolved
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.
Looks like there's a tiny bit more polish needed, but otherwise looks fantastic.
packages/@lwc/engine-server/src/__tests__/fixtures/attribute-global-html/hidden/config.json
Show resolved
Hide resolved
@@ -0,0 +1,16 @@ | |||
/* | |||
* Copyright (c) 2020, salesforce.com, inc. |
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.
* Copyright (c) 2020, salesforce.com, inc. | |
* Copyright (c) 2025, salesforce.com, inc. |
* This follows the historical behavior in api.ts: | ||
* https://github.com/salesforce/lwc/blob/f34a347/packages/%40lwc/engine-core/src/framework/api.ts#L193-L211 | ||
*/ | ||
export function normalizeTabIndex(value: any): any { |
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.
Yes this is weird behavior. There's probably an actual reason for it though - I wouldn't necessarily assume that the divergence is unmeaningful.
@@ -8,8 +8,5 @@ | |||
// We should slowly drive down these test failures or at least document where we expect the failures | |||
// TODO [#4815]: enable all SSR v2 tests | |||
export const expectedFailures = new Set([ | |||
'attribute-global-html/as-component-prop/undeclared/config.json', | |||
'attribute-global-html/as-component-prop/without-@api/config.json', | |||
'known-boolean-attributes/default-def-html-attributes/static-on-component/config.json', |
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.
🎉🎉🎉🎉🎉
// This follows the historical behavior in api.ts: | ||
// https://github.com/salesforce/lwc/blob/f34a347/packages/%40lwc/engine-core/src/framework/api.ts#L193-L211 | ||
const shouldNormalize = parseInt(literalValue) > 0; | ||
literalValue = shouldNormalize ? '0' : literalValue; | ||
} | ||
return b.property('init', key, b.literal(literalValue)); | ||
} else if (value.type === 'Literal' && typeof value.value === 'boolean') { |
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.
Yes in the case of <div tabindex>
. I don't know how that would be treated in the browser. Good thing to check.
if (name === 'class') { | ||
cxt.import('normalizeClass'); | ||
propValue = b.callExpression(b.identifier('normalizeClass'), [propValue]); | ||
} else if (name.toLowerCase() === 'tabindex') { |
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.
nit: there is more than one name.toLowerCase()
showing up in this function; probably could define nameLower
variable.
/nucleus ignore --reason "Unrelated __lwc failure in webruntime" |
I added hydration coverage for global property precedence. As SSR V1 renders the wrong values, it causes hydration errors. I added a check here and an expectation for those failures. Here is the work item to review this and change the pattern if needed: W-17939029 |
Details
Does this pull request introduce a breaking change?
Does this pull request introduce an observable change?
GUS work item
W-17572462