Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/utilities",
"comment": "Add isEqual and isNotEqual utilities",
"type": "patch"
}
],
"packageName": "@uifabric/utilities",
"email": "v-jojanz@microsoft.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "office-ui-fabric-react",
"comment": "Fix #3607",
"type": "patch"
}
],
"packageName": "office-ui-fabric-react",
"email": "v-jojanz@microsoft.com"
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
divProperties,
getNativeProps,
IRenderFunction,
createRef
createRef,
isEqual,
} from '../../Utilities';
import { IList, IListProps, IPage, IPageProps } from './List.types';

Expand Down Expand Up @@ -299,7 +300,7 @@ export class List extends BaseComponent<IListProps, IListState> implements IList
return true;
}

if (newProps.items === this.props.items &&
if (isEqual(newProps.items, this.props.items) &&
oldPages!.length === newPages!.length) {
for (let i = 0; i < oldPages!.length; i++) {
const oldPage = oldPages![i];
Expand Down
1 change: 1 addition & 0 deletions packages/utilities/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export * from './hoist';
export * from './hoistStatics';
export * from './initializeFocusRects';
export * from './initials';
export * from './isEqual';
export * from './keyboard';
export * from './language';
export * from './math';
Expand Down
125 changes: 125 additions & 0 deletions packages/utilities/src/isEqual.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { isEqual, isNotEqual } from './isEqual';

describe('isEquals and isNotEquals function helper', () => {
describe('Test indexes', () => {
// Set up arrays to compare
const arrayBase = ['1', '2', '3'];
const arrayControl = ['1', '2', '3'];
const arrayDifferentValues = ['1', '2', '4'];
const arrayDifferentTypes = ['1', '2', 3];
const arrayDifferentLength = ['1', '2', '3', '4'];

describe('isEqual', () => {
it('returns true with arrays with the same values', () => {
expect(isEqual(arrayBase, arrayControl)).toEqual(true);
});

it('returns false with arrays with different values', () => {
expect(isEqual(arrayBase, arrayDifferentValues)).toEqual(false);
});

it('returns false with arrays with different types', () => {
expect(isEqual(arrayBase, arrayDifferentTypes)).toEqual(false);
});

it('returns false with arrays with different length', () => {
expect(isEqual(arrayBase, arrayDifferentLength)).toEqual(false);
});
});

describe('isNotEqual', () => {
it('returns true with arrays with the same values', () => {
expect(isNotEqual(arrayBase, arrayControl)).toEqual(false);
});
});
});

describe('Test objects', () => {
// Set up objects to compare
const objectBase = { a: '1', b: '2' };
const objectControl = { a: '1', b: '2' };
const objectDifferentValues = { a: '1', b: '3' };
const objectDifferentValueTypes = { a: '1', b: 2 };
const objectDifferentKeyTypes = { 1: '1', 2: '2' };
const objectDifferentLength = { a: '1', b: '2', c: '3' };

describe('isEqual', () => {
it('returns true with objects with the same values', () => {
expect(isEqual(objectBase, objectControl)).toEqual(true);
});

it('returns false with objects with different values', () => {
expect(isEqual(objectBase, objectDifferentValues)).toEqual(false);
});

it('returns false with objects with different value types', () => {
expect(isEqual(objectBase, objectDifferentValueTypes)).toEqual(false);
});

it('returns false with objects with different key types', () => {
expect(isEqual(objectBase, objectDifferentKeyTypes)).toEqual(false);
});

it('returns false with objects with different length', () => {
expect(isEqual(objectBase, objectDifferentLength)).toEqual(false);
});
});

describe('isNotEqual', () => {
it('returns true with objects with the same values', () => {
expect(isNotEqual(objectBase, objectControl)).toEqual(false);
});
});
});

describe('Test numbers', () => {
// Set up numbers to compare
const numberBase = 123;
const numberControl = 123;
const numberDifferentValues = 1234;
const numberDifferentTypes = '123';

describe('isEqual', () => {
it('returns true with numbers with the same values', () => {
expect(isEqual(numberBase, numberControl)).toEqual(true);
});

it('returns false with numbers with different values', () => {
expect(isEqual(numberBase, numberDifferentValues)).toEqual(false);
});

it('returns false with numbers with different types', () => {
expect(isEqual(numberBase, numberDifferentTypes)).toEqual(false);
});
});

describe('isNotEqual', () => {
it('returns true with numbers with the same values', () => {
expect(isNotEqual(numberBase, numberControl)).toEqual(false);
});
});
});

describe('Test strings', () => {
// Set up strings to compare
const stringBase = 'This is a string';
const stringControl = 'This is a string';
const stringDifferentValues = 'This is a string that isn\'t the same';

describe('isEqual', () => {
it('returns true with strings with the same values', () => {
expect(isEqual(stringBase, stringControl)).toEqual(true);
});

it('returns false with strings with different values', () => {
expect(isEqual(stringBase, stringDifferentValues)).toEqual(false);
});
});

describe('isNotEqual', () => {
it('returns true with strings with the same values', () => {
expect(isNotEqual(stringBase, stringControl)).toEqual(false);
});
});
});
});
85 changes: 85 additions & 0 deletions packages/utilities/src/isEqual.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* Checks if the first and second items are the same, recursively. Use for checking arrays and objects.
*
* @param itemA First item to compare to second item.
* @param itemB Second item to compare to first item.
* @returns {boolean} True if items are the same or false if not.
*/
export const isEqual = (itemA: any, itemB: any): boolean => { // tslint:disable-line no-any
// First, a simple check for strings and numbers
if (typeof itemA === 'string' || typeof itemA === 'number') {
if (itemA === itemB) {
return true;
}
return false;
}
// Get the value type
const type = Object.prototype.toString.call(itemA);
// If the two objects are not the same type, return false
if (type !== Object.prototype.toString.call(itemB)) {
return false;
}
// If items are not an object or array, return false
if (['[object Array]', '[object Object]'].indexOf(type) < 0) {
return false;
}
// Compare the length of the length of the two items
const valueLen = type === '[object Array]' ? itemA.length : Object.keys(itemA).length;
const otherLen = type === '[object Array]' ? itemB.length : Object.keys(itemB).length;
if (valueLen !== otherLen) {
return false;
}
// Compare two items
const compare = (item1: any, item2: any) => { // tslint:disable-line no-any
// Get the object type
const itemType = Object.prototype.toString.call(item1);
// If an object or array, compare recursively
if (['[object Array]', '[object Object]'].indexOf(itemType) >= 0) {
if (!isEqual(item1, item2)) {
return false;
}
}
// If the two items are not the same type, return false
if (itemType !== Object.prototype.toString.call(item2)) {
return false;
}
// Else if it's a function, convert to a string and compare
// Otherwise, just compare
if (itemType === '[object Function]') {
if (item1.toString() !== item2.toString()) {
return false;
}
}
if (item1 !== item2) {
return false;
}
};
// Compare properties
if (type === '[object Array]') {
for (let i = 0; i < valueLen; i++) {
if (compare(itemA[i], itemB[i]) === false) {
return false;
}
}
}
for (const key in itemA) {
if (itemA.hasOwnProperty(key)) {
if (compare(itemA[key], itemB[key]) === false) {
return false;
}
}
}
// If nothing failed, return true
return true;
};

/**
* Checks if the first and second items are NOT the same, recursively. Use for checking arrays and objects.
*
* @param itemA First item to compare to second item.
* @param itemB Second item to compare to first item.
* @returns {boolean} True if items are NOT the same or false if they are.
*/
export const isNotEqual = (itemA: any, itemB: any): boolean => { // tslint:disable-line no-any
return !isEqual(itemA, itemB);
};