flipper-pkg template will now use sandy

Summary:
This diff updates plugin scaffolding by using Sandy and closely following the internal Scarf templates (see D24949452, D24949452)

By using `flipper-plugin`, it is now also possible to write unit tests for a plugin, and the default infra for that is generated (babel / jest)

For now there is still a dependency on `flipper` to support fancy components not yet available in Sandy, this will be updated in the future: T79632585

Changelog: `flipper-pkg init` now uses the new Sandy plugin infrastructure ant Ant.design component system

Reviewed By: nikoant

Differential Revision: D24950080

fbshipit-source-id: afc5e7ac728b20cb84fdbbdcb76cd45968736c01
This commit is contained in:
Michel Weststrate
2020-11-16 13:08:05 -08:00
committed by Facebook GitHub Bot
parent 69504252de
commit dc82ec2885
5 changed files with 207 additions and 81 deletions

View File

@@ -43,6 +43,14 @@ test('It generates the correct files', async () => {
Object { Object {
"/dev/null/.gitignore": "node_modules "/dev/null/.gitignore": "node_modules
dist/ dist/
",
"/dev/null/babel.config.js": "module.exports = {
presets: [
'@babel/preset-typescript',
'@babel/preset-react',
['@babel/preset-env', {targets: {node: 'current'}}]
],
};
", ",
"/dev/null/package.json": "{ "/dev/null/package.json": "{
\\"$schema\\": \\"https://fbflipper.com/schemas/plugin-package/v2.json\\", \\"$schema\\": \\"https://fbflipper.com/schemas/plugin-package/v2.json\\",
@@ -61,65 +69,121 @@ test('It generates the correct files', async () => {
\\"lint\\": \\"flipper-pkg lint\\", \\"lint\\": \\"flipper-pkg lint\\",
\\"prepack\\": \\"flipper-pkg lint && flipper-pkg bundle\\", \\"prepack\\": \\"flipper-pkg lint && flipper-pkg bundle\\",
\\"build\\": \\"flipper-pkg bundle\\", \\"build\\": \\"flipper-pkg bundle\\",
\\"watch\\": \\"flipper-pkg bundle --watch\\" \\"watch\\": \\"flipper-pkg bundle --watch\\",
\\"test\\": \\"jest\\"
}, },
\\"peerDependencies\\": { \\"peerDependencies\\": {
\\"flipper\\": \\"latest\\" \\"flipper\\": \\"latest\\",
\\"flipper-plugin\\": \\"latest\\",
\\"antd\\": \\"latest\\"
}, },
\\"devDependencies\\": { \\"devDependencies\\": {
\\"@babel/preset-react\\": \\"latest\\",
\\"@babel/preset-typescript\\": \\"latest\\",
\\"@types/jest\\": \\"latest\\",
\\"@types/react\\": \\"latest\\", \\"@types/react\\": \\"latest\\",
\\"@types/react-dom\\": \\"latest\\", \\"@types/react-dom\\": \\"latest\\",
\\"antd\\": \\"latest\\",
\\"flipper\\": \\"latest\\", \\"flipper\\": \\"latest\\",
\\"flipper-pkg\\": \\"latest\\" \\"flipper-plugin\\": \\"latest\\",
\\"flipper-pkg\\": \\"latest\\",
\\"jest\\": \\"latest\\"
} }
} }
",
"/dev/null/src/__tests__/test.spec.tsx": "import {TestUtils} from 'flipper-plugin';
import * as Plugin from '..';
// Read more: https://fbflipper.com/docs/extending/desktop-plugins#testing-plugin-logic
// API: https://fbflipper.com/docs/extending/desktop-plugins#testing-plugin-logic
test('It can store data', () => {
const {instance, sendEvent} = TestUtils.startPlugin(Plugin);
expect(instance.data.get()).toEqual({});
sendEvent('newData', {id: 'firstID'});
sendEvent('newData', {id: 'secondID'});
expect(instance.data.get()).toMatchInlineSnapshot(\`
Object {
\\"firstID\\": Object {
\\"id\\": \\"firstID\\",
},
\\"secondID\\": Object {
\\"id\\": \\"secondID\\",
},
}
\`);
});
// Read more: https://fbflipper.com/docs/extending/desktop-plugins#testing-plugin-logic
// API: https://fbflipper.com/docs/extending/desktop-plugins#testing-plugin-logic
test('It can render data', async () => {
const {instance, renderer, sendEvent} = TestUtils.renderPlugin(Plugin);
expect(instance.data.get()).toEqual({});
sendEvent('newData', {id: 'firstID'});
sendEvent('newData', {id: 'secondID'});
expect(await renderer.findByTestId('firstID')).not.toBeNull();
expect(await renderer.findByTestId('secondID')).toMatchInlineSnapshot(\`
<pre
data-testid=\\"secondID\\"
>
{\\"id\\":\\"secondID\\"}
</pre>
\`);
});
", ",
"/dev/null/src/index.tsx": "import React from 'react'; "/dev/null/src/index.tsx": "import React from 'react';
import {FlipperPlugin, View, KeyboardActions} from 'flipper'; import {PluginClient, usePlugin, createState, useValue, Layout} from 'flipper-plugin';
type State = {}; type Data = {
id: string;
type Data = {}; message?: string;
type PersistedState = {
data: Array<Data>;
}; };
export default class extends FlipperPlugin<State, any, PersistedState> { type Events = {
static keyboardActions: KeyboardActions = ['clear']; newData: Data;
};
static defaultPersistedState: PersistedState = { // Read more: https://fbflipper.com/docs/extending/desktop-plugins#creating-a-first-plugin
data: [], // API: https://fbflipper.com/docs/extending/flipper-plugin#pluginclient
}; export function plugin(client: PluginClient<Events, {}>) {
const data = createState<Record<string, Data>>({}, {persist: 'data'});
static persistedStateReducer = ( client.onMessage('newData', (newData) => {
persistedState: PersistedState, data.update((draft) => {
method: string, draft[newData.id] = newData;
data: Data, });
): PersistedState => { });
return {
...persistedState,
data: persistedState.data.concat([data]),
};
};
state = {}; client.addMenuEntry({
action: 'clear',
handler: async () => {
data.set({});
},
});
onKeyboardAction = (action: string) => { return {data};
if (action === 'clear') { }
this.props.setPersistedState({data: []});
}
};
render() { // Read more: https://fbflipper.com/docs/extending/desktop-plugins#building-a-user-interface-for-the-plugin
return ( // API: https://fbflipper.com/docs/extending/flipper-plugin#react-hooks
<View scrollable> export function Component() {
{this.props.persistedState.data.map((d) => ( const instance = usePlugin(plugin);
<div>{JSON.stringify(d, null, 2)}<hr/></div> const data = useValue(instance.data);
))}
</View> return (
) <Layout.ScrollContainer>
} {Object.entries(data).map(([id, d]) => (
<pre key={id} data-testid={id}>
{JSON.stringify(d)}
</pre>
))}
</Layout.ScrollContainer>
);
} }
", ",
"/dev/null/tsconfig.json": "{ "/dev/null/tsconfig.json": "{

View File

@@ -0,0 +1,7 @@
module.exports = {
presets: [
'@babel/preset-typescript',
'@babel/preset-react',
['@babel/preset-env', {targets: {node: 'current'}}]
],
};

View File

@@ -15,15 +15,24 @@
"lint": "flipper-pkg lint", "lint": "flipper-pkg lint",
"prepack": "flipper-pkg lint && flipper-pkg bundle", "prepack": "flipper-pkg lint && flipper-pkg bundle",
"build": "flipper-pkg bundle", "build": "flipper-pkg bundle",
"watch": "flipper-pkg bundle --watch" "watch": "flipper-pkg bundle --watch",
"test": "jest"
}, },
"peerDependencies": { "peerDependencies": {
"flipper": "latest" "flipper": "latest",
"flipper-plugin": "latest",
"antd": "latest"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-react": "latest",
"@babel/preset-typescript": "latest",
"@types/jest": "latest",
"@types/react": "latest", "@types/react": "latest",
"@types/react-dom": "latest", "@types/react-dom": "latest",
"antd": "latest",
"flipper": "latest", "flipper": "latest",
"flipper-pkg": "latest" "flipper-plugin": "latest",
"flipper-pkg": "latest",
"jest": "latest"
} }
} }

View File

@@ -0,0 +1,44 @@
import {TestUtils} from 'flipper-plugin';
import * as Plugin from '..';
// Read more: https://fbflipper.com/docs/extending/desktop-plugins#testing-plugin-logic
// API: https://fbflipper.com/docs/extending/desktop-plugins#testing-plugin-logic
test('It can store data', () => {
const {instance, sendEvent} = TestUtils.startPlugin(Plugin);
expect(instance.data.get()).toEqual({});
sendEvent('newData', {id: 'firstID'});
sendEvent('newData', {id: 'secondID'});
expect(instance.data.get()).toMatchInlineSnapshot(`
Object {
"firstID": Object {
"id": "firstID",
},
"secondID": Object {
"id": "secondID",
},
}
`);
});
// Read more: https://fbflipper.com/docs/extending/desktop-plugins#testing-plugin-logic
// API: https://fbflipper.com/docs/extending/desktop-plugins#testing-plugin-logic
test('It can render data', async () => {
const {instance, renderer, sendEvent} = TestUtils.renderPlugin(Plugin);
expect(instance.data.get()).toEqual({});
sendEvent('newData', {id: 'firstID'});
sendEvent('newData', {id: 'secondID'});
expect(await renderer.findByTestId('firstID')).not.toBeNull();
expect(await renderer.findByTestId('secondID')).toMatchInlineSnapshot(`
<pre
data-testid="secondID"
>
{"id":"secondID"}
</pre>
`);
});

View File

@@ -1,47 +1,49 @@
import React from 'react'; import React from 'react';
import {FlipperPlugin, View, KeyboardActions} from 'flipper'; import {PluginClient, usePlugin, createState, useValue, Layout} from 'flipper-plugin';
type State = {}; type Data = {
id: string;
type Data = {}; message?: string;
type PersistedState = {
data: Array<Data>;
}; };
export default class extends FlipperPlugin<State, any, PersistedState> { type Events = {
static keyboardActions: KeyboardActions = ['clear']; newData: Data;
};
static defaultPersistedState: PersistedState = { // Read more: https://fbflipper.com/docs/extending/desktop-plugins#creating-a-first-plugin
data: [], // API: https://fbflipper.com/docs/extending/flipper-plugin#pluginclient
}; export function plugin(client: PluginClient<Events, {}>) {
const data = createState<Record<string, Data>>({}, {persist: 'data'});
static persistedStateReducer = ( client.onMessage('newData', (newData) => {
persistedState: PersistedState, data.update((draft) => {
method: string, draft[newData.id] = newData;
data: Data, });
): PersistedState => { });
return {
...persistedState,
data: persistedState.data.concat([data]),
};
};
state = {}; client.addMenuEntry({
action: 'clear',
handler: async () => {
data.set({});
},
});
onKeyboardAction = (action: string) => { return {data};
if (action === 'clear') { }
this.props.setPersistedState({data: []});
} // Read more: https://fbflipper.com/docs/extending/desktop-plugins#building-a-user-interface-for-the-plugin
}; // API: https://fbflipper.com/docs/extending/flipper-plugin#react-hooks
export function Component() {
render() { const instance = usePlugin(plugin);
return ( const data = useValue(instance.data);
<View scrollable>
{this.props.persistedState.data.map((d) => ( return (
<div>{JSON.stringify(d, null, 2)}<hr/></div> <Layout.ScrollContainer>
))} {Object.entries(data).map(([id, d]) => (
</View> <pre key={id} data-testid={id}>
) {JSON.stringify(d)}
} </pre>
))}
</Layout.ScrollContainer>
);
} }