import React from 'react';
import { mount, ReactWrapper } from 'enzyme';
import { createEditorFactory } from '@atlaskit/editor-test-helpers/create-editor';
import { doc, p } from '@atlaskit/editor-test-helpers/doc-builder';
import ContextPanel, {
Content,
Panel,
SwappableContentArea,
} from '../../../ui/ContextPanel';
import EditorContext from '../../../ui/EditorContext';
import {
akEditorDefaultLayoutWidth,
akEditorFullWidthLayoutWidth,
akEditorFullWidthLayoutLineLength,
} from '@atlaskit/editor-shared-styles';
import { EditorPlugin } from '../../../types';
import { EventDispatcher } from '../../../event-dispatcher';
import EditorActions from '../../../actions';
import contextPanelPlugin from '../../../plugins/context-panel';
import {
ContextPanelConsumer,
ContextPanelWidthProvider,
} from '../../../ui/ContextPanel/context';
import {
isPushingEditorContent,
editorWithWideBreakoutAndSidebarWidth,
} from '../../__helpers/page-objects/_context-panel';
describe('SwappableContentArea', () => {
const Component: React.FC = jest.fn(() => null);
let wrapper: ReactWrapper | undefined;
afterEach(() => {
if (wrapper) {
wrapper.unmount();
wrapper = undefined;
}
});
it('renders children', () => {
wrapper = mount(
,
);
expect(wrapper.find(Component).length).toBe(1);
});
// ContextPanel animates by doing a CSS transition on the container's width,
// and inside the container, sliding the content off screen.
//
// The container clips content to avoid scroll and overlaying with any elements
// that might be on the right.
describe('container', () => {
it('displays content when visible is true', () => {
wrapper = mount();
const panel = wrapper.find(Panel);
expect(getComputedStyle(panel.getDOMNode()).width).toEqual('320px');
});
it('hides content when visible is false', () => {
wrapper = mount(
,
);
const panel = wrapper.find(Panel);
expect(getComputedStyle(panel.getDOMNode()).width).toEqual('0px');
});
it('clips content using the container', () => {
wrapper = mount();
const style = getComputedStyle(wrapper.find(Panel).getDOMNode());
expect(style.overflow).toEqual('hidden');
});
it('should push the editor content if it will overlap the editor', () => {
wrapper = mount(
,
);
const panelElement = wrapper.find(Panel).getDOMNode();
expect(isPushingEditorContent(panelElement)).toBeTruthy();
});
it('should not push the editor content if it will not overlap the editor', () => {
wrapper = mount(
,
);
const panelElement = wrapper.find(Panel).getDOMNode();
expect(isPushingEditorContent(panelElement)).toBeFalsy();
});
it('should push the editor content if there are full-width editor breakout content', () => {
wrapper = mount(
,
);
const panelElement = wrapper.find(Panel).getDOMNode();
expect(isPushingEditorContent(panelElement)).toBeTruthy();
});
it('should not push the editor content if there are wide breakout editor content but panel will not overlap the editor', () => {
wrapper = mount(
,
);
const panelElement = wrapper.find(Panel).getDOMNode();
expect(isPushingEditorContent(panelElement)).toBeFalsy();
});
it('should push the editor cotent if there are wide breakout editor content and panel will overlap the editor', () => {
wrapper = mount(
,
);
const panelElement = wrapper.find(Panel).getDOMNode();
expect(isPushingEditorContent(panelElement)).toBeTruthy();
});
it('should push the content if editor is in full width mode', () => {
wrapper = mount(
,
);
const panelElement = wrapper.find(Panel).getDOMNode();
expect(isPushingEditorContent(panelElement)).toBeTruthy();
});
});
describe('content', () => {
it('is scrollable up/down', () => {
wrapper = mount();
const style = getComputedStyle(wrapper.find(Content).getDOMNode());
expect(style.overflowY).toEqual('auto');
});
});
});
describe('ContextPanelWidthProvider', () => {
let wrapper: ReactWrapper | undefined;
afterEach(() => {
if (wrapper) {
wrapper.unmount();
wrapper = undefined;
}
});
it('should broadcast width', () => {
let broadCast: (wdith: number) => void = (width) => {};
wrapper = mount(
{({ width, broadcastWidth }) => {
broadCast = broadcastWidth;
return {width}
;
}}
,
);
broadCast(320);
wrapper.update();
expect(wrapper.text()).toBe('320');
});
it('should broadcast positionedOverEditor', () => {
let broadCast: (positionedOverEditor: boolean) => void = (
positionedOverEditor,
) => {};
wrapper = mount(
{({ positionedOverEditor, broadcastPosition }) => {
broadCast = broadcastPosition;
return {positionedOverEditor ? 'true' : 'false'}
;
}}
,
);
broadCast(true);
wrapper.update();
expect(wrapper.text()).toBe('true');
});
it('should broadcast width with SwappableContentArea', () => {
wrapper = mount(
{({ width }) => {
return {width}
;
}}
,
);
expect(wrapper.text()).toBe('320');
});
it('should broadcast positionOverEditor to be true if panel is not pushing Editor', () => {
wrapper = mount(
{({ positionedOverEditor }) => {
return {positionedOverEditor ? 'true' : 'false'}
;
}}
,
);
expect(wrapper.text()).toBe('true');
});
it('should broadcast positionOverEditor to be false if panel is pushing Editor', () => {
wrapper = mount(
{({ positionedOverEditor }) => {
return {positionedOverEditor ? 'true' : 'false'}
;
}}
,
);
expect(wrapper.text()).toBe('false');
});
});
//ContextPanel uses WithEditorActions
const mountWithContext = (node: React.ReactNode, actions?: EditorActions) =>
mount({node});
const editorFactory = createEditorFactory();
const mockContextPanelPlugin: EditorPlugin = {
name: 'mockContextPanelPlugin',
pluginsOptions: {
contextPanel: (state) =>
mario saxaphone
,
},
};
describe('ContextPanel', () => {
let wrapper: ReactWrapper | undefined;
afterEach(() => {
if (wrapper) {
wrapper.unmount();
wrapper = undefined;
}
});
it('renders SwappableContentArea', () => {
wrapper = mountWithContext(
yoshi bongo
,
);
const contentArea = wrapper.find(SwappableContentArea);
expect(contentArea).toBeDefined();
});
it('passes top-level props and children to SwappableContentArea', () => {
wrapper = mountWithContext(
yoshi bongo
,
);
const contentArea = wrapper.find(SwappableContentArea);
expect(contentArea.prop('visible')).toBe(true);
expect(wrapper.text().indexOf('yoshi bongo')).toBeGreaterThan(-1);
});
it('provides no pluginContent if no EventDispatcher', () => {
wrapper = mountWithContext(
yoshi bongo
,
);
const contentArea = wrapper.find(SwappableContentArea);
expect(contentArea.prop('pluginContent')).toBeUndefined();
});
it('provides no pluginContent if no plugins define content', () => {
const editor = editorFactory({ doc: doc(p('hello')) });
const editorActions = new EditorActions();
const eventDispatcher = new EventDispatcher();
editorActions._privateRegisterEditor(editor.editorView, eventDispatcher);
wrapper = mountWithContext(
yoshi bongo
,
editorActions,
);
const contentArea = wrapper.find(SwappableContentArea);
expect(contentArea.prop('pluginContent')).toBeUndefined();
});
it('uses pluginContent instead if plugins define content', () => {
const editor = editorFactory({
editorPlugins: [mockContextPanelPlugin, contextPanelPlugin()],
doc: doc(p('hello')),
});
const editorActions = new EditorActions();
const eventDispatcher = new EventDispatcher();
editorActions._privateRegisterEditor(editor.editorView, eventDispatcher);
wrapper = mountWithContext(
yoshi bongo
,
editorActions,
);
const contentArea = wrapper.find(SwappableContentArea);
expect(contentArea.prop('pluginContent')).toBeDefined();
expect(wrapper.text().indexOf('yoshi bongo')).toEqual(-1);
expect(wrapper.text().indexOf('mario saxaphone')).toBeGreaterThan(-1);
});
});