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

List: Parameterize generic type for item with any as default #8465

Merged
merged 10 commits into from
Mar 26, 2019
7 changes: 5 additions & 2 deletions apps/todo-app/src/components/TodoTabs.tsx
Original file line number Diff line number Diff line change
@@ -61,7 +61,10 @@ export default class TodoTabs extends React.Component<ITodoTabsProps, {}> {
return ev.which === KeyCodes.right;
};

private _onRenderTodoItem(item: ITodoItem): React.ReactElement<ITodoItemProps> {
KevinTCoughlin marked this conversation as resolved.
Show resolved Hide resolved
return <TodoItem key={item.id} item={item} onToggleComplete={this.props.onToggleComplete} onDeleteItem={this.props.onDeleteItem} />;
private _onRenderTodoItem(item?: ITodoItem): React.ReactElement<ITodoItemProps> | null {
if (item) {
return <TodoItem key={item.id} item={item} onToggleComplete={this.props.onToggleComplete} onDeleteItem={this.props.onDeleteItem} />;
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "office-ui-fabric-react",
"comment": "List: Parameterize generic T for item type with default type any",
"type": "minor"
}
],
"packageName": "office-ui-fabric-react",
"email": "keco@microsoft.com"
}
40 changes: 20 additions & 20 deletions packages/office-ui-fabric-react/etc/office-ui-fabric-react.api.ts
Original file line number Diff line number Diff line change
@@ -8799,21 +8799,21 @@ interface IList {
}

// @public (undocumented)
interface IListProps extends React.HTMLAttributes<List | HTMLDivElement> {
interface IListProps<T = any> extends React.HTMLAttributes<List<T> | HTMLDivElement> {
className?: string;
componentRef?: IRefObject<IList>;
getItemCountForPage?: (itemIndex?: number, visibleRect?: IRectangle) => number;
getKey?: (item: any, index?: number) => string;
getKey?: (item: T, index?: number) => string;
getPageHeight?: (itemIndex?: number, visibleRect?: IRectangle) => number;
getPageSpecification?: (itemIndex?: number, visibleRect?: IRectangle) => IPageSpecification;
getPageStyle?: (page: IPage) => any;
items?: any[];
onPageAdded?: (page: IPage) => void;
onPageRemoved?: (page: IPage) => void;
onPagesUpdated?: (pages: IPage[]) => void;
onRenderCell?: (item?: any, index?: number, isScrolling?: boolean) => React.ReactNode;
onRenderPage?: (pageProps: IPageProps, defaultRender?: IRenderFunction<IPageProps>) => React.ReactNode;
onShouldVirtualize?: (props: IListProps) => boolean;
getPageStyle?: (page: IPage<T>) => any;
items?: T[];
onPageAdded?: (page: IPage<T>) => void;
onPageRemoved?: (page: IPage<T>) => void;
onPagesUpdated?: (pages: IPage<T>[]) => void;
onRenderCell?: (item?: T, index?: number, isScrolling?: boolean) => React.ReactNode;
onRenderPage?: (pageProps: IPageProps<T>, defaultRender?: IRenderFunction<IPageProps<T>>) => React.ReactNode;
onShouldVirtualize?: (props: IListProps<T>) => boolean;
renderCount?: number;
renderedWindowsAhead?: number;
renderedWindowsBehind?: number;
@@ -8823,12 +8823,12 @@ interface IListProps extends React.HTMLAttributes<List | HTMLDivElement> {
}

// @public (undocumented)
interface IListState {
interface IListState<T = any> {
// (undocumented)
isScrolling?: boolean;
measureVersion?: number;
// (undocumented)
pages?: IPage[];
pages?: IPage<T>[];
}

// @public (undocumented)
@@ -9213,7 +9213,7 @@ interface IOverlayStyles {
}

// @public (undocumented)
interface IPage {
interface IPage<T = any> {
// (undocumented)
data?: any;
// (undocumented)
@@ -9223,7 +9223,7 @@ interface IPage {
// (undocumented)
itemCount: number;
// (undocumented)
items: any[] | undefined;
items: T[] | undefined;
// (undocumented)
key: string;
// (undocumented)
@@ -9235,8 +9235,8 @@ interface IPage {
}

// @public (undocumented)
interface IPageProps extends React.HTMLAttributes<HTMLDivElement>, React.ClassAttributes<HTMLDivElement> {
page: IPage;
interface IPageProps<T = any> extends React.HTMLAttributes<HTMLDivElement>, React.ClassAttributes<HTMLDivElement> {
page: IPage<T>;
role?: string;
}

@@ -11624,12 +11624,12 @@ class LinkBase extends BaseComponent<ILinkProps, any>, implements ILink {
}

// @public
class List extends BaseComponent<IListProps, IListState>, implements IList {
constructor(props: IListProps);
class List<T = any> extends BaseComponent<IListProps<T>, IListState<T>>, implements IList {
constructor(props: IListProps<T>);
// (undocumented)
componentDidMount(): void;
// (undocumented)
componentWillReceiveProps(newProps: IListProps): void;
componentWillReceiveProps(newProps: IListProps<T>): void;
// (undocumented)
static defaultProps: {
onRenderCell: (item: any, index: number, containsFocus: boolean) => JSX.Element;
@@ -11649,7 +11649,7 @@ class List extends BaseComponent<IListProps, IListState>, implements IList {
render(): JSX.Element;
scrollToIndex(index: number, measureItem?: (itemIndex: number) => number, scrollToMode?: ScrollToMode): void;
// (undocumented)
shouldComponentUpdate(newProps: IListProps, newState: IListState): boolean;
shouldComponentUpdate(newProps: IListProps<T>, newState: IListState<T>): boolean;
}

// @public
61 changes: 31 additions & 30 deletions packages/office-ui-fabric-react/src/components/List/List.tsx
Original file line number Diff line number Diff line change
@@ -25,21 +25,21 @@ const DEFAULT_RENDERED_WINDOWS_AHEAD = 2;
const PAGE_KEY_PREFIX = 'page-';
const SPACER_KEY_PREFIX = 'spacer-';

export interface IListState {
pages?: IPage[];
export interface IListState<T = any> {
KevinTCoughlin marked this conversation as resolved.
Show resolved Hide resolved
pages?: IPage<T>[];

/** The last versionstamp for */
measureVersion?: number;
isScrolling?: boolean;
}

interface IPageCacheItem {
page: IPage;
interface IPageCacheItem<T> {
page: IPage<T>;
pageElement?: JSX.Element;
}

interface IPageCache {
[key: string]: IPageCacheItem;
interface IPageCache<T> {
[key: string]: IPageCacheItem<T>;
}

const EMPTY_RECT = {
@@ -79,7 +79,7 @@ const _measureScrollRect = _measurePageRect;
* or forcing an update change cause pages to shrink/grow. When these operations occur, we increment a measureVersion
* number, which we associate with cached measurements and use to determine if a remeasure should occur.
*/
export class List extends BaseComponent<IListProps, IListState> implements IList {
export class List<T = any> extends BaseComponent<IListProps<T>, IListState<T>> implements IList {
public static defaultProps = {
startIndex: 0,
onRenderCell: (item: any, index: number, containsFocus: boolean) => <>{(item && item.name) || ''}</>,
@@ -124,9 +124,9 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
private _measureVersion: number;
private _scrollHeight: number;
private _scrollTop: number;
private _pageCache: IPageCache;
private _pageCache: IPageCache<T>;

constructor(props: IListProps) {
constructor(props: IListProps<T>) {
super(props);

this.state = {
@@ -304,7 +304,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
}
}

public componentWillReceiveProps(newProps: IListProps): void {
public componentWillReceiveProps(newProps: IListProps<T>): void {
if (
newProps.items !== this.props.items ||
newProps.renderCount !== this.props.renderCount ||
@@ -321,7 +321,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
}
}

public shouldComponentUpdate(newProps: IListProps, newState: IListState): boolean {
public shouldComponentUpdate(newProps: IListProps<T>, newState: IListState<T>): boolean {
const { pages: oldPages } = this.state;
const { pages: newPages } = newState;
let shouldComponentUpdate = false;
@@ -377,7 +377,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
);
}

private _shouldVirtualize(props: IListProps = this.props): boolean {
private _shouldVirtualize(props: IListProps<T> = this.props): boolean {
const { onShouldVirtualize } = props;
return !onShouldVirtualize || onShouldVirtualize(props);
}
@@ -389,7 +389,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
this._pageCache = {};
}

private _renderPage(page: IPage): JSX.Element {
private _renderPage(page: IPage<T>): JSX.Element {
const { usePageCache } = this.props;
let cachedPage;
// if usePageCache is set and cached page element can be found, just return cached page
@@ -430,7 +430,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
}

/** Generate the style object for the page. */
private _getPageStyle(page: IPage): React.StyleHTMLAttributes<HTMLDivElement> {
private _getPageStyle(page: IPage<T>): React.StyleHTMLAttributes<HTMLDivElement> {
const { getPageStyle } = this.props;

return {
@@ -443,7 +443,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
};
}

private _onRenderPage = (pageProps: IPageProps, defaultRender?: IRenderFunction<IPageProps>): any => {
private _onRenderPage = (pageProps: IPageProps<T>, defaultRender?: IRenderFunction<IPageProps<T>>): any => {
const { onRenderCell, role } = this.props;

const {
@@ -459,7 +459,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
const index = startIndex + i;
const item = items[i];

let itemKey = this.props.getKey ? this.props.getKey(item, index) : item && item.key;
let itemKey = this.props.getKey ? this.props.getKey(item, index) : item && (item as any).key;

if (itemKey === null || itemKey === undefined) {
itemKey = index;
@@ -559,7 +559,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
this.forceUpdate();
}

private _updatePages(props: IListProps = this.props): void {
private _updatePages(props: IListProps<T> = this.props): void {
// console.log('updating pages');

if (!this._requiredRect) {
@@ -597,7 +597,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList

// Notify the caller that rendering the new pages has completed
if (props.onPagesUpdated) {
props.onPagesUpdated(this.state.pages as IPage[]);
props.onPagesUpdated(this.state.pages as IPage<T>[]);
}
});
}
@@ -608,12 +608,12 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
* @param newPages - The new pages
* @param props - The props to use
*/
private _notifyPageChanges(oldPages: IPage[], newPages: IPage[], props: IListProps = this.props): void {
private _notifyPageChanges(oldPages: IPage<T>[], newPages: IPage<T>[], props: IListProps<T> = this.props): void {
const { onPageAdded, onPageRemoved } = props;

if (onPageAdded || onPageRemoved) {
const renderedIndexes: {
[index: number]: IPage;
[index: number]: IPage<T>;
} = {};

for (const page of oldPages) {
@@ -640,7 +640,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
}
}

private _updatePageMeasurements(pages: IPage[]): boolean {
private _updatePageMeasurements(pages: IPage<T>[]): boolean {
let heightChanged = false;

// when not in virtualize mode, we render all the items without page measurement
@@ -663,7 +663,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
* Given a page, measure its dimensions, update cache.
* @returns True if the height has changed.
*/
private _measurePage(page: IPage): boolean {
private _measurePage(page: IPage<T>): boolean {
let hasChangedHeight = false;
const pageElement = this.refs[page.key] as HTMLElement;
const cachedHeight = this._cachedPageHeights[page.startIndex];
@@ -700,7 +700,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
}

/** Called when a page has been added to the DOM. */
private _onPageAdded(page: IPage): void {
private _onPageAdded(page: IPage<T>): void {
const { onPageAdded } = this.props;

// console.log('page added', page.startIndex, this.state.pages.map(page => page.key).join(', '));
@@ -711,7 +711,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
}

/** Called when a page has been removed from the DOM. */
private _onPageRemoved(page: IPage): void {
private _onPageRemoved(page: IPage<T>): void {
const { onPageRemoved } = this.props;

// console.log(' --- page removed', page.startIndex, this.state.pages.map(page => page.key).join(', '));
@@ -722,14 +722,14 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
}

/** Build up the pages that should be rendered. */
private _buildPages(props: IListProps): IListState {
private _buildPages(props: IListProps<T>): IListState<T> {
let { renderCount } = props;
const { items, startIndex, getPageHeight } = props;

renderCount = this._getRenderCount(props);

const materializedRect = { ...EMPTY_RECT };
const pages: IPage[] = [];
const pages: IPage<T>[] = [];

let itemsPerPage = 1;
let pageTop = 0;
@@ -755,7 +755,8 @@ export class List extends BaseComponent<IListProps, IListState> implements IList

const pageBottom = pageTop + pageHeight - 1;

const isPageRendered = findIndex(this.state.pages as IPage[], (page: IPage) => !!page.items && page.startIndex === itemIndex) > -1;
const isPageRendered =
findIndex(this.state.pages as IPage<T>[], (page: IPage<T>) => !!page.items && page.startIndex === itemIndex) > -1;
const isPageInAllowedRange = !allowedRect || (pageBottom >= allowedRect.top && pageTop <= allowedRect.bottom!);
const isPageInRequiredRange = !this._requiredRect || (pageBottom >= this._requiredRect.top && pageTop <= this._requiredRect.bottom!);
const isPageVisible = (!isFirstRender && (isPageInRequiredRange || (isPageInAllowedRange && isPageRendered))) || !shouldVirtualize;
@@ -883,7 +884,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
style: React.CSSProperties = {},
data?: any,
isSpacer?: boolean
): IPage {
): IPage<T> {
pageKey = pageKey || PAGE_KEY_PREFIX + startIndex;
const cachedPage = this._pageCache[pageKey];
if (cachedPage && cachedPage.page) {
@@ -903,14 +904,14 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
};
}

private _getRenderCount(props?: IListProps): number {
private _getRenderCount(props?: IListProps<T>): number {
const { items, startIndex, renderCount } = props || this.props;

return renderCount === undefined ? (items ? items.length - startIndex! : 0) : renderCount;
}

/** Calculate the visible rect within the list where top: 0 and left: 0 is the top/left of the list. */
private _updateRenderRects(props?: IListProps, forceUpdate?: boolean): void {
private _updateRenderRects(props?: IListProps<T>, forceUpdate?: boolean): void {
props = props || this.props;
const { renderedWindowsAhead, renderedWindowsBehind } = props;
const { pages } = this.state;
Loading