-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
110 lines (107 loc) · 17.8 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OMS</title>
<link rel="stylesheet" href="https://stackedit.io/style.css" />
</head>
<body class="stackedit">
<div class="stackedit__html"><h2 id="oms-frontend-technical-documentation">OMS Frontend Technical Documentation</h2>
<h6 id="authored-by-kyle-estes-">** Authored by Kyle Estes **</h6>
<p>This form is the primary documentation for working with the react based frontend for TigerOMS. The purpose of this document is to clearly relay acceptable design patterns based on agreed upon react principals, As well as to document third party libraries and their usages. In doing this we should have an easy to read project that feels more coheasive as an end result. All pull request submitted should follow these rules.</p>
<ol>
<li>Work is following design principals listed in this document</li>
<li>When considering required functionality of a piece of work, Third party libraries that are listed in this document should be considered first.</li>
<li>The work being done has been refactored to an acceptable degree following normal coding conventions <strong>(DRY, KISS, YAGNI)</strong></li>
</ol>
<p>Any pull request not following these steps should be denied immediately.</p>
<h3 id="folder-structure">Folder Structure</h3>
<p>We want OMS to be as easy as possible to jump in and get started. And to that end the folder structure for the project and for components should be consistent and easy to understand. The typical folder structure for the project is as follows</p>
<h4 id="project-directory">Project Directory</h4>
<pre><code>-src
--components
--pages
--helpers
----api.ts
--models
--redux
</code></pre>
<p><strong>src</strong> is the global root directory for the project code.<br>
<strong>components</strong> is a directory for global reusable components<br>
<strong>pages</strong> is a directory for components that compose a page<br>
<strong>helpers</strong> will have different utility classes and api classes<br>
<strong>models</strong> is a directory that has ALL models for interacting with the server<br>
<strong>redux</strong> will have all files related to the global state manager</p>
<h4 id="component-directory">Component Directory</h4>
<p>A component is typically a folder which has at least three files. <code>index.tsx</code> <code>types.ts</code> <code>styles.ts</code></p>
<p>A component also may have and endless amount of nested components within it. Take the following component for example.</p>
<pre><code>-listView
--index.tsx
--styles.tsx
--types.tsx
</code></pre>
<p>a listView component will typically have a listViewItem. And although that is a seperate component. It really is only meant to be used in the context of a listView component. Therefore it makes sense to nest it under the listView directory. Doing so also makes it clear to anyone reviewing the project that this nested-component is not being used by any other component.</p>
<p><strong>index.tsx</strong> this is the entry point for the component<br>
<strong>styles.tsx</strong> all styled components relevant for this component will be located in this folder<br>
<strong>types.tsx</strong> any special types being used by just this component will be here.</p>
<h4 id="note">Note</h4>
<p>When you use index.tsx as a file name, it allows you to import by folder instead of file name.</p>
<p>For example if you named it listView.tsx instead of instead you would have to important like so.<br>
<code>import listView from './components/listView/listView.tsx</code><br>
Contrast that with this import using index.tsx<br>
<code>import listView from './components/listView</code></p>
<p>This is a general rule of react. You can always import a component by folder if that folder includes an index.tsx</p>
<h3 id="images">Images</h3>
<p>This is more of a note for the use of images. Typically images will be stored in <code>public/imgs</code>. When you store an image in this folder you can refer to it like so in an img component <code><img src='/public/imgs/someImg.png'/></code><br>
however it should be noted that when using the public folder there is a slight delay depending on network speed for showing that image. If your image is extremely important for the component your showing. You may want to use the alternative method of storing the images in <code>src/assets</code> and using it as so<br>
<code>import myPng from './assets/someImg.png</code><br>
<code><img src={myPng}/></code><br>
However, Every image added into assets will increase the bundle size of the webpage as a whole by the size of the image. Its for this reason you should try to never do this. If a component requires an image to work. Try to rethink the component first before resorting to this method.</p>
<h3 id="typescript-over-javascript">Typescript Over Javascript</h3>
<p>Usage of javascript is not allowed. No exceptions.</p>
<h3 id="styled-component-over-css">Styled-Component over CSS</h3>
<p>This ones fairly simple to tackle. We will not be using .css files in OMS. All components using styles should have a corresponding styled component.</p>
<h3 id="functional-over-class">Functional over Class</h3>
<p>This design principal asserts that you should <strong>ALWAYS</strong> use functional components over class components. Readability of functional components is much better once you understand the syntax, There are certain speed performances that can be obtained only through the use of functional components, And it is certainly also industry standard. However you may find a lot of react examples using classes online. These cannot be used as is and must be converted to a functional component. At the time of writing there are very few class components that cannot be converted to functional. However excemptions to this rule can be made for use with certain libraries that require class components explicitly.</p>
<h3 id="redux-over-provider">Redux over Provider</h3>
<p>In recent years, Context Provider has been made very easy to use and share data between components. However in this project we will strictly limit its usage in favor of redux. There are many reasons for which, some of them will be detailed later.</p>
<h3 id="server-interactions">Server Interactions</h3>
<p>All server interactions will go through a single API function. This api function can take any request and will always return in a predictable way. This API function can be found in <code>/src/helpers/api.ts</code>.</p>
<p>The starting point for writing any interaction with the server should be in <code>api.ts</code>. The API function itself is not an export function and so its use should be done within the file.</p>
<pre><code>export const OAuthLoginRequest = async (data: SomeData) => {
const request: BodyRequest ={
endPoint: "users/OAuthLogin", <- can be relative to a base url or an absolute url itself
method: RequestType.POST, <- get/post/delete/put
headers: new Headers(), <- any header you want attached
contentType: "application/json", <- typically application/json
body: JSON.stringify(data) <- the stringified payload you want to send
}
return await API(request) as ApiResponse;
}
</code></pre>
<p>Here is an example of an API call to a server endpoint. Make note that typically any server call to a Tiger server will response with an APIResponse. And so a call made to such a server can be casted directly into an APIResponse. However in cases were a call was unable to be made successfully(such as a timeout condition), The API function will still respond with an APIResponse so its still safe to type cast it.</p>
<h3 id="redux-thunk">Redux Thunk</h3>
<p>After you have created an server interaction to be called by the API function, the next piece will be creating a redux action which will call that api function and place its results into a global state. For this exact purpose, We implement redux thunk middleware that allows us to dispatch async actions. All interactions with the server MUST go through this async-actions step.</p>
<pre class=" language-mermaid"><svg id="mermaid-svg-coRX53zEtAL2LBt3" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="129.89500427246094" style="max-width: 575.3825073242188px;" viewBox="0 0.000003814697265625 575.3825073242188 129.89500427246094"><style>#mermaid-svg-coRX53zEtAL2LBt3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}#mermaid-svg-coRX53zEtAL2LBt3 .error-icon{fill:#552222;}#mermaid-svg-coRX53zEtAL2LBt3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-coRX53zEtAL2LBt3 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-coRX53zEtAL2LBt3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-coRX53zEtAL2LBt3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-coRX53zEtAL2LBt3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-coRX53zEtAL2LBt3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-coRX53zEtAL2LBt3 .marker{fill:#666;stroke:#666;}#mermaid-svg-coRX53zEtAL2LBt3 .marker.cross{stroke:#666;}#mermaid-svg-coRX53zEtAL2LBt3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-coRX53zEtAL2LBt3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#mermaid-svg-coRX53zEtAL2LBt3 .cluster-label text{fill:#333;}#mermaid-svg-coRX53zEtAL2LBt3 .cluster-label span{color:#333;}#mermaid-svg-coRX53zEtAL2LBt3 .label text,#mermaid-svg-coRX53zEtAL2LBt3 span{fill:#000000;color:#000000;}#mermaid-svg-coRX53zEtAL2LBt3 .node rect,#mermaid-svg-coRX53zEtAL2LBt3 .node circle,#mermaid-svg-coRX53zEtAL2LBt3 .node ellipse,#mermaid-svg-coRX53zEtAL2LBt3 .node polygon,#mermaid-svg-coRX53zEtAL2LBt3 .node path{fill:#eee;stroke:#999;stroke-width:1px;}#mermaid-svg-coRX53zEtAL2LBt3 .node .label{text-align:center;}#mermaid-svg-coRX53zEtAL2LBt3 .node.clickable{cursor:pointer;}#mermaid-svg-coRX53zEtAL2LBt3 .arrowheadPath{fill:#333333;}#mermaid-svg-coRX53zEtAL2LBt3 .edgePath .path{stroke:#666;stroke-width:1.5px;}#mermaid-svg-coRX53zEtAL2LBt3 .flowchart-link{stroke:#666;fill:none;}#mermaid-svg-coRX53zEtAL2LBt3 .edgeLabel{background-color:white;text-align:center;}#mermaid-svg-coRX53zEtAL2LBt3 .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#mermaid-svg-coRX53zEtAL2LBt3 .cluster rect{fill:hsl(210,66.6666666667%,95%);stroke:#26a;stroke-width:1px;}#mermaid-svg-coRX53zEtAL2LBt3 .cluster text{fill:#333;}#mermaid-svg-coRX53zEtAL2LBt3 .cluster span{color:#333;}#mermaid-svg-coRX53zEtAL2LBt3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(-160,0%,93.3333333333%);border:1px solid #26a;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-coRX53zEtAL2LBt3:root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-coRX53zEtAL2LBt3 flowchart{fill:apa;}</style><g><g class="output"><g class="clusters"></g><g class="edgePaths"><g class="edgePath LS-A LE-B" id="L-A-B" style="opacity: 1;"><path class="path" d="M107.8499984741211,64.94750213623047L132.8499984741211,64.94750213623047L157.8499984741211,64.94750213623047" marker-end="url(https://stackedit.io/app#arrowhead7)" style="fill:none"></path><defs><marker id="arrowhead7" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="8" markerHeight="6" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowheadPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker></defs></g><g class="edgePath LS-B LE-E" id="L-B-E" style="opacity: 1;"><path class="path" d="M292.9000015258789,64.94750213623047L317.9000015258789,64.94750213623047L342.9000015258789,64.94750213623047" marker-end="url(https://stackedit.io/app#arrowhead8)" style="fill:none"></path><defs><marker id="arrowhead8" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="8" markerHeight="6" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowheadPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker></defs></g><g class="edgePath LS-E LE-D" id="L-E-D" style="opacity: 1;"><path class="path" d="M403.4875030517578,64.94750213623047L428.4875030517578,64.94750213623047L453.98750467300414,65.44750213623047" marker-end="url(https://stackedit.io/app#arrowhead9)" style="fill:none"></path><defs><marker id="arrowhead9" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="8" markerHeight="6" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowheadPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker></defs></g></g><g class="edgeLabels"><g class="edgeLabel" transform="" style="opacity: 1;"><g transform="translate(0,0)" class="label"><rect rx="0" ry="0" width="0" height="0"></rect><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;"><span id="L-L-A-B" class="edgeLabel L-LS-A' L-LE-B"></span></div></foreignObject></g></g><g class="edgeLabel" transform="" style="opacity: 1;"><g transform="translate(0,0)" class="label"><rect rx="0" ry="0" width="0" height="0"></rect><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;"><span id="L-L-B-E" class="edgeLabel L-LS-B' L-LE-E"></span></div></foreignObject></g></g><g class="edgeLabel" transform="" style="opacity: 1;"><g transform="translate(0,0)" class="label"><rect rx="0" ry="0" width="0" height="0"></rect><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;"><span id="L-L-E-D" class="edgeLabel L-LS-E' L-LE-D"></span></div></foreignObject></g></g></g><g class="nodes"><g class="node default" id="flowchart-A-30" transform="translate(57.92499923706055,64.94750213623047)" style="opacity: 1;"><rect rx="0" ry="0" x="-49.92499923706055" y="-23.356249809265137" width="99.8499984741211" height="46.71249961853027" class="label-container"></rect><g class="label" transform="translate(0,0)"><g transform="translate(-39.92499923706055,-13.356249809265137)"><foreignObject width="79.8499984741211" height="26.712499618530273"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">component</div></foreignObject></g></g></g><g class="node default" id="flowchart-B-31" transform="translate(225.375,64.94750213623047)" style="opacity: 1;"><rect rx="5" ry="5" x="-67.5250015258789" y="-23.356249809265137" width="135.0500030517578" height="46.71249961853027" class="label-container"></rect><g class="label" transform="translate(0,0)"><g transform="translate(-57.525001525878906,-13.356249809265137)"><foreignObject width="115.05000305175781" height="26.712499618530273"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">async-actions.ts</div></foreignObject></g></g></g><g class="node default" id="flowchart-E-33" transform="translate(373.19375228881836,64.94750213623047)" style="opacity: 1;"><rect rx="5" ry="5" x="-30.293750762939453" y="-23.356249809265137" width="60.587501525878906" height="46.71249961853027" class="label-container"></rect><g class="label" transform="translate(0,0)"><g transform="translate(-20.293750762939453,-13.356249809265137)"><foreignObject width="40.587501525878906" height="26.712499618530273"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">api.ts</div></foreignObject></g></g></g><g class="node default" id="flowchart-D-35" transform="translate(510.4350051879883,64.94750213623047)" style="opacity: 1;"><polygon points="56.947500514984135,0 113.89500102996827,-56.947500514984135 56.947500514984135,-113.89500102996827 0,-56.947500514984135" transform="translate(-56.947500514984135,56.947500514984135)" class="label-container"></polygon><g class="label" transform="translate(0,0)"><g transform="translate(-29.918750762939453,-13.356249809265137)"><foreignObject width="59.837501525878906" height="26.712499618530273"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">Backend</div></foreignObject></g></g></g></g></g></g></svg></pre>
<p>You can use the diagram above to visualize the flow of data being requested in OMS. There a clear lines between server interactions and the redux actions. This is by design and should be maintained as much as possible. This allows us to change everything about how the application talks to the api without effecting the application. As long as the async action gets what its expecting everything will always work no matter the change.</p>
<pre><code>export const LoginSilent = (instance: IPublicClientApplication) => async (dispatch: any) => {
dispatch(setGlobalLoading(true));
const response = API.OAuthLoginRequest(instance);
if (!response.success) {
dispatch(setGlobalLoading(false))
return;
}
dispatch(setActiveUser(response.data));
dispatch(setGlobalLoading(false))
}
</code></pre>
<p>Using the api function created in the previous section, We can write an async-action like above to call our function. It can then interact with our global states in conjunction with the response it gets from the server.</p>
<p>We can now bind this action like a normal redux action and use it from anywhere within our application. And any component listening to the GlobalLoading or ActiveUser states will update when the action updates them.</p>
<h3 id="react-helmet">React Helmet</h3>
<p>React helmet is used to change the internet tab title dynamically per the page you are on. This should be used in place of any other method of switching the title. Documentation can be found <a href="https://www.npmjs.com/package/react-helmet">here</a></p>
<h3 id="skeleton-loading">Skeleton Loading</h3>
<p>React Skeleton should be used in cases of dynamically created pages where much of the pages content depends on an API call that will take some amount of time. This is prefered over a simple loading gif in most cases. Documentation can be found <a href="https://www.npmjs.com/package/react-loading-skeleton">here</a></p>
</div>
</body>
</html>