Преглед изворни кода

Merge pull request #789 from porter-dev/0.5.0-frontend-contributor-guide

[0.5.0] [Docs] Frontend Contribution Guide
abelanger5 пре 4 година
родитељ
комит
da08e3154c

+ 168 - 0
docs/developing/frontend-guide.md

@@ -0,0 +1,168 @@
+# Porter Frontend Contribution Guide
+
+The purpose of this guide is to provide guidance on the development of the trickier parts of Porter's current frontend. 
+
+## Getting Started
+
+### Components
+
+#### Functional Components and Migration
+
+Currently, most of the frontend is written using Class Components. If you contribute to any part of the frontend, you are encouraged to rewrite any class component into a functional component. While doing so, keep a few things in mind:
+
+
+#### Typing and Internal Organization
+
+To keep a functional component consistent, it should be typed using `React.FC` with a `Props` interface. Internally, the hooks used by the component should be listed near each other at the start of the function body, to avoid confusion. For example, the following is a component that obeys these rules:
+
+```typescript
+interface Props {
+    ...
+}
+
+const MyComponent: React.FC<Props> = (props) => {
+    const [foo, updateFoo] = useState(...);
+    const [bar, updateBar] = useState(...);
+    const { baz } = useContext(...);
+
+    const qux  = ...;
+
+    ...
+}
+```
+
+## Data Flow
+
+### Context
+
+If a prop is passed down more than three levels in the component heirarchy, it should be rewritten to be contained in a context. Make sure that all the components that then consume this context are functional, since the Context API isn't very good for class components.
+
+### Hooks
+
+One of the advantages of using react 16+ is the posibility to create custom hooks, so if you feel that something could be easier or you could reduce the repetition of logic by using custom hooks we encourage you to do so!
+
+#### Where should I place my new hooks
+
+If the hook that you're creating may be used all over the application you can add it to the `src/shared/hooks` folder, if not, you can place them right next to the components that will be using the hooks.
+
+#### Typing and documentation
+
+Please note that every hook that you may create will be used by other people, so typing it and adding comments on how it works/why you create such hook is super useful and we encourage this behaviour.
+
+
+## Routing
+
+### Routing model for new standard:
+
+The idea is to keep something similar to what NextJS or Angular+2 with submodules does, if we have a route the folder structure should try to respect that path
+
+For example:
+
+Having the following routes
+
+```
+URL dashboard.getporter.dev/cluster-dashboard/node-list
+URL dashboard.getporter.dev/cluster-dashboard/expanded-node-view
+URL dashboard.getporter.dev/project-dashboard/cluster-list
+URL dashboard.getporter.dev/applications?cluster_id=somename
+```
+
+We should end with the following structure
+
+```
+|-- src/
+    |-- project-dashboard
+ 	|	|-- Routes.tsx
+    |   |-- ProjectDashboard.tsx
+ 	|	|-- _SomeSpecificComponentNeeded.tsx
+ 	|	`-- cluster-list
+ 	|		`-- ClusterList.tsx
+    |-- cluster-dashboard
+ 	|	|-- Routes.tsx
+ 	|	|-- ClusterDashboard.tsx
+ 	|	|-- node-list
+ 	|	|	`-- NodeList.tsx
+ 	|	`-- expanded-node-view
+ 	|		`-- ExpandedNodeView
+    `-- applications
+        |-- Route.tsx
+        `-- Applications.tsx
+```
+
+All first level routes should have it's own folder as it may be considered a new module of the application, inside each module we may have specific components we don't wanna share between modules, those should be named with an underscore first to be clear that they're not pages but simple components.
+In the case that the Routes.tsx on the module became too long, it can be divided into subroutes inside the subfollowing folders.
+
+## Advanced
+
+### Forms
+
+Porter allows developers to build customizable forms on top of Helm by adding an optional `form.yaml` file to any Helm chart. Currently, forms can write to any field specified in a chart's `values.yaml`. We are working to add support for user-defined applets that can directly perform CRUD operations on cluster resources.
+
+On the frontend, there are two components responsible for making forms work and are separated such that one only handles 
+the form logic while the other does the rendering. The first one is `PorterFormContextProvider`,
+which provides a context that the second component `PorterForm` subscribes to using a custom hook.
+This relationship should be kept in mind when adding new functionality to this system: logic and rendering must be
+separated between these components.
+
+### Debugging Forms
+
+To get an easy-to-access version of the form component with all the relevant props
+already passed in for you, navigate to a project dashboard and press and hold `command+k+z`;
+
+### Form State
+As a whole, the frontend form stores its state in three places:
+1. The variables of the form - these are shared and can be modified by any form field
+2. The state specific to each form field which can only be modified by the form field itself
+3. The validation information for each field which can only be modified by the form field itself
+
+This state is exposed to each form field through the `useFormFieldHook<T>` (where `T` is the interface describing the state of the component), which every component calls with a unique id passed down
+to it through props:
+```typescript
+interface FieldStateInterface {
+    some: string;
+    fields: boolean;
+}
+
+const { state, variables, setVars, setState, setValidation } = useFormField<FieldStateInterface>(
+    props.id,
+    {
+      initState: {
+          some: "foo",
+          fields: false,
+      },
+      initValidation: {
+        validated: !props.required
+      },
+      initVars: {},
+    }
+  );
+```
+The returned state changing functions behave in the same way as the `setState` function behaves in Class components. So,
+for example, if we wanted to change the value of variable "foo" in the form, we could write:
+```typescript
+setVars((vars) => {
+    return {
+        ...vars,
+        foo: "bar"
+    }
+})
+```
+To see more about how this system works, check out the implementation for some simpler form components like `Checkbox` or 
+`Input`.
+
+### Extracting Variables on Submit
+
+If you looked at the implementations of other form fields, you may have noticed that each form field file
+exports a function in the form `getFinalVariablesFor[FieldName]`. This function is neccesarry for two reasons:
+1. Sometimes, when the form is submitted, some fields have not yet been rendered but still need their values included
+in the final variable output (for example, if a string input has a default value).
+   
+2. Sometimes, a field wants to make modification to the variable/state belonging to it before the form is submitted
+   (for example, a string input could append units to its value on submission).
+   
+So, if a field has a default/wants to modify variables on submit, this functions should be included in the file
+(and in the appropriate place in the `PorterFormContextProvider`). In general, this function takes in three arguments:
+the list of unmodified variables on submission, the props of the field, and the state of the field upon submission. It 
+returns an object that will be applied on top of the variable list. Also note that the state passed into this function
+could be `null` or `undefined` if the field has never been rendered. For more details, look at the implementation of this function for `Input`.
+   

+ 79 - 0
docs/developing/frontend-roadmap-status.md

@@ -0,0 +1,79 @@
+# Frontend roadmap status
+
+In this page you will be able to see how the roadmap status is going! Right now we have a work in progress list of components that needs to be tested and worked on!
+
+Keep in mind that this is still not the final version, and this document will be updated to also divide the components by their correspondant module to wich they have to be migrated to!
+
+| Component                     | Current path                                                                        | Migrated to functional | Tested up | Cleaned up |
+| ----------------------------- | ----------------------------------------------------------------------------------- | ---------------------- | --------- | ---------- |
+| App                           | `dashboard/src/App.tsx`                                                             |                        |           |            |
+| MainWrapper                   | `dashboard/src/main/MainWrapper.tsx`                                                |                        |           |            |
+| ContextProvider               | `dashboard/src/shared/Context.tsx`                                                  |                        |           |            |
+| Main                          | `dashboard/src/main/Main.tsx`                                                       |                        |           |            |
+| Login                         | `dashboard/src/main/auth/Login.tsx`                                                 |                        |           |            |
+| Register                      | `dashboard/src/main/auth/Register.tsx`                                              |                        |           |            |
+| ResetPasswordFinalize         | `dashboard/src/main/auth/ResetPasswordFinalize.tsx`                                 |                        |           |            |
+| ResetPasswordInit             | `dashboard/src/main/auth/ResetPasswordInit.tsx`                                     |                        |           |            |
+| VerifyEmail                   | `dashboard/src/main/auth/VerifyEmail.tsx`                                           |                        |           |            |
+| CurrentError                  | `dashboard/src/main/CurrentError.tsx`                                               |                        |           |            |
+| Home                          | `dashboard/src/main/home/Home.tsx`                                                  |                        |           |            |
+| NoClusterPlaceholder          | `dashboard/src/main/home/NoClusterPlaceholder.tsx`                                  |                        |           |            |
+| ClusterSection                | `dashboard/src/main/home/sidebar/ClusterSection.tsx`                                |                        |           |            |
+| Drawer                        | `dashboard/src/main/home/sidebar/Drawer.tsx`                                        |                        |           |            |
+| ProjectSection                | `dashboard/src/main/home/sidebar/ProjectSection.tsx`                                |                        |           |            |
+| ProjectSectionContainer       | `dashboard/src/main/home/sidebar/ProjectSectionContainer.tsx`                       |                        |           |            |
+| Sidebar                       | `dashboard/src/main/home/sidebar/Sidebar.tsx`                                       |                        |           |            |
+| AWSFormSection                | `dashboard/src/main/home/provisioner/AWSFormSection.tsx`                            |                        |           |            |
+| DOFormSection                 | `dashboard/src/main/home/provisioner/DOFormSection.tsx`                             |                        |           |            |
+| ExistingClusterSection        | `dashboard/src/main/home/provisioner/ExistingClusterSection.tsx`                    |                        |           |            |
+| GCPFormSection                | `dashboard/src/main/home/provisioner/GCPFormSection.tsx`                            |                        |           |            |
+| InfraStatuses                 | `dashboard/src/main/home/provisioner/InfraStatuses.tsx`                             |                        |           |            |
+| Provisioner                   | `dashboard/src/main/home/provisioner/Provisioner.tsx`                               |                        |           |            |
+| ProvisionerLogs               | `dashboard/src/main/home/provisioner/ProvisionerLogs.tsx`                           |                        |           |            |
+| ProvisionerSettings           | `dashboard/src/main/home/provisioner/ProvisionerSettings.tsx`                       |                        |           |            |
+| InvitePage                    | `dashboard/src/main/home/project-settings/InviteList.tsx`                           | ✅                     |           |            |
+| ProjectSettings               | `dashboard/src/main/home/project-settings/ProjectSettings.tsx`                      |                        |           |            |
+| NewProject                    | `dashboard/src/main/home/new-project/NewProject.tsx`                                |                        |           |            |
+| Feedback                      | `dashboard/src/main/home/navbar/Feedback.tsx`                                       |                        |           |            |
+| Navbar                        | `dashboard/src/main/home/navbar/Navbar.tsx`                                         |                        |           |            |
+| AccountSettingsModal          | `dashboard/src/main/home/modals/AccountSettingsModal.tsx`                           |                        |           |            |
+| ClusterInstructionsModal      | `dashboard/src/main/home/modals/ClusterInstructionsModal.tsx`                       |                        |           |            |
+| DeleteNamespaceModal          | `dashboard/src/main/home/modals/DeleteNamespaceModal.tsx`                           |                        |           |            |
+| EditInviteOrCollaboratorModal | `dashboard/src/main/home/modals/EditInviteOrCollaboratorModal.tsx`                  |                        |           |            |
+| EnvEditorModal                | `dashboard/src/main/home/modals/EnvEditorModal.tsx`                                 |                        |           |            |
+| IntegrationsInstructionsModal | `dashboard/src/main/home/modals/IntegrationsInstructionsModal.tsx`                  |                        |           |            |
+| IntegrationsModal             | `dashboard/src/main/home/modals/IntegrationsModal.tsx`                              |                        |           |            |
+| LoadEnvGroupModal             | `dashboard/src/main/home/modals/LoadEnvGroupModal.tsx`                              |                        |           |            |
+| Modal                         | `dashboard/src/main/home/modals/Modal.tsx`                                          |                        |           |            |
+| NamespaceModal                | `dashboard/src/main/home/modals/NamespaceModal.tsx`                                 |                        |           |            |
+| UpdateClusterModal            | `dashboard/src/main/home/modals/UpdateClusterModal.tsx`                             |                        |           |            |
+| Launch                        | `dashboard/src/main/home/launch/Launch.tsx`                                         |                        |           |            |
+| LaunchFlow                    | `dashboard/src/main/home/launch/launch-flow/LaunchFlow.tsx`                         |                        |           |            |
+| SettingsPage                  | `dashboard/src/main/home/launch/launch-flow/SettingsPage.tsx`                       |                        |           |            |
+| SourcePage                    | `dashboard/src/main/home/launch/launch-flow/SourcePage.tsx`                         |                        |           |            |
+| ExpandedTemplate              | `dashboard/src/main/home/launch/expanded-template/ExpandedTemplate.tsx`             |                        |           |            |
+| TemplateInfo                  | `dashboard/src/main/home/launch/expanded-template/TemplateInfo.tsx`                 |                        |           |            |
+| SlackIntegrationList          | `dashboard/src/main/home/integrations/SlackIntegrationList.tsx`                     | ✅                     |           |            |
+| Integrations                  | `dashboard/src/main/home/integrations/Integrations.tsx`                             | ✅                     |           |            |
+| IntegrationRow                | `dashboard/src/main/home/integrations/IntegrationRow.tsx`                           |                        |           |            |
+| IntegrationList               | `dashboard/src/main/home/integrations/IntegrationList.tsx`                          |                        |           |            |
+| IntegrationCategories         | `dashboard/src/main/home/integrations/IntegrationCategories.tsx`                    | ✅                     |           |            |
+| DockerHubForm                 | `dashboard/src/main/home/integrations/edit-integration/DockerHubForm.tsx`           |                        |           |            |
+| ECRForm                       | `dashboard/src/main/home/integrations/edit-integration/ECRForm.tsx`                 |                        |           |            |
+| EditIntegrationForm           | `dashboard/src/main/home/integrations/edit-integration/EditIntegrationForm.tsx`     |                        |           |            |
+| EKSForm                       | `dashboard/src/main/home/integrations/edit-integration/EKSForm.tsx`                 |                        |           |            |
+| GCRForm                       | `dashboard/src/main/home/integrations/edit-integration/GCRForm.tsx`                 |                        |           |            |
+| GKEForm                       | `dashboard/src/main/home/integrations/edit-integration/GKEForm.tsx`                 |                        |           |            |
+| CreateIntegrationForm         | `dashboard/src/main/home/integrations/create-integration/CreateIntegrationForm.tsx` |                        |           |            |
+| DockerHubForm                 | `dashboard/src/main/home/integrations/create-integration/DockerHubForm.tsx`         |                        |           |            |
+| ECRForm                       | `dashboard/src/main/home/integrations/create-integration/ECRForm.tsx`               |                        |           |            |
+| EKSForm                       | `dashboard/src/main/home/integrations/create-integration/EKSForm.tsx`               |                        |           |            |
+| GCRForm                       | `dashboard/src/main/home/integrations/create-integration/GCRForm.tsx`               |                        |           |            |
+| GKEForm                       | `dashboard/src/main/home/integrations/create-integration/GKEForm.tsx`               |                        |           |            |
+| ClusterList                   | `dashboard/src/main/home/dashboard/ClusterList.tsx`                                 |                        |           |            |
+| ClusterPlaceholder            | `dashboard/src/main/home/dashboard/ClusterPlaceholder.tsx`                          |                        |           |            |
+| ClusterPlaceholderContainer   | `dashboard/src/main/home/dashboard/ClusterPlaceholderContainer.tsx`                 |                        |           |            |
+| Dashboard                     | `dashboard/src/main/home/dashboard/Dashboard.tsx`                                   |                        |           |            |
+| PipelinesSection              | `dashboard/src/main/home/dashboard/PipelinesSection.tsx`                            |                        |           |            |
+| ExpandedChartWrapper          | `dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChartWrapper.tsx` | ✅                     |           |            |
+| ExpandedChart                 | `dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx`        | ✅                     |           |            |

+ 46 - 0
docs/developing/frontend-roadmap.md

@@ -0,0 +1,46 @@
+# Frontend Roadmap
+
+We know that the current state of the Porter Dashboard is not the most updated one in terms of React practices, but the idea is not to keep it that way. That's why we want to introduce a new roadmap that every contributor can help on in terms to improve the current functionality!
+
+If you want to see the current state of the roadmap you can check out this document! [Frontend Roadmap Status](frontend-roadmap-status.md)
+
+## Roadmap
+
+The next image represents a raw perspective of how we want to face this migration to update and improve our dashboard in terms of technical debt! You can see a more step by step detailed guide below.
+
+![image](https://user-images.githubusercontent.com/23369263/128541304-4e7a8d3d-08f5-4c3c-841f-91f8abfbef4c.png)
+
+### Migrate to functional components
+
+This step is pretty self explinatory, we want to leave behind class components and build a new era of Functional components with custom hooks and all the pretty stuff that came after React 16 version. The main idea is to have the chance to have almost all the application on functional components and start separating stuff to custom hooks when it's needed to improve the readability of components.
+
+If you want to help on this step, you need to consider two things:
+
+- We want to rewrite the minimal amount of logic necessary to migrate, don't spend hours trying to improve the component, just having it as functional instead of class based is enough for this step.
+- The functional component should be an equivalent version of it's class component (this means, that the functionality should be the same even if the implementation may vary a little).
+
+### Setup Jest and testing
+
+This is one of the big ones, after migrate a component to functional components, the idea is to setup tests to be sure that the components behaviour is the one that we expect, after all, testing is one of the keys to have a more stable system. We want to use [Jest](https://jestjs.io/docs/getting-started) and the [React Testing library](https://testing-library.com/docs/react-testing-library/intro/) to get this step done, mainly because we want to implement as much as possible black box testing, this is mainly because of the next step `Clean up data flows` where we will be updating most of the internal data flows of the components, but we want to keep the functionality for the user to be the same.
+
+#### Want to help on this step?
+
+We are not testing experts, so there's probably a lot of things to improve the way of how we think about this, don't doubt on asking us or suggesting on our [discord channel](https://discord.gg/GJynMR3KXK)!
+
+### Clean up datas flows
+
+Right here is where all the magic will happen, currently the components are highly coupled and this makes really hard for implementing new features and we detect a lot of cases where the performance of the app is dropped.
+This step should be composed by a couple of questions:
+
+- Can we reduce the http calls that we're making?
+- Can we reduce the amount of data that we're handling?
+- Do we need a context for handling data to be more optimal and clean?
+- And last, is there anything that we can atomize and share between multiple components?
+
+With this questions in mind, is pretty obvious that the work for clean up can be really hard, but the main idea is to make the components as readable as possible, adding comments, removing useless logic or making it more accessible for new contributors!
+
+### Migrate routes
+
+This one is a little bit more trickier in terms of implementation, as its not aimed for just one component but instead, for a whole set of components that have deep relations between them. A clear example of this is the applications module, it's a tightly coupled set of components that are mixed with jobs and env groups, even if they don't share any logic and a really small sets of components, this is clearly not clean in any way and this step should help the project to be more organized for exploring it and to know where to add new stuff or find the component that we want to change!
+
+You can find more about the routing system that we will implement on the [frontend guide document](frontend-guide.md)!