diff --git a/README.md b/README.md index d1daa7f..fc2a774 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ import {Movable} from 'webrix'; * [useDebounce()](https://webrix.amdocs.com/docs/hooks/usedebounce) * [useThrottle()](https://webrix.amdocs.com/docs/hooks/usethrottle) * [useObject()](https://webrix.amdocs.com/docs/hooks/useobject) -* [useDimensions()](https://webrix.amdocs.com/docs/hooks/usedimensions) +* [useResizeObserver()](https://webrix.amdocs.com/docs/hooks/useResizeObserver) * [useAnimationFrame()](https://webrix.amdocs.com/docs/hooks/useanimationframe) * [useBoundingRectObserver()](https://webrix.amdocs.com/docs/hooks/useboundingrectobserver) * [useEventListener()](https://webrix.amdocs.com/docs/hooks/useeventlistener) @@ -97,3 +97,4 @@ import {Movable} from 'webrix'; * [](https://webrix.amdocs.com/docs/tools/resizeobserver) * [](https://webrix.amdocs.com/docs/tools/puppeteer) +* [](https://webrix.amdocs.com/docs/tools/clickoutside) diff --git a/src/components/Movable/Movable.hooks.js b/src/components/Movable/Movable.hooks.js index 4b8fe0f..47f4700 100644 --- a/src/components/Movable/Movable.hooks.js +++ b/src/components/Movable/Movable.hooks.js @@ -17,19 +17,22 @@ import {useRef, useCallback} from 'react'; export const useMove = ops => { + const opsRef = useRef(ops); const shared = useRef({}); + opsRef.current = ops; + const onBeginMove = useCallback(e => { - ops.forEach(({onBeginMove}) => onBeginMove(e, shared.current)); - }, [ops]); + opsRef.current.forEach(({onBeginMove}) => onBeginMove(e, shared.current)); + }, []); const onMove = useCallback(e => { - ops.forEach(({onMove}) => onMove(e, shared.current)); - }, [ops]); + opsRef.current.forEach(({onMove}) => onMove(e, shared.current)); + }, []); const onEndMove = useCallback(e => { - ops.forEach(({onEndMove}) => onEndMove(e, shared.current)) - }, [ops]); + opsRef.current.forEach(({onEndMove}) => onEndMove(e, shared.current)) + }, []); return {onBeginMove, onMove, onEndMove}; }; \ No newline at end of file diff --git a/src/components/Movable/Movable.test.js b/src/components/Movable/Movable.test.js index f60ab6b..0b436db 100644 --- a/src/components/Movable/Movable.test.js +++ b/src/components/Movable/Movable.test.js @@ -32,13 +32,14 @@ describe('', () => { expect(stopPropagation.calledOnce).toEqual(true); expect(preventDefault.calledOnce).toEqual(true); }); + it('onMove()', () => { const handleOnMove = sinon.spy(); const wrapper = mount(); const handlers = {}; let event; - document.addEventListener = (type, handler) => handlers[type] = handler; + document.addEventListener = (type, handler) => {handlers[type] = handler}; wrapper.simulate('mousedown', {clientX: 10, clientY: 10}); wrapper.simulate('touchstart', {changedTouches: [{clientX: 10, clientY: 10}]}); @@ -78,12 +79,13 @@ describe('', () => { expect(event.dx).toEqual(-10); expect(event.dy).toEqual(-10); }); + it('onEndMove()', () => { const handleOnEndMove = sinon.spy(); const wrapper = mount(); const handlers = {}; - document.addEventListener = (type, handler) => handlers[type] = handler; + document.addEventListener = (type, handler) => {handlers[type] = handler}; document.removeEventListener = sinon.spy(); wrapper.simulate('mousedown', {clientX: 10, clientY: 10}); diff --git a/src/components/Resizable/Resizable.hooks.js b/src/components/Resizable/Resizable.hooks.js index cc5dbb4..a6ad245 100644 --- a/src/components/Resizable/Resizable.hooks.js +++ b/src/components/Resizable/Resizable.hooks.js @@ -17,19 +17,22 @@ import {useRef, useCallback} from 'react'; export const useResize = ops => { + const opsRef = useRef(ops); const shared = useRef({}); + opsRef.current = ops; + const onBeginResize = useCallback(e => { - ops.forEach(({onBeginResize}) => onBeginResize(e, shared.current)); - }, [ops]); + opsRef.current.forEach(({onBeginResize}) => onBeginResize(e, shared.current)); + }, []); const onResize = useCallback(e => { - ops.forEach(({onResize}) => onResize(e, shared.current)); - }, [ops]); + opsRef.current.forEach(({onResize}) => onResize(e, shared.current)); + }, []); const onEndResize = useCallback(e => { - ops.forEach(({onEndResize}) => onEndResize(e, shared.current)); - }, [ops]); + opsRef.current.forEach(({onEndResize}) => onEndResize(e, shared.current)); + }, []); return {onBeginResize, onResize, onEndResize}; }; \ No newline at end of file diff --git a/src/components/Scrollable/Scrollable.jsx b/src/components/Scrollable/Scrollable.jsx index d6ee1ed..1de1f9c 100644 --- a/src/components/Scrollable/Scrollable.jsx +++ b/src/components/Scrollable/Scrollable.jsx @@ -167,14 +167,14 @@ export default class Scrollable extends React.PureComponent { }; render() { - const {children, style, element} = this.props; + const {children, element, className} = this.props; const vsb = findChildByType(children, VerticalScrollbarPlaceholder); const hsb = findChildByType(children, HorizontalScrollbarPlaceholder); const content = React.Children.toArray(children).filter(child => ![VerticalScrollbarPlaceholder, HorizontalScrollbarPlaceholder].includes(child.type)); return ( -
+
{React.cloneElement(element, this.getElementProps(), content)} {vsb ? vsb.props.children : } diff --git a/src/components/Scrollable/Scrollable.props.js b/src/components/Scrollable/Scrollable.props.js index d45999d..143516d 100644 --- a/src/components/Scrollable/Scrollable.props.js +++ b/src/components/Scrollable/Scrollable.props.js @@ -15,11 +15,11 @@ */ import React from 'react'; -import {func, shape, node, bool} from 'prop-types'; +import {func, node, bool, string} from 'prop-types'; import {noop} from 'utility/memory'; export const propTypes = { - style: shape({}), + className: string, onScroll: func, onUpdate: func, scrollOnDOMChange: bool, @@ -29,7 +29,7 @@ export const propTypes = { }; export const defaultProps = { - style: null, + className: null, onScroll: noop, onUpdate: noop, scrollOnDOMChange: true, diff --git a/src/components/Scrollable/Scrollable.scss b/src/components/Scrollable/Scrollable.scss index 2e617d7..b4589d7 100644 --- a/src/components/Scrollable/Scrollable.scss +++ b/src/components/Scrollable/Scrollable.scss @@ -2,6 +2,7 @@ --scrollable-track-thickness: 12px; --scrollable-thumb-thickness: Calc(var(--scrollable-track-thickness)/2); --scrollable-thumb-offset: 3px; + --scrollable-thumb-color: silver; position: relative; max-height: 100%; @@ -37,10 +38,7 @@ cursor: pointer; .scrollbar-thumb-inner { - background-color: rgba(28, 34, 43, 0.6); - border-radius: 4px; - opacity: 0; - transition: opacity 0.2s ease-out 0.5s; // The transition delay is used to keep the thumb visible for a short time when the cursor leaves. (see `Scrollable.constants.js`) + background-color: var(--scrollable-thumb-color); } } } diff --git a/src/components/Scrollable/components/HorizontalScrollbar/HorizontalScrollbar.jsx b/src/components/Scrollable/components/HorizontalScrollbar/HorizontalScrollbar.jsx index 0cef8b4..caa4183 100644 --- a/src/components/Scrollable/components/HorizontalScrollbar/HorizontalScrollbar.jsx +++ b/src/components/Scrollable/components/HorizontalScrollbar/HorizontalScrollbar.jsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, {useContext, useMemo, useRef} from 'react'; +import React, {useContext, useRef} from 'react'; import Movable from 'components/Movable'; import Context from '../../Scrollable.context'; import {CSS_VARS} from '../../Scrollable.constants'; @@ -25,7 +25,7 @@ const HorizontalScrollbar = () => { const track = useRef(); const thumb = useRef(); const {container, scrollLeft, cssVarsOnTracks} = useContext(Context); - const props = Movable.useMove(useMemo(() => [move(container, thumb, track)], [container])); + const props = Movable.useMove([move(container, thumb, track)]); const handleOnClick = e => { e.stopPropagation(); diff --git a/src/components/Scrollable/components/VerticalScrollbar/VerticalScrollbar.jsx b/src/components/Scrollable/components/VerticalScrollbar/VerticalScrollbar.jsx index 5fb8e69..161df43 100644 --- a/src/components/Scrollable/components/VerticalScrollbar/VerticalScrollbar.jsx +++ b/src/components/Scrollable/components/VerticalScrollbar/VerticalScrollbar.jsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, {useContext, useMemo, useRef} from 'react'; +import React, {useContext, useRef} from 'react'; import Movable from 'components/Movable'; import Context from '../../Scrollable.context'; import {CSS_VARS} from '../../Scrollable.constants'; @@ -25,7 +25,7 @@ const VerticalScrollbar = () => { const track = useRef(); const thumb = useRef(); const {container, scrollTop, cssVarsOnTracks} = useContext(Context); - const props = Movable.useMove(useMemo(() => [move(container, thumb, track)], [container])); + const props = Movable.useMove([move(container, thumb, track)]); const handleOnClick = e => { e.stopPropagation(); diff --git a/src/hooks/index.js b/src/hooks/index.js index 58e6104..051211b 100644 --- a/src/hooks/index.js +++ b/src/hooks/index.js @@ -1,12 +1,12 @@ export {default as useAnimationFrame} from './useAnimationFrame'; export {default as useBooleanState, useVisibilityState, useFocusabilityState} from './useBooleanState'; export {default as useBoundingRectObserver} from './useBoundingRectObserver'; -export {default as useClickOutside, ClickOutside, ClickOutsideOverride} from './useClickOutside'; -export {default as useMounted, useUnmounted} from './useMounted'; +export {default as useClickOutside} from './useClickOutside'; +export {default as useMounted} from './useMounted'; export {default as useDebounce} from './useDebounce'; -export {default as useDimensions} from './useDimensions'; +export {default as useResizeObserver} from './useResizeObserver'; export {default as useEventListener} from './useEventListener'; export {default as useThrottle} from './useThrottle'; export {default as useObject} from './useObject'; export {default as usePrevious} from './usePrevious'; -export {default as useTimeout} from './useTimeout'; \ No newline at end of file +export {default as useTimeout} from './useTimeout'; diff --git a/src/hooks/useClickOutside/index.js b/src/hooks/useClickOutside/index.js index 516eaeb..f0aea3d 100644 --- a/src/hooks/useClickOutside/index.js +++ b/src/hooks/useClickOutside/index.js @@ -15,5 +15,4 @@ */ import {useClickOutside} from './useClickOutside'; -export {ClickOutside, ClickOutsideOverride} from './useClickOutside'; export default useClickOutside; \ No newline at end of file diff --git a/src/hooks/useClickOutside/useClickOutside.js b/src/hooks/useClickOutside/useClickOutside.js index 45b2d8f..a3ff612 100644 --- a/src/hooks/useClickOutside/useClickOutside.js +++ b/src/hooks/useClickOutside/useClickOutside.js @@ -14,8 +14,7 @@ * limitations under the License. */ -import React, {useRef, useCallback, useContext} from 'react'; -import {func, node} from 'prop-types'; +import {useRef, useCallback, useContext} from 'react'; import {_document} from 'utility/mocks'; import useEventListener from '../useEventListener'; import OverrideContext from './useClickOutside.context'; @@ -39,6 +38,8 @@ import OverrideContext from './useClickOutside.context'; * element (for example, when dragging). In such cases the click is still considered as an inside click, * since it originated inside the element. * + * For class components use ClickOutside from 'webrix/tools/ClickOutside' + * * @param {Function} callback */ export const useClickOutside = callback => { @@ -57,26 +58,3 @@ export const useClickOutside = callback => { isClickedInside.current = true; }, [isClickedInside]); }; - -// Use this for class components -export const ClickOutside = ({children, onClickOutside}) => { - const handleOnMouseDownCapture = useClickOutside(onClickOutside); - // We're updating the contained element instead of adding a wrapper since adding - // a wrapper can may affect the styling/behavior - return React.cloneElement( - React.Children.only(children), {onMouseDownCapture: handleOnMouseDownCapture} - ); -}; - -// It is sometimes necessary to modify the condition of the click-outside handler -// of one or more elements. This can be done using the ClickOutsideOverride -export const ClickOutsideOverride = ({condition, children}) => ( - - {children} - -); - -ClickOutsideOverride.propTypes = { - condition: func, - children: node, -}; diff --git a/src/hooks/useClickOutside/useClickOutside.test.js b/src/hooks/useClickOutside/useClickOutside.test.js index 395513c..2665dde 100644 --- a/src/hooks/useClickOutside/useClickOutside.test.js +++ b/src/hooks/useClickOutside/useClickOutside.test.js @@ -2,9 +2,8 @@ import React from 'react'; // https://reactjs.org/docs/hooks-faq.html#how-to-test-components-that-use-hooks import {act} from 'react-dom/test-utils'; import sinon from 'sinon'; -import {mount, shallow} from 'enzyme'; -import OverrideContext from './useClickOutside.context'; -import {useClickOutside, ClickOutside, ClickOutsideOverride} from './useClickOutside'; +import {mount} from 'enzyme'; +import {useClickOutside} from './useClickOutside'; const Elem = callback => { useClickOutside(callback); @@ -33,15 +32,4 @@ describe('useClickOutside()', () => { elem.simulate('click'); expect(callback.callCount).toEqual(0); }); - - it('', () => { - const wrapper = shallow(
); - expect(() => shallow()).toThrow(); - expect(typeof wrapper.find('div').prop('onMouseDownCapture')).toBe('function'); - }); - - it('', () => { - const wrapper = shallow(); - expect(wrapper.find(OverrideContext.Provider)).toHaveLength(1); - }); }); diff --git a/src/hooks/useDimensions/readme.md b/src/hooks/useDimensions/readme.md deleted file mode 100644 index e97cc4c..0000000 --- a/src/hooks/useDimensions/readme.md +++ /dev/null @@ -1,2 +0,0 @@ -See the full documentation of `useDimensions()` at the -[Official Webrix Documentation Site](https://webrix.amdocs.com/docs/hooks/useDimensions) \ No newline at end of file diff --git a/src/hooks/useMounted/index.js b/src/hooks/useMounted/index.js index 7f7b31e..dc4a5c8 100644 --- a/src/hooks/useMounted/index.js +++ b/src/hooks/useMounted/index.js @@ -15,5 +15,4 @@ */ import {useMounted} from './useMounted'; -export {useUnmounted} from './useMounted'; export default useMounted; \ No newline at end of file diff --git a/src/hooks/useMounted/useMounted.js b/src/hooks/useMounted/useMounted.js index e662fc4..7092099 100644 --- a/src/hooks/useMounted/useMounted.js +++ b/src/hooks/useMounted/useMounted.js @@ -20,13 +20,6 @@ export const useMounted = () => { const mounted = useRef(false); useEffect(() => { mounted.current = true; - return () => mounted.current = false; }, []); return mounted.current; -}; - -export const useUnmounted = () => { - const unmounted = useRef(false); - useEffect(() => () => {unmounted.current = true}, []); - return unmounted.current; }; \ No newline at end of file diff --git a/src/hooks/useMounted/useMounted.test.js b/src/hooks/useMounted/useMounted.test.js index 80819b1..c172f02 100644 --- a/src/hooks/useMounted/useMounted.test.js +++ b/src/hooks/useMounted/useMounted.test.js @@ -1,7 +1,7 @@ import React from 'react'; import {act} from 'react-dom/test-utils'; import {mount} from 'enzyme'; -import {useMounted, useUnmounted} from './useMounted'; +import {useMounted} from './useMounted'; describe('useMounted()', () => { it('Should return the previous value', () => { @@ -20,21 +20,3 @@ describe('useMounted()', () => { expect(wrapper.find('.unmounted').length).toEqual(0); }); }); - -describe('useUnmounted()', () => { - it('Should return the previous value', () => { - const Elem = () => { - const unmounted = useUnmounted(); - return ( -
- ); - }; - let wrapper = null; - act(() => {wrapper = mount()}); - expect(wrapper.find('.unmounted').length).toEqual(0); - expect(wrapper.find('.mounted').length).toEqual(1); - wrapper.setProps({foo: 'bar'}); // Force an update... - expect(wrapper.find('.mounted').length).toEqual(1); - expect(wrapper.find('.unmounted').length).toEqual(0); - }); -}); \ No newline at end of file diff --git a/src/hooks/useDimensions/index.js b/src/hooks/useResizeObserver/index.js similarity index 87% rename from src/hooks/useDimensions/index.js rename to src/hooks/useResizeObserver/index.js index c64fd73..9596d5a 100644 --- a/src/hooks/useDimensions/index.js +++ b/src/hooks/useResizeObserver/index.js @@ -14,6 +14,6 @@ * limitations under the License. */ -import useDimensions from './useDimensions'; +import useResizeObserver from './useResizeObserver'; -export default useDimensions; \ No newline at end of file +export default useResizeObserver; \ No newline at end of file diff --git a/src/hooks/useResizeObserver/readme.md b/src/hooks/useResizeObserver/readme.md new file mode 100644 index 0000000..ed9847e --- /dev/null +++ b/src/hooks/useResizeObserver/readme.md @@ -0,0 +1,2 @@ +See the full documentation of `useResizeObserver` at the +[Official Webrix Documentation Site](https://webrix.amdocs.com/docs/hooks/useResizeObserver) \ No newline at end of file diff --git a/src/hooks/useDimensions/useDimensions.js b/src/hooks/useResizeObserver/useResizeObserver.js similarity index 100% rename from src/hooks/useDimensions/useDimensions.js rename to src/hooks/useResizeObserver/useResizeObserver.js diff --git a/src/hooks/useDimensions/useDimensions.test.js b/src/hooks/useResizeObserver/useResizeObserver.test.js similarity index 81% rename from src/hooks/useDimensions/useDimensions.test.js rename to src/hooks/useResizeObserver/useResizeObserver.test.js index b6c6321..8f05046 100644 --- a/src/hooks/useDimensions/useDimensions.test.js +++ b/src/hooks/useResizeObserver/useResizeObserver.test.js @@ -1,16 +1,16 @@ import React from 'react'; import {act} from 'react-dom/test-utils'; import {mount} from 'enzyme'; -import useDimensions, {__RewireAPI__ as rewireAPI} from './useDimensions'; +import useResizeObserver, {__RewireAPI__ as rewireAPI} from './useResizeObserver'; const Elem = () => { - const {width, height} = useDimensions({current: {}}); + const {width, height} = useResizeObserver({current: {}}); return (
{width},{height}
); }; -describe('useDimensions()', () => { +describe('useResizeObserver()', () => { it('Should return the previous value', async () => { let wrapper = null; let observed = 0, disconnected = 0; diff --git a/src/tools/ClickOutside/ClickOutside.jsx b/src/tools/ClickOutside/ClickOutside.jsx new file mode 100644 index 0000000..2a34ae1 --- /dev/null +++ b/src/tools/ClickOutside/ClickOutside.jsx @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2020, Amdocs Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import {func, node} from 'prop-types'; +import OverrideContext from 'hooks/useClickOutside/useClickOutside.context'; +import {useClickOutside} from 'hooks'; + +export const ClickOutside = ({children, onClickOutside}) => { + const handleOnMouseDownCapture = useClickOutside(onClickOutside); + // We're updating the contained element instead of adding a wrapper since adding + // a wrapper can may affect the styling/behavior + return React.cloneElement( + React.Children.only(children), {onMouseDownCapture: handleOnMouseDownCapture} + ); +}; + +// It is sometimes necessary to modify the condition of the click-outside handler +// of one or more elements. This can be done using the ClickOutsideOverride +export const ClickOutsideOverride = ({condition, children}) => ( + + {children} + +); + +ClickOutsideOverride.propTypes = { + condition: func, + children: node, +}; \ No newline at end of file diff --git a/src/tools/ClickOutside/ClickOutside.test.js b/src/tools/ClickOutside/ClickOutside.test.js new file mode 100644 index 0000000..e30911e --- /dev/null +++ b/src/tools/ClickOutside/ClickOutside.test.js @@ -0,0 +1,18 @@ +import React from 'react'; +// https://reactjs.org/docs/hooks-faq.html#how-to-test-components-that-use-hooks +import { shallow} from 'enzyme'; +import OverrideContext from 'hooks/useClickOutside/useClickOutside.context'; +import {ClickOutside, ClickOutsideOverride} from './ClickOutside'; + +describe('ClickOutside', () => { + it('', () => { + const wrapper = shallow(
); + expect(() => shallow()).toThrow(); + expect(typeof wrapper.find('div').prop('onMouseDownCapture')).toBe('function'); + }); + + it('', () => { + const wrapper = shallow(); + expect(wrapper.find(OverrideContext.Provider)).toHaveLength(1); + }); +}); diff --git a/src/tools/ClickOutside/index.js b/src/tools/ClickOutside/index.js new file mode 100644 index 0000000..5bb1ed7 --- /dev/null +++ b/src/tools/ClickOutside/index.js @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2020, Amdocs Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {ClickOutside} from './ClickOutside'; +export {ClickOutsideOverride} from './ClickOutside'; +export default ClickOutside; diff --git a/src/tools/index.js b/src/tools/index.js index ec9a1db..ddb4561 100644 --- a/src/tools/index.js +++ b/src/tools/index.js @@ -1,2 +1,3 @@ export {default as ResizeObserver} from './ResizeObserver'; -export {default as Puppeteer, puppet} from './Puppeteer'; \ No newline at end of file +export {default as Puppeteer, puppet} from './Puppeteer'; +export {default as ClickOutside, ClickOutsideOverride} from './ClickOutside'; \ No newline at end of file diff --git a/src/utility/object/object.test.js b/src/utility/object/object.test.js index 54fbe78..bc8d362 100644 --- a/src/utility/object/object.test.js +++ b/src/utility/object/object.test.js @@ -1,4 +1,4 @@ -import {get, set, isEqual, omit, clone} from './object'; +import {get, set, isEqual, omit, clone, EqualityIterators} from './object'; describe('Object', () => { @@ -86,6 +86,32 @@ describe('Object', () => { expect(isEqual(parent1, parent2)).toEqual(false); }); + it('isEqual() with shallow comparison', () => { + expect(isEqual(1, 1, EqualityIterators.SHALLOW)).toEqual(true); + expect(isEqual(true, false, EqualityIterators.SHALLOW)).toEqual(false); + expect(isEqual('hello', 'hello', EqualityIterators.SHALLOW)).toEqual(true); + expect(isEqual([1, 2, 3], [1, 2, 3], EqualityIterators.SHALLOW)).toEqual(true); + expect(isEqual([1, 2, 3], [3, 2, 1], EqualityIterators.SHALLOW)).toEqual(false); + expect(isEqual([{id: 1}], [{id: 2}], EqualityIterators.SHALLOW)).toEqual(false); + expect(isEqual({foo: 'bar'}, {foo: 'bar'}, EqualityIterators.SHALLOW)).toEqual(true); + expect(isEqual({foo: 'bar'}, {bar: 'foo'}, EqualityIterators.SHALLOW)).toEqual(false); + expect(isEqual({foo: [1, 2, {a: 1}]}, {foo: [1, 2, {a: 1}]}, EqualityIterators.SHALLOW)).toEqual(false); + expect(isEqual({foo: [1, 2, {a: 1}]}, {foo: [1, 2, {a: 2}]}, EqualityIterators.SHALLOW)).toEqual(false); + + /* Check Circular Traversal */ + const sub1 = {foo: [1, 2, {a: 1}]}; + const sub2 = {foo: [1, 2, {a: 1}]}; + const parent1 = {foo: [1, 2, {a: 1}], sub1, sub2}; + const parent2 = {foo: [1, 2, {a: 1}], sub1, sub2}; + parent1.aaa = parent2; + parent1.bbb = parent1; + parent2.aaa = parent1; + parent2.bbb = parent2; + expect(isEqual(parent1, parent2, EqualityIterators.SHALLOW)).toEqual(false); + parent2.ccc = 6; + expect(isEqual(parent1, parent2, EqualityIterators.SHALLOW)).toEqual(false); + }); + it('clone()', () => { const source = {foo: 1, bar: 'hello', baz: {baq: 23, test: [1, 2, 3]}}; const target = clone(source);