Skip to content
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(animations): allow computeStyle to work on elements created in Node #35810

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 24 additions & 7 deletions packages/animations/browser/src/render/shared.ts
Expand Up @@ -248,26 +248,43 @@ export function hypenatePropsObject(object: {[key: string]: any}): {[key: string
* for that.
*/
export function computeStyle(element: HTMLElement, prop: string): string {
const gcs = window.getComputedStyle(element);
const styles = window.getComputedStyle(element);

// this is casted to any because the `CSSStyleDeclaration` type is a fixed
// set of properties and `prop` is a dynamic reference to a property within
// the `CSSStyleDeclaration` list.
let value = gcs[prop as any];
let value = getComputedValue(styles, prop as keyof CSSStyleDeclaration);

// Firefox returns empty string values for `margin` and `padding` properties
// when extracted using getComputedStyle (see similar issue here:
// https://github.com/jquery/jquery/issues/3383). In this situation
// we want to emulate the value that is returned by creating the top,
// right, bottom and left properties as individual style lookups.
if (value.length === 0 && (prop === 'margin' || prop === 'padding')) {
const t = getComputedValue(styles, (prop + 'Top') as 'marginTop' | 'paddingTop');
const r = getComputedValue(styles, (prop + 'Right') as 'marginRight' | 'paddingRight');
const b = getComputedValue(styles, (prop + 'Bottom') as 'marginBottom' | 'paddingBottom');
const l = getComputedValue(styles, (prop + 'Left') as 'marginLeft' | 'paddingLeft');

// reconstruct the padding/margin value as `top right bottom left`
const propTop = (prop + 'Top') as 'marginTop' | 'paddingTop';
const propRight = (prop + 'Right') as 'marginRight' | 'paddingRight';
const propBottom = (prop + 'Bottom') as 'marginBottom' | 'paddingBottom';
const propLeft = (prop + 'Left') as 'marginLeft' | 'paddingLeft';
value = `${gcs[propTop]} ${gcs[propRight]} ${gcs[propBottom]} ${gcs[propLeft]}`;
// we `trim()` the value because if all of the values above are
// empty string values then we would like the return value to
// also be an empty string.
value = `${t} ${r} ${b} ${l}`.trim();
}

return value;
}

/**
* Reads and returns the provided property style from the provided styles collection.
*
* This function is useful because it will return an empty string in the
* event that the value obtained from the styles collection is a non-string
* value (which is usually the case if the `styles` object is mocked out).
*/
function getComputedValue<K extends keyof CSSStyleDeclaration>(
styles: CSSStyleDeclaration, prop: K): string {
const value = styles[prop];
return typeof value === 'string' ? value : '';
}
9 changes: 9 additions & 0 deletions packages/animations/browser/test/render/shared_spec.ts
Expand Up @@ -11,6 +11,15 @@ describe('shared animations code', () => {
if (isNode) return;

describe('computeStyle', () => {
it('should return an empty string if the inner computed style value is not a string', () => {
const gcsSpy = spyOn(window, 'getComputedStyle').and.returnValue(() => null);
const elementLike = buildActualElement();
expect(computeStyle(elementLike, 'width')).toEqual('');
expect(computeStyle(elementLike, 'padding')).toEqual('');
expect(computeStyle(elementLike, 'margin')).toEqual('');
gcsSpy.and.callThrough();
});

it('should compute the margin style into the form top,right,bottom,left', () => {
const div = buildActualElement();

Expand Down