sandy-migration.mdx (Creating Plugins - Migrating a Plugin to Sandy)

Summary: Restyle of page, including changes to spelling, grammar, links, and structure (where relevant).

Reviewed By: nikoant

Differential Revision: D36516592

fbshipit-source-id: c8f7ffe8ae26a5e741ae818a55b14cc3e35193ff
This commit is contained in:
Kevin Strider
2022-05-20 03:55:28 -07:00
committed by Facebook GitHub Bot
parent 55137371f3
commit 3dbd3f43e1

View File

@@ -1,9 +1,9 @@
--- ---
id: sandy-migration id: sandy-migration
title: Migrating a plugin to Sandy title: Migrating a Plugin to Sandy
--- ---
Migrating a plugin to the new Sandy plugin architecture consists of 3 steps: Migrating a plugin to the new Sandy plugin architecture consists of three steps:
1. Enabling Sandy for a plugin. 1. Enabling Sandy for a plugin.
2. Update state and connection management to use the Sandy APIs. 2. Update state and connection management to use the Sandy APIs.
@@ -11,21 +11,23 @@ Migrating a plugin to the new Sandy plugin architecture consists of 3 steps:
## Opting in to Sandy ## Opting in to Sandy
Converting a Flipper plugin to use Sandy can best be done by running Flipper from source. Converting a Flipper plugin to use Sandy is best done by running Flipper from source.
<OssOnly> <OssOnly>
For open source users: clone this repository and run `yarn install` in the `desktop` folder. For open-source users, clone the repository and run `yarn install` in the `desktop` folder.
</OssOnly> </OssOnly>
<FbInternalOnly> <FbInternalOnly>
For Facebook employees, pull the latest `fbsource`, and run `yarn install` in `~/fbsource/xplat/sonar/desktop` For Meta employees, pull the latest `fbsource`, and run `yarn install` in `~/fbsource/xplat/sonar/desktop`
</FbInternalOnly> </FbInternalOnly>
Enabling Sandy for a plugin requires two steps. First of all, `flipper-plugin` should be added as peer dependency to the `package.json` of the plugin: Enabling Sandy for a plugin requires two steps:
1. The `flipper-plugin` should be added as peer dependency to the `package.json` of the plugin:
```json ```json
"peerDependencies": { "peerDependencies": {
@@ -33,10 +35,7 @@ Enabling Sandy for a plugin requires two steps. First of all, `flipper-plugin` s
}, },
``` ```
Next, make sure to run `yarn install` again in the `desktop/` folder. 2. Make sure to run `yarn install` again in the `desktop/` folder. Sandy is now enabled for this plugin" the plugin has to be restructured to the new architecture, which you'll be able to do in the next step.
Sandy is now enabled for this plugin, and the plugin has to be restructured to the new architecture which we will do in the next step.
## Using Sandy for state and connection management ## Using Sandy for state and connection management
@@ -44,55 +43,52 @@ The goal of this step is to use and leave the plugin UI largely as is but conver
<FbInternalOnly> <FbInternalOnly>
For every plugin, we generated a task on our [Sandy Plugin Migration](https://www.internalfb.com/tasks?folder_filters&q=1341478626215302&group_by_type=MANUAL) dashboard. Completing this step corresponds to completing the `[flipper][sandy] convert plugin 'xxxx' to use Sandy APIs` task. For every plugin, a task is generated on the [Sandy Plugin Migration](https://www.internalfb.com/tasks?folder_filters&q=1341478626215302&group_by_type=MANUAL) dashboard. Completing this step corresponds to completing the `[flipper][sandy] convert plugin 'xxxx' to use Sandy APIs` task.
If you start this task, please assign yourself as owner and link it in the diff. If you start this task, please assign yourself as owner and link it in the diff.
</FbInternalOnly> </FbInternalOnly>
Comparing to 'classic' plugins, there are a few fundamental differences when it comes to the plugin structure of Sandy plugins. Compared to 'classic' plugins, there are a few fundamental differences when it comes to the plugin structure of Sandy plugins. A class extending from `FlipperPlugin` that is exported as `default` is _no longer_ used to define a plugin.
A class extending from `FlipperPlugin` which is exported as `default` is _no longer_ used to define a plugin.
Instead, a plugin definition consists of two parts: Instead, a plugin definition consists of two parts:
1. A definition of the state and logic of our plugin that is exported under the name `plugin`: `export function plugin(client: PluginClient<Events, Methods>) { ... }`. Most of the state and all connection logic will move here. 1. A definition of the state and logic of the plugin that is exported under the name `plugin`: `export function plugin(client: PluginClient<Events, Methods>) { ... }`. Most of the state and all connection logic will move here.
2. A definition of the root of the UI is exported under the name `Component`: `export function Component() { ... }` 2. A definition of the root of the UI is exported under the name `Component`: `export function Component() { ... }`
There are a few conceptual changes that are important to understand, as they are different compared to classic plugins: There are a few conceptual changes that are important to understand, as they are different compared to classic plugins:
The `plugin` function is called exactly once when a plugin is set up for an application. This means that all state that is created inside the `plugin` definition will be kept as long as the app is connected, * The `plugin` function is called exactly once when a plugin is set up for an application. This means that all state that is created inside the `plugin` definition is kept as long as the app is connected, even when the user is navigating away.
even when the user is navigating away.
It used to be necessary to use `persistedState` for this kind of state, but that is no longer the case. It used to be necessary to use `persistedState` for this kind of state, but that is no longer the case.
In contrast, the `Component` component is mounted whenever the user _opens_ the plugin, so any state stored locally in that React component will be lost if the user navigates away. * In contrast, the `Component` component is mounted whenever the user _opens_ the plugin, so any state stored locally in that React component will be lost if the user navigates away. It's recommended avoiding this and, instead, store state (including selection) in the `plugin` definition, using the `createState` abstraction.
We recommend avoiding that, and store state, including selection, in the `plugin` definition instead, using the `createState` abstraction.
The relation between `plugin`, its parameter `client`, and how to use it in your `Component` definition is documented in detail in the [Plugin Declaration section](../tutorial/js-custom.mdx#the-plugin-declaration). The relation between `plugin`, its parameter `client`, and how to use it in your `Component` definition is documented in detail in the [Plugin Declaration section](../tutorial/js-custom.mdx#the-plugin-declaration). Please read it before continuing as it explains in detail how to manage state, handle receiving and sending data, and testing.
Please read it before continuing as it will explain how to manage state, handle receiving and sending data and testing in detail.
The full set of available APIs on `client` is documented on the [Desktop Plugin API](flipper-plugin.mdx#pluginclient) page. The full set of available APIs on `client` is documented in the [Desktop Plugin API](flipper-plugin.mdx#pluginclient) page.
This step is completed if the plugin follows the next `plugin` / `component` structure and is working again. Make sure to test extensively! This step is completed if the plugin follows the next `plugin` / `component` structure and is working again. Make sure to test extensively!
### Tips: ### Tips
* To quickly verify the plugin compiles, the simplest way is to keep `yarn tsc -w` running in the `desktop` folder. * To quickly verify the plugin compiles, the simplest way is to keep `yarn tsc -w` running in the `desktop` folder.
* Similarly `yarn watch` can be used to run the unit tests in watch mode. Use the `p` key to filter for your specific plugin if `jest` doesn't do so automatically. * Similarly `yarn watch` can be used to run the unit tests in watch mode. Use the `p` key to filter for your specific plugin if `jest` doesn't do so automatically.
* Example of migrating the network plugin to use Sandy APIs. D24108772 / [Github commit](https://github.com/facebook/flipper/commit/fdde2761ef054e44f399c846a2eae6baba03861e) * For an example of migrating the network plugin to use Sandy APIs, see diff [D24108772](https://www.internalfb.com/diff/D24108772) / [Github commit](https://github.com/facebook/flipper/commit/fdde2761ef054e44f399c846a2eae6baba03861e).
* Example of migrating the example plugin to use Sandy APIs. D22308265 / [Github commit](https://github.com/facebook/flipper/commit/babc88e472612c66901d21d289bd217ef28ee385#diff-a145be72bb13a4675dcc8cbac5e55abcd9a542cc92f5c781bd7d3749f13676fc) * For an example of migrating the example plugin to use Sandy APIs, see diff [D22308265](https://www.internalfb.com/diff/D22308265) / [Github commit](https://github.com/facebook/flipper/commit/babc88e472612c66901d21d289bd217ef28ee385#diff-a145be72bb13a4675dcc8cbac5e55abcd9a542cc92f5c781bd7d3749f13676fc).
* Other plugins that can be check for inspiration are the Logs and Network plugins. * Other plugins that can be checked for inspiration are Logs and Network plugins.
* These steps typically does not involve change much the UI or touch other files than `index.tsx`. Typically, the root component needs to be changed, but most other components can remain as is. However, if a ManagedTable is used (see the next section), it might be easier to already convert the table in this step. * These steps typically don't change the UI much or touch other files than `index.tsx`. Typically, the root component needs to be changed, but most other components can remain as is. However, if a ManagedTable is used (see the next section), it might be easier to already convert the table in this step.
* Sandy has first class support for unit testing your plugin and mocking device interactions. Please do set up unit tests per documentation linked above! * Sandy has first class support for unit testing your plugin and mocking device interactions. Please do set up unit tests per documentation linked above!
* If the original plugin definition contained `state`, it is recommended to create one new state atoms (`createState`) per field in the original `state`, rather than having one big atom. * If the original plugin definition contained `state`, it is recommended to create one new state atoms (`createState`) per field in the original `state`, rather than having one big atom.
* If the original plugin definition contained `persistedState`, it is recommended to create one new state atoms (`createState`) per field in the original `state`, rather than having one big atom. By setting the `persist` [option](flipper-plugin.mdx#options) of the state, you can make sure this piece of state becomes part of the import / export functionality of Flipper. Which is important if the data stored here is relevant for bug reports. * If the original plugin definition contained `persistedState`, it is recommended to create one new state atoms (`createState`) per field in the original `state`, rather than having one big atom. By setting the `persist` [option](flipper-plugin.mdx#options) of the state, you can make sure this piece of state becomes part of the import / export functionality of Flipper. Which is important if the data stored here is relevant for bug reports.
* For deeply nested state updates, using `state.update` is often simpler than using `state.set`, as it uses [immer](https://immerjs.github.io/immer/) under the hood to make immutable state updates straight forward. * For deeply nested state updates, using `state.update` is often simpler than using `state.set`, as it uses [Immer](https://immerjs.github.io/immer/) under the hood to make immutable state updates straight forward.
* For the same reason, you don't need to shallowly clone your state anymore, as long as `state.update` is used for state updates. * For the same reason, you don't need to shallowly clone your state anymore, as long as `state.update` is used for state updates.
* When dealing a lot with promises, using [`async` / `await`](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await) is usually simpler. * When dealing a lot with promises, using [`async` / `await`](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await) is usually simpler.
### Migration table ### Migration table
Some abstractions that used to be (for example) static methods on `FlipperPlugin` are now exposed from the `client` object: Some abstractions that used to be static methods on `FlipperPlugin` are now exposed from the `client` object. Following are a few examples:
| Old | New | | Old | New |
| --- | --- | | :-- | :-- |
| `persistedState` | Use `createState` and set the `persist` option | | `persistedState` | Use `createState` and set the `persist` option |
| `persistedStateReducer` | Create message handlers and update the state objects directly | | `persistedStateReducer` | Create message handlers and update the state objects directly |
| `exportPersistedState` | Use the `client.onExport` hook | | `exportPersistedState` | Use the `client.onExport` hook |
@@ -100,17 +96,16 @@ Some abstractions that used to be (for example) static methods on `FlipperPlugin
| `createTablePlugin` | TBD, so these conversions can be skipped for now | | `createTablePlugin` | TBD, so these conversions can be skipped for now |
| `init` | `client.onReady` | | `init` | `client.onReady` |
## Using Sandy / Ant.design to organise the plugin UI ## Using Sandy / Ant.design to organise the plugin UI
<FbInternalOnly> <FbInternalOnly>
For every plugin, we generated a tasks on our [Sandy Plugin Migration](https://www.internalfb.com/tasks?folder_filters&q=1341478626215302&group_by_type=MANUAL) dashboard. Completing this step corresponds to completing the `[flipper][sandy] convert plugin 'xxxx's UI to use ant.design` task. For every plugin, a task is generated on the [Sandy Plugin Migration](https://www.internalfb.com/tasks?folder_filters&q=1341478626215302&group_by_type=MANUAL) dashboard. Taking this step corresponds to completing the `[flipper][sandy] convert plugin 'xxxx's UI to use ant.design` task.
If you start this task, please assign yourself as owner and link it in the diff. If you start this task, please assign yourself as owner and link it in the diff.
</FbInternalOnly> </FbInternalOnly>
The goal of this step is to update the UI of the plugin to use Sandy / Ant.design components. The goal of this step is to update the UI of the plugin to use Sandy / Ant Design components.
These will provide a more consistent user experience, usually provide better UX and they support dark mode! These will provide a more consistent user experience, usually provide better UX and they support dark mode!
Roughly speaking this typically involves replacing all imported components with their modern counterpart. Roughly speaking this typically involves replacing all imported components with their modern counterpart.
@@ -118,28 +113,32 @@ For Sandy plugins, components can be found here:
* Interactive data displaying components are exposed from `flipper-plugin`: `DataTable` (for tables), `DataInspector` (for JSON trees) and `ElementInspector` (for element trees). * Interactive data displaying components are exposed from `flipper-plugin`: `DataTable` (for tables), `DataInspector` (for JSON trees) and `ElementInspector` (for element trees).
* `flipper-plugin` also provides the primitives to organise the `Layout` of the plugin. * `flipper-plugin` also provides the primitives to organise the `Layout` of the plugin.
* Practically all other, more generic components are provided by [ant.design](https://ant.design/components/overview/), a proven mature open source component library, which is much richer than the components that are offered from `flipper`. * Practically all other, more generic components are provided by [Ant Design](https://ant.design/components/overview/), a proven mature open source component library, which is much richer than the components that are offered from `flipper`.
In Sandy, the layout is typically build by using a combination of `Layout.Top` (or `.Right`, `.Left`, `.Bottom`), which divides all available space in a fixed and dynamic section, `Layout.Scrollable`, which takes all available space and provides scrollbars if its content is still greater, and `Layout.Container` which organizes paddings, borders and spacing between elements etc. In Sandy, the layout is typically built by using a combination of the following:
We generally recommend against using `margins`; use padding and `gap` instead. * `Layout.Top` (or `.Right`, `.Left`, `.Bottom`), which divides all available space in a fixed and dynamic section
Ideally, use `theme.spacing` to get standard numbers for margins and paddings instead of hard-coded numbers. * `Layout.Scrollable`, which takes all available space and provides scrollbars if its content is still greater,
This will help with achieving consistency in look and feel. * `Layout.Container` which organizes paddings, borders and spacing between elements etc.
Generally, it's recommended not to use `margins`; use padding and `gap` instead.
Ideally, use `theme.spacing` to get standard numbers for margins and paddings instead of hard-coded numbers. This will help with achieving consistency in look and feel.
### Design resources ### Design resources
There are three important resources to check for documentation on the components available: There are three important resources to check for documentation on the components available:
1. [Flipper style guide - a general overview of the Flipper design system](style-guide.mdx). It will demonstrate colors, typography and creating layouts including some examples. 1. [Flipper style guide](style-guide.mdx) - a general overview of the Flipper design system that demonstrates colors, typography and creating layouts including some examples.
2. The [ant.design component overview](https://ant.design/components/overview/) 2. [Ant Design component overview](https://ant.design/components/overview/)
3. The [API reference](flipper-plugin.mdx#ui-components) documentation for the components provided by `flipper-plugin` 3. [API reference](flipper-plugin.mdx#ui-components) documentation for the components provided by `flipper-plugin`
### Old and new components ### Old and new components
For conversion, the following table maps the old components to the new ones: For conversion, the following table maps the old components to the new ones:
| Old `flipper` component | New component | Providing package | Notes | | Old `flipper` component | New component | Providing package | Notes |
| --- | --- | --- | --- | | :-- | :-- | :-- | :-- |
| `DetailsSidebar` | `DetailsSidebar` | `flipper-plugin` | as-is | | `DetailsSidebar` | `DetailsSidebar` | `flipper-plugin` | as-is |
| `Sidebar` | `Layout.Top` (or `.Right`, `.Left`, `.Bottom`) | `flipper-plugin` | Set the `resizable` flag to allow the user to resize the pane. | | | `Sidebar` | `Layout.Top` (or `.Right`, `.Left`, `.Bottom`) | `flipper-plugin` | Set the `resizable` flag to allow the user to resize the pane. | |
| `FlexColumn` / `Pane` / `View` | `Layout.Container` | `flipper-plugin` | Use the `gap` property to provide some spacing between the children!| | `FlexColumn` / `Pane` / `View` | `Layout.Container` | `flipper-plugin` | Use the `gap` property to provide some spacing between the children!|
@@ -155,22 +154,23 @@ For conversion, the following table maps the old components to the new ones:
| `Panel` | `Panel` | `flipper-plugin` || | `Panel` | `Panel` | `flipper-plugin` ||
| `Tabs` / `Tab` | `Tabs` / `Tab` | `flipper-plugin | Note that `Tab`'s `title` property is now called `tab`. | | `Tabs` / `Tab` | `Tabs` / `Tab` | `flipper-plugin | Note that `Tab`'s `title` property is now called `tab`. |
Most other components, like `select` elements, tabs, date-pickers, etc etc can all be found in the Ant documentaiton. Most other components, such as `select` elements, tabs, and date-pickers can all be found in the Ant documentation.
### Theming & custom styled components ### Theming & custom styled components
Creating your own components / styling using `styled` is still supported. Creating your own components / styling using `styled` is still supported.
But ideally, you should need custom styled components a lot less! But ideally, you should need custom styled components a lot less!
Since Sandy plugins are expected to support dark mode, (use the settings pane to quickly toggle), we recommend to not use hard-coded colors, but instead use one of the semantic colors that are provided through the `theme` object that can be imported from `flipper-plugin`. Since Sandy plugins are expected to support dark mode, (use the settings pane to quickly toggle), it's recommended not to use hard-coded colors. Instead, use one of the semantic colors that are provided through the `theme` object that can be imported from `flipper-plugin`.
Ideally there should be no hard-coded colors anymore either, and little need to use `width: 100%` / `height: 100%` anywhere, as needing those typically signals a layout issue. Ideally, there should be no hard-coded colors anymore either, and little need to use `width: 100%` / `height: 100%` anywhere, as needing those typically signals a layout issue.
Tip: it is recommended to keep components as much as possible outside the entry file, as components defined outside the index.tsx file will benefit from fast refresh. :::note Tip
It's recommended to keep components as much as possible outside the entry file, as components defined outside the index.tsx file will benefit from fast refresh.
:::
### Wrapping up ### Wrapping up
This step of the process is completed as soon as there are no imports from the `flipper` package anymore. This step of the process is completed as soon as there are no imports from the `flipper` package anymore. Don't forget to remove `flipper` from the `peerDependencies` in the `package.json` section if present.
Don't forget to remove `flipper` from the `peerDependencies` in the `package.json` section if present.
Feel free to reach out to the Flipper team for any questions! If you have any questions, feel free to reach out to the [Flipper Support](https://fb.workplace.com/groups/flippersupport) Workplace group.