Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6d0decd
Adds new prop: className.
May 9, 2018
94ea1cd
Removes unnecessary styles.
May 9, 2018
0db37ed
Refactoring some styles.
May 10, 2018
762e3c5
A lot of refactor.
May 12, 2018
1c409ba
Change new prop name for better semantics.
May 15, 2018
f2d84d5
Apply new prop to application in detailsList example.
May 15, 2018
053648b
Use the new prop in TilesList.
May 15, 2018
567983c
More REFACTOR.
May 15, 2018
8c375a6
More examples...
May 16, 2018
3b2d554
Enums refactor and documentation.
May 16, 2018
ce8d116
Overflow text fix in LoadData example.
May 16, 2018
8689193
Moves deprecated props down.
May 16, 2018
df48bbb
Brings logic for RTL animation.
May 16, 2018
a536e6c
Introduces @customizable decorator to enable custom theming of the co…
May 16, 2018
22a23e0
Sets an example for getStyles prop.
May 16, 2018
c2af0ab
Adds more documentation to Shimmer Page.
May 17, 2018
e0831cf
Sets up a flag to remove the shimmerWrapper from the flow as soon as …
May 21, 2018
9607b8f
Adds rounded corners to shimmer lines.
May 21, 2018
61356d3
More refactor :)
May 21, 2018
a063dd2
Clears a timeout for application example.
May 21, 2018
4a261d1
Adds accessibility capabilities for screenreaders.
May 21, 2018
dff7fd9
Refactors some highContrast styling.
May 22, 2018
074a924
Modularize ShimmerElementsGroup
May 22, 2018
6fcd497
Customize ShimmerElementsGroup.
May 22, 2018
c12bad1
Refactor mergeStyles pattern in ShimmerElementsGroup
May 22, 2018
7e68d54
Convert subelements to the new pattern of mergeStyles.
May 22, 2018
535bd69
Removes some styles.
May 22, 2018
ed177be
Fixes support for various browsers.
May 23, 2018
8ab9d75
Fill color for line corners in high contrast mode.
May 23, 2018
d8ae65b
Rush change log.
May 23, 2018
f2a6c76
Fix tslint warnings.
May 23, 2018
84fe6c8
Adds a default prop in ShimmerElementsGroup
May 24, 2018
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@uifabric/experiments",
"comment": "Shimmer: Refactors and enhances Shimmer with more features.",
"type": "minor"
}
],
"packageName": "@uifabric/experiments",
"email": "v-vibr@microsoft.com"
}
211 changes: 93 additions & 118 deletions packages/experiments/src/components/Shimmer/Shimmer.base.tsx
Original file line number Diff line number Diff line change
@@ -1,161 +1,136 @@
import * as React from 'react';
import { BaseComponent, classNamesFunction } from '../../Utilities';
import { DefaultPalette, IStyleSet } from '../../Styling';
import {
BaseComponent,
classNamesFunction,
customizable,
DelayedRender
} from '../../Utilities';
import {
IShimmerProps,
IShimmerStyleProps,
IShimmerStyles,
ShimmerElementType,
ICircle,
ILine,
IGap,
ShimmerElementVerticalAlign,
IShimmerElement,
} from './Shimmer.types';
import { ShimmerLine } from './ShimmerLine/ShimmerLine';
import { ShimmerGap } from './ShimmerGap/ShimmerGap';
import { ShimmerCircle } from './ShimmerCircle/ShimmerCircle';
import { ShimmerElementsGroup } from './ShimmerElementsGroup/ShimmerElementsGroup';

export interface IShimmerState {
/**
* Flag for knowing when to remove the shimmerWrapper from the DOM.
*/
contentLoaded?: boolean;
}

const LINE_DEFAULT_HEIGHT = 16;
const GAP_DEFAULT_HEIGHT = 16;
const CIRCLE_DEFAULT_HEIGHT = 24;
const TRANSITION_ANIMATION_INTERVAL = 200; /* ms */

const getClassNames = classNamesFunction<IShimmerStyleProps, IShimmerStyles>();

export class ShimmerBase extends BaseComponent<IShimmerProps, {}> {
@customizable('Shimmer', ['theme'])
export class ShimmerBase extends BaseComponent<IShimmerProps, IShimmerState> {
public static defaultProps: IShimmerProps = {
isDataLoaded: false,
isBaseStyle: false
};

private _classNames: { [key in keyof IShimmerStyles]: string };
private _lastTimeoutId: number | undefined;

constructor(props: IShimmerProps) {
super(props);

this.state = {
contentLoaded: props.isDataLoaded
};

this._warnDeprecations({
'isBaseStyle': 'customElementsGroup',
'width': 'widthInPercentage or widthInPixel',
'lineElements': 'shimmerElements'
});

this._warnMutuallyExclusive({
'lineElements': 'shimmerElements',
'customElementsGroup': 'lineElements'
});
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to support deprecated props in an experimental component. Of course anything using it will have to be changed and tested.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JasonGore This experimental component is used in odsp-common repo in items-view and odsp-shared-react packages. So removing the deprecated props will cause a break on their side. The intention is to merge this change and right after I will migrate this component to OUFR without the deprecated props. Next step will be to go and change in odsp-common the imports from experiments to office-ui-fabric-react and use the correct props. Only when all this happens I will be able to remove the deprecated props in experiments package without breaking the contract with Shimmer API.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks for the explanation

}

public componentWillReceiveProps(nextProps: IShimmerProps): void {
const { isDataLoaded } = nextProps;

if (this._lastTimeoutId !== undefined) {
this._async.clearTimeout(this._lastTimeoutId);
this._lastTimeoutId = undefined;
}
if (isDataLoaded) {
this._lastTimeoutId = this._async.setTimeout(() => {
this.setState({
contentLoaded: isDataLoaded
});
this._lastTimeoutId = undefined;
}, TRANSITION_ANIMATION_INTERVAL);
} else {
this.setState({
contentLoaded: isDataLoaded
});
}
}

public render(): JSX.Element {
const {
getStyles,
Copy link
Copy Markdown
Contributor

@jordandrako jordandrako May 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved, but the getStyles prop name is being replaced with styles in 6.0. Depending on when you want this merged, before or after 6.0 is merged to master, you may need to replace getStyles in the props with styles. The function getStyles within the .styles.ts file can remain getStyles.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See this commit for details: f226fb4

width,
lineElements,
shimmerElements,
children,
isDataLoaded,
isBaseStyle,
widthInPercentage,
widthInPixel
widthInPixel,
className,
customElementsGroup,
theme,
ariaLabel
} = this.props;

const rowHeight: number | undefined = lineElements ? findMaxElementHeight(lineElements) : undefined;
const { contentLoaded } = this.state;

// lineElements is a deprecated prop so need to check which one was used.
const elements: IShimmerElement[] | undefined = shimmerElements || lineElements;

this._classNames = getClassNames(getStyles!, {
width, rowHeight, isDataLoaded, isBaseStyle, widthInPercentage, widthInPixel
theme: theme!,
width,
isDataLoaded,
widthInPercentage,
widthInPixel,
className,
transitionAnimationInterval: TRANSITION_ANIMATION_INTERVAL
});

const renderedElements: React.ReactNode = getRenderedElements(lineElements, rowHeight);

return (
<div className={ this._classNames.root }>
<div className={ this._classNames.shimmerWrapper }>
{ !!isBaseStyle ? children : renderedElements }
</div>

{ !!isDataLoaded &&
{ !contentLoaded &&
<div className={ this._classNames.shimmerWrapper }>
{ isBaseStyle ? children : // isBaseStyle prop is deprecated and this check needs to be removed in the future
customElementsGroup ? customElementsGroup :
<ShimmerElementsGroup
shimmerElements={ elements }
/>
}
</div>
}
{ !isBaseStyle && children && // isBaseStyle prop is deprecated and needs to be removed in the future
<div className={ this._classNames.dataWrapper }>
{ !!children ? children : null }
{ children }
</div>
}
{ ariaLabel && !isDataLoaded &&
<div role='status' aria-live='polite'>
<DelayedRender>
<div className={ this._classNames.screenReaderText }>{ ariaLabel }</div>
</DelayedRender>
</div>
}
</div>
);
}
}

export function getRenderedElements(lineElements?: Array<ICircle | IGap | ILine>, rowHeight?: number): React.ReactNode {
const renderedElements: React.ReactNode = lineElements ?
lineElements.map((elem: ICircle | ILine | IGap, index: number): JSX.Element => {
switch (elem.type) {
case ShimmerElementType.circle:
return (
<ShimmerCircle
key={ index }
{ ...elem }
borderStyle={ getBorderStyles(elem, rowHeight) }
/>
);
case ShimmerElementType.gap:
return (
<ShimmerGap
key={ index }
{ ...elem }
borderStyle={ getBorderStyles(elem, rowHeight) }
/>
);
case ShimmerElementType.line:
return (
<ShimmerLine
key={ index }
{ ...elem }
borderStyle={ getBorderStyles(elem, rowHeight) }
/>
);
}
}) : (
<ShimmerLine
height={ LINE_DEFAULT_HEIGHT }
/>
);

return renderedElements;
}

export function getBorderStyles(elem: ICircle | IGap | ILine, rowHeight?: number): IStyleSet | undefined {
const elemHeight: number | undefined = elem.height;

const dif: number = rowHeight && elemHeight ? rowHeight - elemHeight : 0;

let borderStyle: IStyleSet | undefined;

if (!elem.verticalAlign || elem.verticalAlign === ShimmerElementVerticalAlign.center) {
borderStyle = {
borderBottom: `${dif ? Math.floor(dif / 2) : 0}px solid ${DefaultPalette.white}`,
borderTop: `${dif ? Math.ceil(dif / 2) : 0}px solid ${DefaultPalette.white}`
};
} else if (elem.verticalAlign && elem.verticalAlign === ShimmerElementVerticalAlign.top) {
borderStyle = {
borderBottom: `${dif ? dif : 0}px solid ${DefaultPalette.white}`,
borderTop: `0px solid ${DefaultPalette.white}`
};
} else if (elem.verticalAlign && elem.verticalAlign === ShimmerElementVerticalAlign.bottom) {
borderStyle = {
borderBottom: `0px solid ${DefaultPalette.white}`,
borderTop: `${dif ? dif : 0}px solid ${DefaultPalette.white}`
};
}

return borderStyle;
}

export function findMaxElementHeight(elements: Array<ICircle | IGap | ILine>): number {
const itemsDefaulted: Array<ICircle | IGap | ILine> = elements.map((elem: ICircle | IGap | ILine): ICircle | IGap | ILine => {
switch (elem.type) {
case ShimmerElementType.circle:
if (!elem.height) {
elem.height = CIRCLE_DEFAULT_HEIGHT;
}
case ShimmerElementType.line:
if (!elem.height) {
elem.height = LINE_DEFAULT_HEIGHT;
}
case ShimmerElementType.gap:
if (!elem.height) {
elem.height = GAP_DEFAULT_HEIGHT;
}
}
return elem;
});

const rowHeight = itemsDefaulted.reduce((acc: number, next: ICircle | IGap | ILine): number => {
return next.height ?
next.height > acc ? next.height : acc
: acc;
}, 0);

return rowHeight;
}
Loading