/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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, { ReactNode } from 'react';
import { render, mount } from 'enzyme';
import { requiredProps } from '../../test/required_props';
import {
OuiPopover,
getPopoverPositionFromAnchorPosition,
getPopoverAlignFromAnchorPosition,
PopoverAnchorPosition,
} from './popover';
import { keys } from '../../services';
jest.mock('../portal', () => ({
OuiPortal: ({ children }: { children: ReactNode }) => children,
}));
let id = 0;
const getId = () => `${id++}`;
describe('OuiPopover', () => {
test('is rendered', () => {
const component = render(
}
closePopover={() => {}}
{...requiredProps}
/>
);
expect(component).toMatchSnapshot();
});
test('children is rendered', () => {
const component = render(
} closePopover={() => {}}>
Children
);
expect(component).toMatchSnapshot();
});
describe('props', () => {
describe('display block', () => {
test('is rendered', () => {
const component = render(
}
closePopover={() => {}}
/>
);
expect(component).toMatchSnapshot();
});
});
describe('anchorClassName', () => {
test('is rendered', () => {
const component = render(
}
closePopover={() => {}}
/>
);
expect(component).toMatchSnapshot();
});
});
describe('closePopover', () => {
it('is called when ESC key is hit and the popover is open', () => {
const closePopoverHandler = jest.fn();
const component = mount(
}
closePopover={closePopoverHandler}
isOpen
/>
);
component.simulate('keydown', { key: keys.ESCAPE });
expect(closePopoverHandler).toBeCalledTimes(1);
});
it('is not called when ESC key is hit and the popover is closed', () => {
const closePopoverHandler = jest.fn();
const component = mount(
}
closePopover={closePopoverHandler}
isOpen={false}
/>
);
component.simulate('keydown', { key: keys.ESCAPE });
expect(closePopoverHandler).not.toBeCalled();
});
});
describe('anchorPosition', () => {
test('defaults to centerDown', () => {
const component = render(
}
closePopover={() => {}}
/>
);
expect(component).toMatchSnapshot();
});
test('leftCenter is rendered', () => {
const component = render(
}
closePopover={() => {}}
anchorPosition="leftCenter"
/>
);
expect(component).toMatchSnapshot();
});
test('downRight is rendered', () => {
const component = render(
}
closePopover={() => {}}
anchorPosition="downRight"
/>
);
expect(component).toMatchSnapshot();
});
});
describe('isOpen', () => {
test('defaults to false', () => {
const component = render(
}
closePopover={() => {}}
/>
);
expect(component).toMatchSnapshot();
});
test('renders true', () => {
const component = mount(
}
closePopover={() => {}}
isOpen
/>
);
// console.log(component.debug());
expect(component.render()).toMatchSnapshot();
});
});
describe('ownFocus', () => {
test('defaults to true', () => {
const component = mount(
}
closePopover={() => {}}
/>
);
expect(component.render()).toMatchSnapshot();
});
test('renders false', () => {
const component = mount(
}
closePopover={() => {}}
/>
);
expect(component.render()).toMatchSnapshot();
});
});
describe('panelClassName', () => {
test('is rendered', () => {
const component = mount(
}
closePopover={() => {}}
panelClassName="test"
isOpen
/>
);
expect(component.render()).toMatchSnapshot();
});
});
describe('panelPaddingSize', () => {
test('is rendered', () => {
const component = mount(
}
closePopover={() => {}}
panelPaddingSize="s"
isOpen
/>
);
expect(component.render()).toMatchSnapshot();
});
});
describe('panelProps', () => {
test('is rendered', () => {
const component = mount(
}
closePopover={() => {}}
panelProps={requiredProps}
isOpen
/>
);
expect(component.render()).toMatchSnapshot();
});
});
describe('focusTrapProps', () => {
test('is rendered', () => {
const component = mount(
}
closePopover={() => {}}
focusTrapProps={{
clickOutsideDisables: false,
noIsolation: false,
scrollLock: false,
}}
isOpen
/>
);
expect(component.render()).toMatchSnapshot();
});
});
describe('offset', () => {
test('with arrow', () => {
const component = mount(
}
closePopover={() => {}}
offset={10}
isOpen
/>
);
expect(component.render()).toMatchSnapshot();
});
test('without arrow', () => {
const component = mount(
}
closePopover={() => {}}
offset={10}
hasArrow={false}
isOpen
/>
);
expect(component.render()).toMatchSnapshot();
});
});
describe('arrowChildren', () => {
test('is rendered', () => {
const component = mount(
}
closePopover={() => {}}
arrowChildren={}
isOpen
/>
);
expect(component.render()).toMatchSnapshot();
});
});
test('buffer', () => {
const component = mount(
}
closePopover={() => {}}
buffer={0}
isOpen
/>
);
expect(component.render()).toMatchSnapshot();
});
test('buffer for all sides', () => {
const component = mount(
}
closePopover={() => {}}
buffer={[20, 40, 60, 80]}
isOpen
/>
);
expect(component.render()).toMatchSnapshot();
});
});
describe('listener cleanup', () => {
let _raf: typeof window['requestAnimationFrame'];
let _caf: typeof window['cancelAnimationFrame'];
beforeAll(() => {
jest.useFakeTimers();
_raf = window.requestAnimationFrame;
_caf = window.cancelAnimationFrame;
const activeAnimationFrames = new Map();
let nextAnimationFrameId = 0;
window.requestAnimationFrame = (fn) => {
const animationFrameId = nextAnimationFrameId++;
activeAnimationFrames.set(animationFrameId, setTimeout(fn));
return animationFrameId;
};
window.cancelAnimationFrame = (id: number) => {
const timeoutId = activeAnimationFrames.get(id);
if (timeoutId) {
clearTimeout(timeoutId);
activeAnimationFrames.delete(id);
}
};
});
afterAll(() => {
jest.useRealTimers();
window.requestAnimationFrame = _raf;
window.cancelAnimationFrame = _caf;
});
it('cleans up timeouts and rAFs on unmount', () => {
const component = mount(
}
closePopover={() => {}}
panelPaddingSize="s"
isOpen={false}
/>
);
component.setProps({ isOpen: true });
component.unmount();
// OUI's jest configuration throws an error if there are any console.error calls, like
// React's setState on an unmounted component warning
// to be future proof, verify that's still the case
expect(() => {
console.error('This is a test');
}).toThrow();
// execute any pending timeouts or animation frame callbacks
// and validate the timeout/rAF clearing done by OuiPopover
jest.advanceTimersByTime(10);
});
});
});
describe('getPopoverPositionFromAnchorPosition', () => {
it('maps the first anchor position in a camel-cased string to a popover position', () => {
expect(getPopoverPositionFromAnchorPosition('upLeft')).toBe('top');
expect(getPopoverPositionFromAnchorPosition('rightDown')).toBe('right');
expect(getPopoverPositionFromAnchorPosition('downRight')).toBe('bottom');
expect(getPopoverPositionFromAnchorPosition('leftUp')).toBe('left');
});
it('returns undefined when an invalid position is extracted', () => {
expect(
getPopoverPositionFromAnchorPosition(
'nowhereNohow' as PopoverAnchorPosition
)
).toBeUndefined();
});
});
describe('getPopoverAlignFromAnchorPosition', () => {
it('maps the second anchor position in a camel-cased string to a popover position', () => {
expect(getPopoverAlignFromAnchorPosition('upLeft')).toBe('left');
expect(getPopoverAlignFromAnchorPosition('rightDown')).toBe('bottom');
expect(getPopoverAlignFromAnchorPosition('downRight')).toBe('right');
expect(getPopoverAlignFromAnchorPosition('leftUp')).toBe('top');
});
it('returns undefined when an invalid position is extracted', () => {
expect(
getPopoverAlignFromAnchorPosition('nowhereNohow' as PopoverAnchorPosition)
).toBeUndefined();
});
});