From 573518e48d1f4208476f269d9e8f00a4b510b4f8 Mon Sep 17 00:00:00 2001
From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com>
Date: Tue, 22 Aug 2023 14:40:38 +0200
Subject: [PATCH] Integrations - frontend adjustments (#4527)
## About the changes
- [x] Create a feature flag
- [x] Rename page title
- [x] Rename menu item
- [x] Update frontend URL (add redirect from old one)
https://linear.app/unleash/issue/1-1263/integrations-frontend-adjustments
---
.../component/addons/AddonList/AddonList.tsx | 19 --------
.../AddonRedirect/AddonRedirect.tsx | 19 ++++++++
.../CreateIntegration/CreateIntegration.tsx} | 6 +--
.../EditIntegration/EditIntegration.tsx} | 8 ++--
.../IntegrationForm.styles.tsx} | 0
.../IntegrationForm/IntegrationForm.tsx} | 20 ++++-----
.../IntegrationInstall.tsx} | 4 +-
.../IntegrationMultiSelector.test.tsx} | 25 +++++++----
.../IntegrationMultiSelector.tsx} | 12 +++---
.../IntegrationParameter.tsx} | 8 ++--
.../IntegrationParameters.tsx} | 22 +++++-----
.../AvailableAddons/AvailableAddons.tsx | 18 +++++---
.../ConfigureAddonsButton.tsx} | 9 ++--
.../AvailableIntegrations.tsx | 8 ++++
.../ConfiguredAddons/ConfiguredAddons.tsx | 13 ++++--
.../ConfiguredAddonsActionsCell.tsx | 3 ++
.../IntegrationIcon/IntegrationIcon.tsx} | 4 +-
.../IntegrationList/IntegrationList.tsx | 28 ++++++++++++
.../IntegrationNameCell.tsx} | 9 +++-
.../__snapshots__/routes.test.tsx.snap | 28 ++++++++++++
frontend/src/component/menu/routes.ts | 43 ++++++++++++++++---
frontend/src/interfaces/uiConfig.ts | 1 +
.../__snapshots__/create-config.test.ts.snap | 2 +
src/lib/types/experimental.ts | 7 ++-
website/README.md | 3 +-
25 files changed, 225 insertions(+), 94 deletions(-)
delete mode 100644 frontend/src/component/addons/AddonList/AddonList.tsx
create mode 100644 frontend/src/component/integrations/AddonRedirect/AddonRedirect.tsx
rename frontend/src/component/{addons/CreateAddon/CreateAddon.tsx => integrations/CreateIntegration/CreateIntegration.tsx} (87%)
rename frontend/src/component/{addons/EditAddon/EditAddon.tsx => integrations/EditIntegration/EditIntegration.tsx} (78%)
rename frontend/src/component/{addons/AddonForm/AddonForm.styles.tsx => integrations/IntegrationForm/IntegrationForm.styles.tsx} (100%)
rename frontend/src/component/{addons/AddonForm/AddonForm.tsx => integrations/IntegrationForm/IntegrationForm.tsx} (95%)
rename frontend/src/component/{addons/AddonForm/AddonInstall/AddonInstall.tsx => integrations/IntegrationForm/IntegrationInstall/IntegrationInstall.tsx} (93%)
rename frontend/src/component/{addons/AddonForm/AddonMultiSelector/AddonMultiSelector.test.tsx => integrations/IntegrationForm/IntegrationMultiSelector/IntegrationMultiSelector.test.tsx} (88%)
rename frontend/src/component/{addons/AddonForm/AddonMultiSelector/AddonMultiSelector.tsx => integrations/IntegrationForm/IntegrationMultiSelector/IntegrationMultiSelector.tsx} (95%)
rename frontend/src/component/{addons/AddonForm/AddonParameters/AddonParameter/AddonParameter.tsx => integrations/IntegrationForm/IntegrationParameters/IntegrationParameter/IntegrationParameter.tsx} (90%)
rename frontend/src/component/{addons/AddonForm/AddonParameters/AddonParameters.tsx => integrations/IntegrationForm/IntegrationParameters/IntegrationParameters.tsx} (65%)
rename frontend/src/component/{addons/AddonList => integrations/IntegrationList}/AvailableAddons/AvailableAddons.tsx (88%)
rename frontend/src/component/{addons/AddonList/AvailableAddons/ConfigureAddonButton/ConfigureAddonButton.tsx => integrations/IntegrationList/AvailableAddons/ConfigureAddonButton/ConfigureAddonsButton.tsx} (77%)
create mode 100644 frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx
rename frontend/src/component/{addons/AddonList => integrations/IntegrationList}/ConfiguredAddons/ConfiguredAddons.tsx (95%)
rename frontend/src/component/{addons/AddonList => integrations/IntegrationList}/ConfiguredAddons/ConfiguredAddonsActionCell/ConfiguredAddonsActionsCell.tsx (97%)
rename frontend/src/component/{addons/AddonList/AddonIcon/AddonIcon.tsx => integrations/IntegrationList/IntegrationIcon/IntegrationIcon.tsx} (94%)
create mode 100644 frontend/src/component/integrations/IntegrationList/IntegrationList.tsx
rename frontend/src/component/{addons/AddonList/AddonNameCell/AddonNameCell.tsx => integrations/IntegrationList/IntegrationNameCell/IntegrationNameCell.tsx} (84%)
diff --git a/frontend/src/component/addons/AddonList/AddonList.tsx b/frontend/src/component/addons/AddonList/AddonList.tsx
deleted file mode 100644
index 5d4de0e8c8a8..000000000000
--- a/frontend/src/component/addons/AddonList/AddonList.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import React from 'react';
-import { ConfiguredAddons } from './ConfiguredAddons/ConfiguredAddons';
-import { AvailableAddons } from './AvailableAddons/AvailableAddons';
-import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import useAddons from 'hooks/api/getters/useAddons/useAddons';
-
-export const AddonList = () => {
- const { providers, addons, loading } = useAddons();
-
- return (
- <>
- 0}
- show={}
- />
-
- >
- );
-};
diff --git a/frontend/src/component/integrations/AddonRedirect/AddonRedirect.tsx b/frontend/src/component/integrations/AddonRedirect/AddonRedirect.tsx
new file mode 100644
index 000000000000..acb916333faf
--- /dev/null
+++ b/frontend/src/component/integrations/AddonRedirect/AddonRedirect.tsx
@@ -0,0 +1,19 @@
+import { Link, useLocation, useNavigate } from 'react-router-dom';
+import { useEffect } from 'react';
+import { PageContent } from 'component/common/PageContent/PageContent';
+
+export const AddonRedirect = () => {
+ const location = useLocation();
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ navigate(`/integrations${location.pathname.replace('/addons', '')}`);
+ }, [location.pathname, navigate]);
+
+ return (
+
+ Addons where renamed to{' '}
+ /integrations
+
+ );
+};
diff --git a/frontend/src/component/addons/CreateAddon/CreateAddon.tsx b/frontend/src/component/integrations/CreateIntegration/CreateIntegration.tsx
similarity index 87%
rename from frontend/src/component/addons/CreateAddon/CreateAddon.tsx
rename to frontend/src/component/integrations/CreateIntegration/CreateIntegration.tsx
index 30548882f179..0158ab9957fe 100644
--- a/frontend/src/component/addons/CreateAddon/CreateAddon.tsx
+++ b/frontend/src/component/integrations/CreateIntegration/CreateIntegration.tsx
@@ -1,5 +1,5 @@
import useAddons from 'hooks/api/getters/useAddons/useAddons';
-import { AddonForm } from '../AddonForm/AddonForm';
+import { IntegrationForm } from '../IntegrationForm/IntegrationForm';
import cloneDeep from 'lodash.clonedeep';
import { IAddon } from 'interfaces/addons';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
@@ -14,7 +14,7 @@ export const DEFAULT_DATA = {
environments: [],
} as unknown as IAddon; // TODO: improve type
-export const CreateAddon = () => {
+export const CreateIntegration = () => {
const providerId = useRequiredPathParam('providerId');
const { providers, refetchAddons } = useAddons();
@@ -29,7 +29,7 @@ export const CreateAddon = () => {
};
return (
- {
+export const EditIntegration = () => {
const addonId = useRequiredPathParam('addonId');
const { providers, addons, refetchAddons } = useAddons();
@@ -18,7 +18,7 @@ export const EditAddon = () => {
: undefined;
return (
- = ({
+export const IntegrationForm: VFC = ({
editMode,
provider,
addon: initialValues,
@@ -272,7 +272,7 @@ export const AddonForm: VFC = ({
(
- = ({
- = ({
/>
- = ({
/>
- = ({
/>
- {
});
it('renders with default state', () => {
- render();
+ render(
+
+ );
const checkbox = screen.getByLabelText(
/all current and future projects/i
@@ -49,7 +51,9 @@ describe('AddonMultiSelector', () => {
it('can toggle "ALL" checkbox', async () => {
const user = userEvent.setup();
- render();
+ render(
+
+ );
await user.click(screen.getByTestId('select-all-projects'));
@@ -70,7 +74,10 @@ describe('AddonMultiSelector', () => {
it('renders with autocomplete enabled if default value is not a wildcard', () => {
render(
-
+
);
const checkbox = screen.getByLabelText(
@@ -87,7 +94,7 @@ describe('AddonMultiSelector', () => {
it("doesn't show up for less than 3 options", async () => {
const user = userEvent.setup();
render(
- {
it('can filter options', async () => {
const user = userEvent.setup();
render(
- void;
@@ -44,7 +44,7 @@ const StyledCheckbox = styled(Checkbox)(() => ({
const CustomPaper = ({ ...props }) => ;
-export const AddonMultiSelector: VFC = ({
+export const IntegrationMultiSelector: VFC = ({
options,
selectedItems,
onChange,
@@ -138,9 +138,9 @@ export const AddonMultiSelector: VFC = ({
const DefaultHelpText = () => (
- Selecting {entityName}(s) here will filter events so that your addon
- will only receive events that are tagged with one of your{' '}
- {entityName}s.
+ Selecting {entityName}(s) here will filter events so that your
+ integration will only receive events that are tagged with one of
+ your {entityName}s.
);
diff --git a/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameter/AddonParameter.tsx b/frontend/src/component/integrations/IntegrationForm/IntegrationParameters/IntegrationParameter/IntegrationParameter.tsx
similarity index 90%
rename from frontend/src/component/addons/AddonForm/AddonParameters/AddonParameter/AddonParameter.tsx
rename to frontend/src/component/integrations/IntegrationForm/IntegrationParameters/IntegrationParameter/IntegrationParameter.tsx
index d4f15ec80bf0..8983a5b9e5c6 100644
--- a/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameter/AddonParameter.tsx
+++ b/frontend/src/component/integrations/IntegrationForm/IntegrationParameters/IntegrationParameter/IntegrationParameter.tsx
@@ -1,7 +1,7 @@
import { TextField, Typography } from '@mui/material';
import { IAddonConfig, IAddonProviderParams } from 'interfaces/addons';
import { ChangeEventHandler } from 'react';
-import { StyledAddonParameterContainer } from '../../AddonForm.styles';
+import { StyledAddonParameterContainer } from '../../IntegrationForm.styles';
const resolveType = ({ type = 'text', sensitive = false }, value: string) => {
if (sensitive && value === MASKED_VALUE) {
@@ -15,19 +15,19 @@ const resolveType = ({ type = 'text', sensitive = false }, value: string) => {
const MASKED_VALUE = '*****';
-export interface IAddonParameterProps {
+export interface IIntegrationParameterProps {
parametersErrors: Record;
definition: IAddonProviderParams;
setParameterValue: (param: string) => ChangeEventHandler;
config: IAddonConfig;
}
-export const AddonParameter = ({
+export const IntegrationParameter = ({
definition,
config,
parametersErrors,
setParameterValue,
-}: IAddonParameterProps) => {
+}: IIntegrationParameterProps) => {
const value = config.parameters[definition.name] || '';
const type = resolveType(definition, value);
const error = parametersErrors[definition.name];
diff --git a/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameters.tsx b/frontend/src/component/integrations/IntegrationForm/IntegrationParameters/IntegrationParameters.tsx
similarity index 65%
rename from frontend/src/component/addons/AddonForm/AddonParameters/AddonParameters.tsx
rename to frontend/src/component/integrations/IntegrationForm/IntegrationParameters/IntegrationParameters.tsx
index fb62c6894fde..646b45ab8453 100644
--- a/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameters.tsx
+++ b/frontend/src/component/integrations/IntegrationForm/IntegrationParameters/IntegrationParameters.tsx
@@ -1,26 +1,26 @@
import React from 'react';
import { IAddonProvider } from 'interfaces/addons';
import {
- AddonParameter,
- IAddonParameterProps,
-} from './AddonParameter/AddonParameter';
-import { StyledTitle } from '../AddonForm.styles';
+ IntegrationParameter,
+ IIntegrationParameterProps,
+} from './IntegrationParameter/IntegrationParameter';
+import { StyledTitle } from '../IntegrationForm.styles';
-interface IAddonParametersProps {
+interface IIntegrationParametersProps {
provider?: IAddonProvider;
- parametersErrors: IAddonParameterProps['parametersErrors'];
+ parametersErrors: IIntegrationParameterProps['parametersErrors'];
editMode: boolean;
- setParameterValue: IAddonParameterProps['setParameterValue'];
- config: IAddonParameterProps['config'];
+ setParameterValue: IIntegrationParameterProps['setParameterValue'];
+ config: IIntegrationParameterProps['config'];
}
-export const AddonParameters = ({
+export const IntegrationParameters = ({
provider,
config,
parametersErrors,
setParameterValue,
editMode,
-}: IAddonParametersProps) => {
+}: IIntegrationParametersProps) => {
if (!provider) return null;
return (
@@ -33,7 +33,7 @@ export const AddonParameters = ({
) : null}
{provider.parameters.map(parameter => (
- {
return (
- } />
+ }
+ />
);
},
},
@@ -79,7 +85,7 @@ export const AvailableAddons = ({
accessor: 'name',
width: '90%',
Cell: ({ row: { original } }: any) => (
-
+
),
sortType: 'alphanumeric',
},
@@ -88,7 +94,7 @@ export const AvailableAddons = ({
align: 'center',
Cell: ({ row: { original } }: any) => (
-
+
),
width: 150,
diff --git a/frontend/src/component/addons/AddonList/AvailableAddons/ConfigureAddonButton/ConfigureAddonButton.tsx b/frontend/src/component/integrations/IntegrationList/AvailableAddons/ConfigureAddonButton/ConfigureAddonsButton.tsx
similarity index 77%
rename from frontend/src/component/addons/AddonList/AvailableAddons/ConfigureAddonButton/ConfigureAddonButton.tsx
rename to frontend/src/component/integrations/IntegrationList/AvailableAddons/ConfigureAddonButton/ConfigureAddonsButton.tsx
index dfe10193c3b9..bc165e9059f3 100644
--- a/frontend/src/component/addons/AddonList/AvailableAddons/ConfigureAddonButton/ConfigureAddonButton.tsx
+++ b/frontend/src/component/integrations/IntegrationList/AvailableAddons/ConfigureAddonButton/ConfigureAddonsButton.tsx
@@ -3,13 +3,16 @@ import { CREATE_ADDON } from 'component/providers/AccessProvider/permissions';
import { IAddonProvider } from 'interfaces/addons';
import { useNavigate } from 'react-router-dom';
-interface IConfigureAddonButtonProps {
+interface IConfigureAddonsButtonProps {
provider: IAddonProvider;
}
-export const ConfigureAddonButton = ({
+/**
+ * @deprecated Remove when integrationsRework flag is removed
+ */
+export const ConfigureAddonsButton = ({
provider,
-}: IConfigureAddonButtonProps) => {
+}: IConfigureAddonsButtonProps) => {
const navigate = useNavigate();
return (
diff --git a/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx b/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx
new file mode 100644
index 000000000000..970f5991c48b
--- /dev/null
+++ b/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx
@@ -0,0 +1,8 @@
+import { PageContent } from 'component/common/PageContent/PageContent';
+import { VFC } from 'react';
+
+interface IAvailableIntegrationsProps {}
+
+export const AvailableIntegrations: VFC = () => {
+ return Available integrations;
+};
diff --git a/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx b/frontend/src/component/integrations/IntegrationList/ConfiguredAddons/ConfiguredAddons.tsx
similarity index 95%
rename from frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx
rename to frontend/src/component/integrations/IntegrationList/ConfiguredAddons/ConfiguredAddons.tsx
index edfff5c0d903..54198c7b4c90 100644
--- a/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx
+++ b/frontend/src/component/integrations/IntegrationList/ConfiguredAddons/ConfiguredAddons.tsx
@@ -13,10 +13,13 @@ import { useTable, useSortBy } from 'react-table';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { SortableTableHeader, TablePlaceholder } from 'component/common/Table';
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
-import { AddonIcon } from '../AddonIcon/AddonIcon';
+import { IntegrationIcon } from '../IntegrationIcon/IntegrationIcon';
import { ConfiguredAddonsActionsCell } from './ConfiguredAddonsActionCell/ConfiguredAddonsActionsCell';
-import { AddonNameCell } from '../AddonNameCell/AddonNameCell';
+import { IntegrationNameCell } from '../IntegrationNameCell/IntegrationNameCell';
+/**
+ * @deprecated Remove when integrationsRework flag is removed
+ */
export const ConfiguredAddons = () => {
const { refetchAddons, addons, providers, loading } = useAddons();
const { updateAddon, removeAddon } = useAddonsApi();
@@ -73,7 +76,9 @@ export const ConfiguredAddons = () => {
original: { provider },
},
}: any) => (
- } />
+ }
+ />
),
disableSortBy: true,
},
@@ -86,7 +91,7 @@ export const ConfiguredAddons = () => {
original: { provider, description },
},
}: any) => (
- name === provider
diff --git a/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddonsActionCell/ConfiguredAddonsActionsCell.tsx b/frontend/src/component/integrations/IntegrationList/ConfiguredAddons/ConfiguredAddonsActionCell/ConfiguredAddonsActionsCell.tsx
similarity index 97%
rename from frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddonsActionCell/ConfiguredAddonsActionsCell.tsx
rename to frontend/src/component/integrations/IntegrationList/ConfiguredAddons/ConfiguredAddonsActionCell/ConfiguredAddonsActionsCell.tsx
index f2a9e21136c1..2d3374818b1a 100644
--- a/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddonsActionCell/ConfiguredAddonsActionsCell.tsx
+++ b/frontend/src/component/integrations/IntegrationList/ConfiguredAddons/ConfiguredAddonsActionCell/ConfiguredAddonsActionsCell.tsx
@@ -18,6 +18,9 @@ interface IConfiguredAddonsActionsCellProps {
setDeletedAddon: React.Dispatch>;
}
+/**
+ * @deprecated Remove when integrationsRework flag is removed
+ */
export const ConfiguredAddonsActionsCell = ({
toggleAddon,
setShowDelete,
diff --git a/frontend/src/component/addons/AddonList/AddonIcon/AddonIcon.tsx b/frontend/src/component/integrations/IntegrationList/IntegrationIcon/IntegrationIcon.tsx
similarity index 94%
rename from frontend/src/component/addons/AddonList/AddonIcon/AddonIcon.tsx
rename to frontend/src/component/integrations/IntegrationList/IntegrationIcon/IntegrationIcon.tsx
index 427a6ca7ba76..ee2e6f1e2c3d 100644
--- a/frontend/src/component/addons/AddonList/AddonIcon/AddonIcon.tsx
+++ b/frontend/src/component/integrations/IntegrationList/IntegrationIcon/IntegrationIcon.tsx
@@ -14,11 +14,11 @@ const style: React.CSSProperties = {
marginRight: '16px',
};
-interface IAddonIconProps {
+interface IIntegrationIconProps {
name: string;
}
-export const AddonIcon = ({ name }: IAddonIconProps) => {
+export const IntegrationIcon = ({ name }: IIntegrationIconProps) => {
switch (name) {
case 'slack':
case 'slack-app':
diff --git a/frontend/src/component/integrations/IntegrationList/IntegrationList.tsx b/frontend/src/component/integrations/IntegrationList/IntegrationList.tsx
new file mode 100644
index 000000000000..addbad3734e4
--- /dev/null
+++ b/frontend/src/component/integrations/IntegrationList/IntegrationList.tsx
@@ -0,0 +1,28 @@
+import { ConfiguredAddons } from './ConfiguredAddons/ConfiguredAddons';
+import { AvailableAddons } from './AvailableAddons/AvailableAddons';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import useAddons from 'hooks/api/getters/useAddons/useAddons';
+import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
+import { AvailableIntegrations } from './AvailableIntegrations/AvailableIntegrations';
+
+export const IntegrationList = () => {
+ const { providers, addons, loading } = useAddons();
+ const { uiConfig } = useUiConfig();
+ const integrationsRework = uiConfig?.flags?.integrationsRework || false;
+
+ return (
+ <>
+ 0}
+ show={}
+ />
+ }
+ elseShow={
+
+ }
+ />
+ >
+ );
+};
diff --git a/frontend/src/component/addons/AddonList/AddonNameCell/AddonNameCell.tsx b/frontend/src/component/integrations/IntegrationList/IntegrationNameCell/IntegrationNameCell.tsx
similarity index 84%
rename from frontend/src/component/addons/AddonList/AddonNameCell/AddonNameCell.tsx
rename to frontend/src/component/integrations/IntegrationList/IntegrationNameCell/IntegrationNameCell.tsx
index 74ab375c0f16..4d2bb0aeacb5 100644
--- a/frontend/src/component/addons/AddonList/AddonNameCell/AddonNameCell.tsx
+++ b/frontend/src/component/integrations/IntegrationList/IntegrationNameCell/IntegrationNameCell.tsx
@@ -10,14 +10,19 @@ const StyledBadge = styled(Badge)(({ theme }) => ({
marginLeft: theme.spacing(1),
}));
-interface IAddonNameCellProps {
+interface IIntegrationNameCellProps {
provider: Pick<
IAddonProvider,
'displayName' | 'description' | 'deprecated'
>;
}
-export const AddonNameCell = ({ provider }: IAddonNameCellProps) => (
+/**
+ * @deprecated Remove when integrationsRework flag is removed
+ */
+export const IntegrationNameCell = ({
+ provider,
+}: IIntegrationNameCellProps) => (
;
@@ -133,6 +134,10 @@ const flags: IFlags = {
process.env.UNLEASH_EXPERIMENTAL_CUSTOM_ROOT_ROLES_KILL_SWITCH,
false,
),
+ integrationsRework: parseEnvVarBoolean(
+ process.env.UNLEASH_INTEGRATIONS,
+ false,
+ ),
};
export const defaultExperimentalOptions: IExperimentalOptions = {
diff --git a/website/README.md b/website/README.md
index 7c71f1eeb00f..7eee45919dbc 100644
--- a/website/README.md
+++ b/website/README.md
@@ -13,6 +13,7 @@ yarn install
```console
yarn generate
```
+
Generate the Open API docs that live at Reference documentation > APIs > OpenAPI
## Local Development
@@ -21,7 +22,7 @@ Generate the Open API docs that live at Reference documentation > APIs > OpenAPI
yarn start
```
- Start a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
+Start a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
## Build