Skip to content

Commit

Permalink
Add back Fixture Editor to new Component Playground (#380)
Browse files Browse the repository at this point in the history
* Fix component name #360

* Create DragHandle component #360

* Integrate DragHandle for resizing left nav of CP #360

* Fix overflow or left nav #360

* Cover iframe while dragging to have drag continuity #360

* Rename test #360

* Don't show StarryBg when Loader is visible #360

* Add selected state and toggling to fixture editor button #360

* Fix env default in CP web pack config #360

* Narrow down CSS trans to one attr #360

* Add fixture for open fixture editor in CP #360

* Implement horizontal pane for fixture editor #360

* Adjust fixture editor pane orientation based on window size #360

* Update Cypress selector #360

* Update the content orientation when content width changes due to left nav #360

* Nevermind debouncing #360

* Add FixtureEditor component #360

* Prevent editor key events from reaching other components #360

* Use lodash.merge to override fixtures in tests #360

* Keep focused state inside FixtureEditor #360

* Ensure `fixtureLoad` is sent before 1st `fixtureUpdate` from RemoteLoader #360

* Don't show fixture editor before Loader are ready #360

* Integrate FixtureEditor in CP #360

* Clarify FixtureEditor state #360

* Bring back Cypress fixture editor smoke #360

* Always check children for state #360

* Simplify FixtureList fuzzy matching #360

* Add snapshot tests for StarryBg #360

* Send new message to Loader when clicking on selected fixture #360

* Unmount loader before mounting new one after HMR #360

* Use toHaveLength test helper #360

cc @flaviusone
  • Loading branch information
ovidiuch committed Jun 28, 2017
1 parent 827e34c commit 842353b
Show file tree
Hide file tree
Showing 58 changed files with 2,014 additions and 233 deletions.
5 changes: 2 additions & 3 deletions cypress/integration/local-state_spec.js
Expand Up @@ -30,7 +30,7 @@ describe('Local state example', () => {
});

it('should show welcome message', () => {
cy.get(getSelector('index__loader')).should('contain', "You're all set");
cy.get(getSelector('index__content')).should('contain', "You're all set");
});
});

Expand Down Expand Up @@ -75,8 +75,7 @@ describe('Local state example', () => {
});
});

// TODO: Enable back once FixtureEditor is put into new ComponentPlayground
context.skip('fixture editor', () => {
context('fixture editor', () => {
// The first menu button is the fixture editor toggle
const editorButtonSel = `${getSelector('index__button')}:eq(1)`;

Expand Down
3 changes: 3 additions & 0 deletions packages/__mocks__/@skidding/react-codemirror.js
@@ -0,0 +1,3 @@
import React from 'react';

module.exports = () => <span />;
9 changes: 9 additions & 0 deletions packages/__mocks__/localforage.js
@@ -0,0 +1,9 @@
let itemMocks = {};

module.exports = {
__setItemMocks: mocks => {
itemMocks = mocks;
},
getItem: jest.fn(itemKey => Promise.resolve(itemMocks[itemKey])),
setItem: jest.fn(() => Promise.resolve()),
};
4 changes: 4 additions & 0 deletions packages/react-cosmos-component-playground/package.json
Expand Up @@ -10,9 +10,13 @@
],
"main": "lib/index.js",
"devDependencies": {
"@skidding/react-codemirror": "^1.0.1",
"classnames": "^2.2.5",
"codemirror": "^5.25.2",
"fuzzaldrin-plus": "^0.4.1",
"html-webpack-plugin": "^2.28.0",
"localforage": "^1.5.0",
"lodash.merge": "^4.6.0",
"lodash.omitby": "^4.6.0",
"lodash.reduce": "^4.6.0",
"prop-types": "^15.5.10",
Expand Down
@@ -0,0 +1,22 @@
export default {
props: {
loaderUri: '/mock/loader/index.html',
router: {
goTo: url => console.log('go to', url),
routeLink: e => {
e.preventDefault();
console.log('link to', e.currentTarget.href);
},
},
component: 'ComponentA',
fixture: 'foo',
editor: true,
},
state: {
waitingForLoader: false,
fixtures: {
ComponentA: ['foo', 'bar'],
ComponentB: ['baz', 'qux'],
},
},
};
@@ -0,0 +1,45 @@
import React from 'react';
import { mount } from 'enzyme';
import { Loader } from 'react-cosmos-loader';
import createStateProxy from 'react-cosmos-state-proxy';
import selectedEditorFixture from '../../__fixtures__/selected-editor';
import DragHandle from '../../../DragHandle';
import ComponentPlayground from '../../';

// Vars populated in beforeEach blocks
let wrapper;

describe('Fixture editor controls', () => {
// Fixture editor is already on so the button will untoggle it
const fixtureEditorUrl = '/?component=ComponentA&fixture=foo';

beforeEach(() => {
return new Promise(resolve => {
// Mount component in order for ref and lifecycle methods to be called
wrapper = mount(
<Loader
proxies={[createStateProxy]}
component={ComponentPlayground}
fixture={selectedEditorFixture}
onComponentRef={resolve}
/>
);
});
});

it('should set untoggle URL to fixture editor button', () => {
expect(wrapper.find(`.header a[href="${fixtureEditorUrl}"]`)).toHaveLength(
1
);
});

it('should render selected fixture editor button', () => {
expect(
wrapper.find(`.header a[href="${fixtureEditorUrl}"].selectedButton`)
).toHaveLength(1);
});

it('should render DragHandle in fixture editor pane', () => {
expect(wrapper.find('.fixtureEditorPane').find(DragHandle)).toHaveLength(1);
});
});
@@ -0,0 +1,121 @@
import React from 'react';
import { mount } from 'enzyme';
import { Loader } from 'react-cosmos-loader';
import createStateProxy from 'react-cosmos-state-proxy';
import selectedEditorFixture from '../../__fixtures__/selected-editor';
import FixtureEditor from '../../../FixtureEditor';
import ComponentPlayground from '../../';

// Vars populated in beforeEach blocks
let messageHandlers;
let wrapper;
let loaderContentWindow;

const handleMessage = e => {
const { type } = e.data;
if (!messageHandlers[type]) {
throw new Error('Unexpected message event');
}
messageHandlers[type](e.data);
};

const waitForPostMessage = type =>
new Promise(resolve => {
messageHandlers[type] = resolve;
});

describe('Fixture editor', () => {
beforeEach(() => {
messageHandlers = {};
window.addEventListener('message', handleMessage, false);

const onFixtureLoad = waitForPostMessage('fixtureLoad');

return new Promise(resolve => {
// Mount component in order for ref and lifecycle methods to be called
wrapper = mount(
<Loader
proxies={[createStateProxy]}
component={ComponentPlayground}
fixture={selectedEditorFixture}
onComponentRef={resolve}
/>
);
}).then(instance => {
loaderContentWindow = {
postMessage: jest.fn(),
};
// iframe.contentWindow isn't available in jsdom
instance.loaderFrame = {
contentWindow: loaderContentWindow,
};

window.postMessage(
{
type: 'fixtureLoad',
fixtureBody: {
foo: 'bar',
},
},
'*'
);

return onFixtureLoad;
});
});

afterEach(() => {
window.removeEventListener('message', handleMessage);
});

it('sends initial fixture body as value to FixtureEditor', () => {
expect(wrapper.find(FixtureEditor).prop('value')).toEqual({
foo: 'bar',
});
});

describe('on fixture update from Loader', () => {
beforeEach(() => {
const onFixtureUpdate = waitForPostMessage('fixtureUpdate');

window.postMessage(
{
type: 'fixtureUpdate',
fixtureBody: {
baz: 'qux',
},
},
'*'
);

return onFixtureUpdate;
});

it('sends updated fixture body as value to FixtureEditor', () => {
expect(wrapper.find(FixtureEditor).prop('value')).toEqual({
foo: 'bar',
baz: 'qux',
});
});
});

describe('on fixture edit from editor', () => {
beforeEach(() => {
wrapper.find(FixtureEditor).prop('onChange')({
foo: 'baz',
});
});

it('sends edited fixture body to Loader', () => {
expect(loaderContentWindow.postMessage).toHaveBeenCalledWith(
{
type: 'fixtureEdit',
fixtureBody: {
foo: 'baz',
},
},
'*'
);
});
});
});

0 comments on commit 842353b

Please sign in to comment.