diff --git a/docs/extending/create-plugin.mdx b/docs/extending/create-plugin.mdx index ad82039f1..42d58b679 100644 --- a/docs/extending/create-plugin.mdx +++ b/docs/extending/create-plugin.mdx @@ -9,7 +9,7 @@ import {FbInternalOnly, OssOnly} from 'internaldocs-fb-helpers'; ## FlipperPlugin -The plugin implementation that runs on the (mobile) application side of things is called the _client plugin_ in Flipper terminology. +The plugin implementation that runs on the (mobile) application side of things is called the _client plugin_ in Flipper terminology. To build a client plugin, implement the `FlipperPlugin` interface. The ID that is returned from your implementation needs to match the `name` defined in your JavaScript counterpart's `package.json`. @@ -47,7 +47,7 @@ public class MyFlipperPlugin implements FlipperPlugin { -```objective-c +```objc @interface MyFlipperPlugin : NSObject @end @@ -127,7 +127,7 @@ connection.receive("getData", new FlipperReceiver() { -```objective-c +```objc @interface MyFlipperPlugin : NSObject @end @@ -204,7 +204,7 @@ connection.send("MyMessage", -```objective-c +```objc [connection send:@"getData" withParams:@{@"message":@"hello"}]; ``` @@ -275,7 +275,7 @@ if (client != null) { -```objective-c +```objc FlipperClient *client = [FlipperClient sharedClient]; MyFlipperPlugin *myPlugin = [client pluginWithIdentifier:@"MyFlipperPlugin"]; [myPlugin sendData:myData]; diff --git a/docs/extending/create-table-plugin.mdx b/docs/extending/create-table-plugin.mdx index 88b7b0274..86c0b8efc 100644 --- a/docs/extending/create-table-plugin.mdx +++ b/docs/extending/create-table-plugin.mdx @@ -18,7 +18,7 @@ Below is a sample implementation of a desktop plugin based on `createTablePlugin See "[Create Plugin](create-plugin)" for how to create the native counterpart for your plugin. -```javascript +```tsx import {ManagedDataInspector, Panel, Text, createTablePlugin} from 'flipper'; type Id = string; diff --git a/docs/extending/desktop-plugin-structure.mdx b/docs/extending/desktop-plugin-structure.mdx index 383283b35..802b84d7e 100644 --- a/docs/extending/desktop-plugin-structure.mdx +++ b/docs/extending/desktop-plugin-structure.mdx @@ -122,7 +122,7 @@ Flipper Desktop plugins come in three possible flavors: A plugin always exposes two elements from its entry module (typically `src/index.tsx`): `plugin` and `Component`: -```typescript +```tsx import {PluginClient} from 'flipper-plugin'; export function plugin(client: PluginClient) { @@ -143,7 +143,7 @@ Flipper also supports so-called device plugins - plugins that are available for so are a bit more limited in general. Their entry module anatomy is: -```typescript +```tsx import {DevicePluginClient} from 'flipper-plugin'; export function devicePlugin(client: DevicePluginClient) { diff --git a/docs/extending/establishing-a-connection.mdx b/docs/extending/establishing-a-connection.mdx index 064e7d25c..833f3618b 100644 --- a/docs/extending/establishing-a-connection.mdx +++ b/docs/extending/establishing-a-connection.mdx @@ -49,7 +49,7 @@ localhost:8089/sonar?os={OS} ``` On that connection, send the following payload: -``` +```js Request = { "method": "signCertificate", "csr": string, @@ -59,13 +59,13 @@ Request = { ``` Where `csr` is a Certificate Signing Request the client has generated, and `destination` identifies a location accessible to both the client and Flipper desktop, where the certificate should be placed. -The Subject Common Name (CN=...) must be included in the CSR, and your `CertificateProvider` implementation in Flipper may use this in combination with the `destination` to determine where to put the certificate. +The Subject Common Name (CN=...) must be included in the CSR, and your `CertificateProvider` implementation in Flipper may use this in combination with the `destination` to determine where to put the certificate. This will ask Flipper desktop to generate a client certificate, using the CSR provided, and put it into the specified `destination`. Depending on the client, `destination` can have a different meaning. A basic example would be a file path, that both the desktop and the client have access to. With this Flipper desktop could write the certificate to that path. A more involved example is that of the Android Client, where destination specifies a relative path inside an app container. And the Subject Common Name determines which app container. Together these two pieces of information form an absolute file path inside an android device. -For Flipper desktop to work with a given Client type, it needs to be modified to know how to correctly interpret the `destination` argument, and deploy certificates to it. +For Flipper desktop to work with a given Client type, it needs to be modified to know how to correctly interpret the `destination` argument, and deploy certificates to it. `destination` field may not be relevant if your `medium` value is more than 1. `medium=1`(default) means Flipper should do certificate exchange by directly putting certificates at `destination` in the sandbox of the app. `medium=2` means Flipper will use Certificate Uploader and Provider to upload certificates and download it on the client side respectively. diff --git a/docs/extending/new-clients.mdx b/docs/extending/new-clients.mdx index d482c787a..aad4c85d2 100644 --- a/docs/extending/new-clients.mdx +++ b/docs/extending/new-clients.mdx @@ -58,7 +58,7 @@ The syntax used for these type definitions is [Flow](https://flow.org/en/docs/ty ### getPlugins Return the available plugins as a list of identifiers. A plugin identifier is a string which is matched with the plugin identifier of desktop javascript plugins. This allows the client to specify the plugins it supports. -``` +```js Request = { "method": "getPlugins", } @@ -75,7 +75,7 @@ Response = { Returns a subset of the available plugins returned by `getPlugin`. The background connections will automatically receive a connection from Flipper once it starts (and if the plugins are enabled), rather than waiting for the user to open the plugin. -``` +```js Request = { "method": "getBackgroundPlugins", } @@ -89,7 +89,7 @@ Response = { ### init Initialize a plugin. This should result in an onConnected call on the appropriate plugin. Plugins should by nature be lazy and should not be initialized up front as this may incur significant cost. The Flipper desktop client knows when a plugin is needed and should control when to initialize them. -``` +```js Request = { "method": "init", "params": { @@ -100,7 +100,7 @@ Request = { ### deinit Opposite of init. A call to deinit is made when a plugin is no longer needed and should release any resources. Don't rely only on deinit to release plugin resources as Flipper may quit without having the chance to issue a deinit call. In those cases, you should also rely on the RSocket disconnect callbacks. This call is mainly for allowing the desktop app to control the lifecycle of plugins. -``` +```js Request = { "method": "deinit", "params": { @@ -115,7 +115,7 @@ request.params.api is the plugin id. request.params.method is the method within the plugin to execute. request.params.params is an optional params object containing the parameters to the RPC invocation. -``` +```js Request = { "method": "execute", "params": { @@ -137,7 +137,7 @@ Response = { The Flipper desktop app handles error reporting so you don't have to. If an error occurs during the execution of an RPC invocation, return a serialization of it in the response so it can be attributed to the method call. If an error occurs in some other context, you can proactively send it to Flipper with the following request structure: -``` +```js Request = { error: { message: string, @@ -150,7 +150,7 @@ While in development mode, Flipper will display any client errors next to javasc ## Testing Testing is incredibly important when building core infrastructure and tools. The following is pseudo code for tests we would expect any new FlipperClient implementation to implement and correctly execute. To run tests we strongly encourage you to build a mock for the RSocket connection to mock out the desktop side of the protocol and to not have any network dependencies in your test code. -``` +```js test("GetPlugins", { let connection = new MockConnection(); let client = new FlipperClient(connection); @@ -172,7 +172,7 @@ test("GetPlugins", { })); }); ``` -``` +```js test("InitDeinit", { let connection = new MockConnection(); let client = new FlipperClient(connection); @@ -204,7 +204,7 @@ test("InitDeinit", { assertFalse(plugin.connected); }); ``` -``` +```js test("Disconnect", { let connection = new MockConnection(); let client = new FlipperClient(connection); @@ -228,7 +228,7 @@ test("Disconnect", { assertFalse(plugin.connected); }); ``` -``` +```js test("Execute", { let connection = new MockConnection(); let client = new FlipperClient(connection); diff --git a/docs/extending/plugin-distribution.mdx b/docs/extending/plugin-distribution.mdx index 1b5054ba6..dffb90219 100644 --- a/docs/extending/plugin-distribution.mdx +++ b/docs/extending/plugin-distribution.mdx @@ -13,7 +13,7 @@ Flipper plugins are essentially standard npm packages. So you can publish them b 1. `package.json` and code [must follow the Flipper plugin specification](desktop-plugin-structure#plugin-definition) 2. code must be bundled using "flipper-pkg" before packing or publishing. This can be done by executing `flipper-pkg bundle` on `prepack` step: - ``` + ```json { ... "devDependencies": { diff --git a/docs/extending/search-and-filter.mdx b/docs/extending/search-and-filter.mdx index 0b46db8a9..b8b42dbe0 100644 --- a/docs/extending/search-and-filter.mdx +++ b/docs/extending/search-and-filter.mdx @@ -31,7 +31,7 @@ The list of filters that are currently applied. ### Example -``` +```tsx import type {SearchableProps} from 'flipper'; import {Searchable} from 'flipper'; diff --git a/docs/extending/styling-components.mdx b/docs/extending/styling-components.mdx index 861ce5451..dfc789336 100644 --- a/docs/extending/styling-components.mdx +++ b/docs/extending/styling-components.mdx @@ -10,17 +10,17 @@ Flipper ships with its own design system which is based on [Ant Design](https:// In general, custom styling should be needed rarily, as Ant Design provides a very extensive set of [components](https://ant.design/components/overview/). To build plugin layout and data visualization Flipper ships with an additional set of components through the `flipper-plugin` package. -The list of available additional compoents can be found in the API Reference and are further documented +The list of available additional components can be found in the API Reference and are further documented in the Flipper Style Guide which can be found in Flipper under `View > Flipper style guide`. -In case you still need customly styled components, +In case you still need customly styled components, we are using [emotion](https://emotion.sh) to style our components. For more details on how this works, please refer to emotion's documentation. We heavily use their [Styled Components](https://emotion.sh/docs/styled) approach, which allows you to extend our and Ant's built-in components. ## Basic tags For basic building blocks (views, texts, ...) you can use the styled object. -```javascript +```js import {styled} from 'flipper-plugin'; const MyView = styled.div({ @@ -34,10 +34,10 @@ const MyInput = styled.input({ ... }); ## Extending Flipper Components -In some cases it is required to customize Ant or Flipper's components in some way. For example changing colors, alignment, or wrapping behavior. +In some cases it is required to customize Ant or Flipper's components in some way. For example changing colors, alignment, or wrapping behavior. Flippers components can be wrapped using the `styled` function which allows adding or overwriting existing style rules. -```javascript +```jsx import {Layout, styled} from 'flipper-plugin'; const Container = styled(Layout.Container)({ @@ -55,7 +55,7 @@ The CSS-in-JS object passed to the styled components takes just any CSS rule, wi The style object can also be returned from a function for dynamic values. Props can be passed to the styled component using React. -```javascript +```jsx const MyView = styled.div( props => ({ fontSize: 10, @@ -81,7 +81,7 @@ Children can be matched by using normal CSS selectors. This makes it possible to ## Colors -The `theme` module contains all standard colors used by Flipper. All available colors can be previewed by starting Flipper and opening `View > Flipper Style Guide`. +The `theme` module contains all standard colors used by Flipper. All available colors can be previewed by starting Flipper and opening `View > Flipper Style Guide`. The colors exposed here will handle dark mode automatically, so it is recommended to use those colors over hardcoded ones. ```javascript diff --git a/docs/extending/supporting-layout.mdx b/docs/extending/supporting-layout.mdx index 55ee985af..59da8cb0a 100644 --- a/docs/extending/supporting-layout.mdx +++ b/docs/extending/supporting-layout.mdx @@ -8,7 +8,7 @@ To enable the Flipper layout inspector on a new platform, just implement a clien Note that we're using [Flow](https://flow.org/en/docs/types/objects/) syntax to specify this JSON API. ### Node -``` +```ts type NodeId = string; type InspectorValue = { @@ -45,7 +45,7 @@ InspectorValue can also be used to change the parsed type of the value, such as ### Plugin Interface -``` +```ts interface ClientLayoutPlugin { Node getRoot(); GetNodesResponse getNodes({ids: Array}); diff --git a/docs/extending/testing.mdx b/docs/extending/testing.mdx index f98200367..e01fd45ff 100644 --- a/docs/extending/testing.mdx +++ b/docs/extending/testing.mdx @@ -16,9 +16,9 @@ Developer tools are only used if they work. We have built APIs to test plugins. Flipper uses [Jest](https://jestjs.io/) as unit testing framework. -Writing unit tests for Flipper Desktop plugins is covered in detail in the [tutorial](../../docs/tutorial/js-custom#testing-plugin-logic). +Writing unit tests for Flipper Desktop plugins is covered in detail in the [tutorial](../../docs/tutorial/js-custom#testing-plugin-logic). -The `flipper-plugin` package provide several [test utilities](../../docs/extending/flipper-plugin#testutils) to make testing more convenient. +The `flipper-plugin` package provide several [test utilities](../../docs/extending/flipper-plugin#testutils) to make testing more convenient. ## Client plugins @@ -76,7 +76,7 @@ public void myTest() { Start by creating your first test file in this directory `MyFlipperPluginTests.cpp` and import the testing utilities from `fbsource//xplat/sonar/xplat:FlipperTestLib`. These utilities mock out core pieces of the communication channel so that you can test your plugin in isolation. -``` +```objc #include #include #include @@ -99,7 +99,7 @@ TEST(MyFlipperPluginTests, testDummy) { Here is a simple test using these mock utilities to create a plugin, send some data, and assert that the result is as expected. -``` +```objc TEST(MyFlipperPluginTests, testDummy) { std::vector successfulResponses; auto responder = std::make_unique(&successfulResponses); diff --git a/docs/getting-started/ios-native.mdx b/docs/getting-started/ios-native.mdx index 40911754b..446fda76b 100644 --- a/docs/getting-started/ios-native.mdx +++ b/docs/getting-started/ios-native.mdx @@ -91,7 +91,7 @@ AppDelegate. -```objective-c +```objc #import #import #import diff --git a/docs/getting-started/react-native-ios.mdx b/docs/getting-started/react-native-ios.mdx index bbbba6317..6984cb9f9 100644 --- a/docs/getting-started/react-native-ios.mdx +++ b/docs/getting-started/react-native-ios.mdx @@ -107,7 +107,7 @@ The code below enables the following integrations: -```objective-c +```objc ... #if DEBUG #ifdef FB_SONARKIT_ENABLED diff --git a/docs/setup/layout-plugin.mdx b/docs/setup/layout-plugin.mdx index 7ba3d28a4..7f314c599 100644 --- a/docs/setup/layout-plugin.mdx +++ b/docs/setup/layout-plugin.mdx @@ -94,7 +94,7 @@ Once you have added the pod, initialise the plugin and add it to the `FlipperCli -```objective-c +```objc #import SKDescriptorMapper *mapper = [[SKDescriptorMapper alloc] initWithDefaults]; @@ -120,13 +120,13 @@ If you want to enable [ComponentKit support](https://github.com/facebook/compone ```ruby pod 'FlipperKit/FlipperKitLayoutComponentKitSupport', '~>' + flipperkit_version -``` +``` Once you have added the pod you will then need to augment the descriptor with Componentkit-specific settings as shown below. -```objective-c +```objc #import #import diff --git a/docs/setup/network-plugin.mdx b/docs/setup/network-plugin.mdx index c2004b16b..f47887e51 100644 --- a/docs/setup/network-plugin.mdx +++ b/docs/setup/network-plugin.mdx @@ -56,7 +56,7 @@ Initialise the plugin in the following way: -```objective-c +```objc #import [[FlipperClient sharedClient] addPlugin: [[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; diff --git a/docs/troubleshooting.mdx b/docs/troubleshooting.mdx index 7335abd67..9ccfff3a0 100644 --- a/docs/troubleshooting.mdx +++ b/docs/troubleshooting.mdx @@ -232,7 +232,7 @@ If you experience errors such as `Undefined symbol: associated type descriptor f Comment out the relevant lines in `ios/Podfile` and run `cd ios && pod install` again: -``` +```ruby # use_flipper! # post_install do |installer| # flipper_post_install(installer) @@ -243,7 +243,7 @@ Comment out the relevant lines in `ios/Podfile` and run `cd ios && pod install` To speed up CI builds, Flipper can be disabled on CI environments by making the Flipper SDK inclusion conditional (this works on most CI providers, feel free to customize the environment variable): -``` +```ruby if !ENV['CI'] use_flipper! post_install do |installer| diff --git a/docs/tutorial/js-custom.mdx b/docs/tutorial/js-custom.mdx index f4c78c7f4..9df2687d8 100644 --- a/docs/tutorial/js-custom.mdx +++ b/docs/tutorial/js-custom.mdx @@ -13,7 +13,7 @@ Displaying your data in a table might work for many use-cases. However, dependin For our sea mammals app, we might not only want to see them listed as image URLs in a table but render the actual images in nice little cards. When selecting one of the cards we still want to display all details in the sidebar. Custom cards UI for our sea mammals plugin -Currently, the default export in our `index.tsx` is from `createTablePlugin`. +Currently, the default export in our `index.tsx` is from `createTablePlugin`. Now we are going to replace this with a custom React component by using the more flexible APIs exposed by `flipper-plugin` . So first let's add `flipper-plugin` as dependency: `yarn add --peer flipper-plugin antd && yarn add --dev flipper-plugin antd`. @@ -21,7 +21,7 @@ After that, we replace our `createTablePlugin` with a `plugin` definition, and a Separating those two concepts helps with testing and maintaining state when the user switches plugins. -```typescript +```tsx import React from 'react'; import {PluginClient, createState} from 'flipper-plugin'; @@ -71,13 +71,13 @@ export function Component() { ## The `plugin` declaration The implementation of our plugin is driven by the named, exported function `plugin` as defined at `(3)`. -The `plugin` method is called upon instantiating the plugin. -The `plugin` method receives one argument, the `client`, which provides all APIs needed to both interact with Flipper desktop, -and the plugin loaded into the client application. -The `PluginClient` types all available APIs and takes two generic arguments. +The `plugin` method is called upon instantiating the plugin. +The `plugin` method receives one argument, the `client`, which provides all APIs needed to both interact with Flipper desktop, +and the plugin loaded into the client application. +The `PluginClient` types all available APIs and takes two generic arguments. -The first, `Events`, describes all possible events that can be sent from the client plugin to the desktop plugin, -and determines the events available for `client.onMessage` (see below). +The first, `Events`, describes all possible events that can be sent from the client plugin to the desktop plugin, +and determines the events available for `client.onMessage` (see below). In our example, only one event can occur, `newRow`, as defined at `(2)`. But typically there are more. The data provided by this `newRow` event is described with the `Row` type, as defined at `(3)`. @@ -91,31 +91,31 @@ In this case, we return the state atoms `rows` and `selectedID`, and expose the Since the `plugin` function will execute only once during the entire life-cycle of the plugin, we can use local variables in the function body to preserve state. In our example, we create two pieces of state, the set of rows available, `rows`, and the current selection: `selectionID`. See `(5)`. -It is possible to store state directly in `let` declarations, but `createState` creates a storage container that gives us a few advantages. +It is possible to store state directly in `let` declarations, but `createState` creates a storage container that gives us a few advantages. Most importantly, state created using `createState` can be subscribed to by our UI components using the `useValue` hook. -Secondly, state created with `createState` can be made part of Flipper imports / exports. -We can enable this feature by providing a unique `persist` key. +Secondly, state created with `createState` can be made part of Flipper imports / exports. +We can enable this feature by providing a unique `persist` key. The current value of a the container can be read using `.get()`, and `.set()` or `.update()` can be used to replace the current value. -The `client` can be used to receive and send information to the client plugin. +The `client` can be used to receive and send information to the client plugin. With `client.send`, we can invoke methods on the plugin. With `client.onMessage` (`(6)`) we can subscribe to the specific events as specified with the `Events` type (`(2)`). -In the event handler, we can update some pieces of state, using the `.set` method to replace state, or the `.update` method to immutably update the state using [immer](https://immerjs.github.io/immer). +In the event handler, we can update some pieces of state, using the `.set` method to replace state, or the `.update` method to immutably update the state using [immer](https://immerjs.github.io/immer). In this case, we add the received row to the `rows` state under its own `id`. Finally, `(7)`, we create (and expose at `(4)`) a utility to update the selection, which we will user later in our UI. -Note that no state should be stored outside the `plugin` definition; multiple invocations of `plugin` can be 'alive' if multiple connected apps are using the plugin. +Note that no state should be stored outside the `plugin` definition; multiple invocations of `plugin` can be 'alive' if multiple connected apps are using the plugin. Storing the state inside the closure makes sure no state is mixed up. ### Testing `plugin` logic -Before we create the UI for our plugin, we are going to pretend that we always write unit tests first. -Unit tests will be picked automatically by Jest if they are named like `__tests__/*.spec.tsx`, so we create a file called `__tests__/seamammals.spec.tsx` and start the test runner by +Before we create the UI for our plugin, we are going to pretend that we always write unit tests first. +Unit tests will be picked automatically by Jest if they are named like `__tests__/*.spec.tsx`, so we create a file called `__tests__/seamammals.spec.tsx` and start the test runner by running `yarn test --watch` in our plugin root. Here is our initial unit test: -```typescript +```ts // (1) import {TestUtils} from 'flipper-plugin'; // (2) @@ -159,11 +159,11 @@ test('It can store rows', () => { ``` Testing utilities for plugins are shipped as part of `flipper-plugin`, so we import them (`(1)`). -Secondly, we directly import our above plugin implementation into our unit test. +Secondly, we directly import our above plugin implementation into our unit test. Using `as`, we put the entire implementation into one object, which is the format in which our utilities expect them (`(2)`). Using `TestUtils.startPlugin` (`(3)`) we can instantiate our plugin in a fully mocked environment, -in which our plugin can do everything except for actually rendering, which makes this operation really cheap. +in which our plugin can do everything except for actually rendering, which makes this operation really cheap. From the `startPlugin`, we get back an `instance`, which corresponds to the object we returned from our `plugin` implementation (`(4)` in our previous listing). Beyond that, we get a bunch of utilities to interact with our plugin. The full list is documented [here](../extending/flipper-plugin#the-test-runner-object), but for this test we are only interested in `sendEvent`. @@ -178,13 +178,13 @@ The assertions are provided by [Jest](https://jestjs.io/), and `toMatchInlineSna _Note: For now, the plugin implementation as shown here uses the old Flipper component library `flipper`, expect nicer components in the future as part of `flipper-plugin`._ -So far, in `index.tsx`, our `Component` didn't do anything useful yet. Time to build some nice UI. +So far, in `index.tsx`, our `Component` didn't do anything useful yet. Time to build some nice UI. Flipper leverages Ant design, so any [official Ant component](https://ant.design/components/overview/) can be used in Flipper plugins. -The styling system used by Flipper can be found by starting Flipper, and opening `View > Flipper Style Guide`. +The styling system used by Flipper can be found by starting Flipper, and opening `View > Flipper Style Guide`. The different `Layout` elements are documented there as well. -```typescript +```tsx import React, {memo} from 'react'; import {Typography, Card} from 'antd'; import { @@ -240,12 +240,12 @@ function renderSidebar(row: Row) { } ``` -A plugin module can have many components, but it should always export one component named `Component` that is used as the root component for the plugin rendering. +A plugin module can have many components, but it should always export one component named `Component` that is used as the root component for the plugin rendering. The component mustn't take any props, and will be mounted by Flipper when the user selects the plugin (`(1)`). -Inside the component we can grab the relevant instance of the plugin by using the `usePlugin` (`(2)`) hook. +Inside the component we can grab the relevant instance of the plugin by using the `usePlugin` (`(2)`) hook. This returns the instance API we returned in the first listing at the end of the `plugin` function. -Our original `plugin` definition is passed to the `usePlugin` as argument. +Our original `plugin` definition is passed to the `usePlugin` as argument. This is done to get the typings of `instance` correct and should always be done. With the `useValue` hook (`(3)`), we can grab the current value from the states we created earlier using `createState`. @@ -254,7 +254,7 @@ The benefit of `useValue(instance.rows)` over using `rows.get()`, is that the fi Since both `usePlugin` and `useValue` are hooks, they usual React rules for them apply; they need to be called unconditionally. So it is recommended to put them at the top of your component body. Both hooks can not only be used in the root `Component`, but also in any other component in your plugin component tree. -So it is not necessary to grab all the data at the root, or pass down the `instance` to all child components. +So it is not necessary to grab all the data at the root, or pass down the `instance` to all child components. Finally (`(4)`) we render the data we have. The details have been left out here, as from here it is just idiomatic React code. The source of the other `MammalCard` component can be found [here](https://github.com/facebook/flipper/blob/master/desktop/plugins/seamammals/src/index.tsx#L113-L165). @@ -264,7 +264,7 @@ The source of the other `MammalCard` component can be found [here](https://githu At this moment the plugin is ready to be used in Flipper, and opening it should lead to sane results. But let's verify with some tests that the UI works correctly, and doesn't regress in the future by adding another unit test to the `seamammals.spec.tsx` file and assert that the rendering is correct and interactive: -```typescript +```ts test('It can have selection and render details', async () => { // (1) const { @@ -336,18 +336,18 @@ test('It can have selection and render details', async () => { }); ``` -Like in our previous test, we use `TestUtils` to start our plugin. -But rather than using `startPlugin`, we now use `renderPlugin`. +Like in our previous test, we use `TestUtils` to start our plugin. +But rather than using `startPlugin`, we now use `renderPlugin`. Which does the same but also renders the component in memory, using [react-testing-library](https://testing-library.com/docs/react-testing-library/intro). The `renderer` returned by `startPlugin` allows us to interact with the DOM. Like in the previous test, we start by sending some events to the plugin (`(2)`). -After that (`(3)`), our new data should be reflected in the dom. -Since we used `({ method: 'newRow', columns, diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 978f194a1..5c8804c28 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -144,7 +144,7 @@ const siteConfig = { }, }), prism: { - additionalLanguages: ['groovy', 'java', 'kotlin', 'ruby', 'swift'], + additionalLanguages: ['groovy', 'java', 'kotlin', 'ruby', 'swift', 'objectivec'], }, }, favicon: 'img/icon.png',