Repository: gitify-app/gitify
Pull request #1121 (2024-05-17 20:54:44)

feat(login): add help docs to login flows
Add help docs to the bottom left of each login flow.  

![Screenshot 2024-05-17 at 5 37 24 PM](https://github.com/gitify-app/gitify/assets/386277/3cd3b523-19a9-4540-8800-9c09cd02ea93)
![Screenshot 2024-05-17 at 5 37 30 PM](https://github.com/gitify-app/gitify/assets/386277/cc8d364f-42d3-4526-b9c5-fad2ebc8afd7)



Patch:
diff --git a/src/routes/LoginEnterprise.tsx b/src/routes/LoginEnterprise.tsx
index b38d77f2..52127944 100644
--- a/src/routes/LoginEnterprise.tsx
+++ b/src/routes/LoginEnterprise.tsx
@@ -1,6 +1,6 @@
 const ipcRenderer = require('electron').ipcRenderer;
 
-import { ArrowLeftIcon } from '@primer/octicons-react';
+import { ArrowLeftIcon, BookIcon, SignInIcon } from '@primer/octicons-react';
 
 import { type FC, useCallback, useContext, useEffect } from 'react';
 import { Form, type FormRenderProps } from 'react-final-form';
@@ -12,6 +12,9 @@ import type { AuthOptions } from '../types';
 import { getNewOAuthAppURL } from '../utils/auth';
 import { openExternalLink } from '../utils/comms';
 
+const GITHUB_DOCS_URL =
+  'https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authenticating-to-the-rest-api-with-an-oauth-app';
+
 interface IValues {
   hostname?: string;
   clientId?: string;
@@ -109,14 +112,28 @@ export const LoginEnterpriseRoute: FC = () => {
           placeholder="ABC123DEF456"
         />
 
-        <button
-          className="float-right px-4 py-2 my-4 bg-gray-300 font-semibold rounded text-sm text-center hover:bg-gray-500 hover:text-white dark:text-black focus:outline-none"
-          title="Login Button"
-          disabled={submitting || pristine}
-          type="submit"
-        >
-          Login
-        </button>
+        <div className="flex justify-between items-center">
+          <div className="text-xs italic hover:text-blue-500 justify-center items-center">
+            <button
+              type="button"
+              aria-label="GitHub Docs"
+              className={`px-2 py-1 text-xs ${buttonClasses}`}
+              onClick={() => openLink(GITHUB_DOCS_URL)}
+            >
+              <BookIcon size={12} /> Docs
+            </button>
+          </div>
+          <div className="justify-center items-center">
+            <button
+              className={`float-right px-4 py-2 my-4 ${buttonClasses}`}
+              title="Login"
+              disabled={submitting || pristine}
+              type="submit"
+            >
+              <SignInIcon size={14} /> Login
+            </button>
+          </div>
+        </div>
       </form>
     );
   };
diff --git a/src/routes/LoginWithToken.tsx b/src/routes/LoginWithToken.tsx
index bd4c4969..f7d567f2 100644
--- a/src/routes/LoginWithToken.tsx
+++ b/src/routes/LoginWithToken.tsx
@@ -1,4 +1,4 @@
-import { ArrowLeftIcon } from '@primer/octicons-react';
+import { ArrowLeftIcon, BookIcon, SignInIcon } from '@primer/octicons-react';
 
 import { type FC, useCallback, useContext, useState } from 'react';
 import { Form, type FormRenderProps } from 'react-final-form';
@@ -12,6 +12,9 @@ import { Constants } from '../utils/constants';
 
 import { format } from 'date-fns';
 
+const GITHUB_DOCS_URL =
+  'https://docs.github.com/en/enterprise-server@3.13/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens';
+
 interface IValues {
   token?: string;
   hostname?: string;
@@ -104,14 +107,28 @@ export const LoginWithToken: FC = () => {
           </div>
         )}
 
-        <button
-          className={`float-right px-4 py-2 my-4 ${buttonClasses}`}
-          title="Submit Button"
-          disabled={submitting || pristine}
-          type="submit"
-        >
-          Submit
-        </button>
+        <div className="flex justify-between items-center">
+          <div className="text-xs italic hover:text-blue-500 justify-center items-center">
+            <button
+              type="button"
+              aria-label="GitHub Docs"
+              className={`px-2 py-1 text-xs ${buttonClasses}`}
+              onClick={() => openLink(GITHUB_DOCS_URL)}
+            >
+              <BookIcon size={12} /> Docs
+            </button>
+          </div>
+          <div className="justify-center items-center">
+            <button
+              className={`float-right px-4 py-2 my-4 ${buttonClasses}`}
+              title="Login"
+              disabled={submitting || pristine}
+              type="submit"
+            >
+              <SignInIcon size={14} /> Login
+            </button>
+          </div>
+        </div>
       </form>
     );
   };


Test patch:
diff --git a/src/routes/LoginEnterprise.test.tsx b/src/routes/LoginEnterprise.test.tsx
index 2936fe77..97b31364 100644
--- a/src/routes/LoginEnterprise.test.tsx
+++ b/src/routes/LoginEnterprise.test.tsx
@@ -23,6 +23,7 @@ describe('routes/LoginEnterprise.tsx', () => {
   };
 
   beforeEach(() => {
+    openExternalMock.mockReset();
     mockNavigate.mockReset();
 
     jest.spyOn(ipcRenderer, 'send');
@@ -160,10 +161,24 @@ describe('routes/LoginEnterprise.tsx', () => {
       target: { value: 'abc' },
     });
 
-    fireEvent.submit(screen.getByTitle('Login Button'));
+    fireEvent.submit(screen.getByTitle('Login'));
 
     expect(screen.getByText('Invalid hostname.')).toBeTruthy();
     expect(screen.getByText('Invalid client id.')).toBeTruthy();
     expect(screen.getByText('Invalid client secret.')).toBeTruthy();
   });
+
+  it('should open help docs in the browser', async () => {
+    render(
+      <AppContext.Provider value={{ accounts: mockAccounts }}>
+        <MemoryRouter>
+          <LoginEnterpriseRoute />
+        </MemoryRouter>
+      </AppContext.Provider>,
+    );
+
+    fireEvent.click(screen.getByLabelText('GitHub Docs'));
+
+    expect(openExternalMock).toHaveBeenCalledTimes(1);
+  });
 });
diff --git a/src/routes/LoginWithToken.test.tsx b/src/routes/LoginWithToken.test.tsx
index 3c806772..eb969083 100644
--- a/src/routes/LoginWithToken.test.tsx
+++ b/src/routes/LoginWithToken.test.tsx
@@ -124,7 +124,7 @@ describe('routes/LoginWithToken.tsx', () => {
       target: { value: 'github.com' },
     });
 
-    fireEvent.submit(screen.getByTitle('Submit Button'));
+    fireEvent.submit(screen.getByTitle('Login'));
 
     await waitFor(() => expect(mockValidateToken).toHaveBeenCalledTimes(1));
 
@@ -150,7 +150,7 @@ describe('routes/LoginWithToken.tsx', () => {
       fireEvent.change(screen.getByLabelText('Hostname'), {
         target: { value: 'github.com' },
       });
-      fireEvent.submit(screen.getByTitle('Submit Button'));
+      fireEvent.submit(screen.getByTitle('Login'));
     });
 
     await waitFor(() => expect(mockValidateToken).toHaveBeenCalledTimes(1));
@@ -173,9 +173,23 @@ describe('routes/LoginWithToken.tsx', () => {
       target: { value: '123' },
     });
 
-    fireEvent.submit(screen.getByTitle('Submit Button'));
+    fireEvent.submit(screen.getByTitle('Login'));
 
     expect(screen.getByText('Invalid hostname.')).toBeDefined();
     expect(screen.getByText('Invalid token.')).toBeDefined();
   });
+
+  it('should open help docs in the browser', async () => {
+    render(
+      <AppContext.Provider value={{ validateToken: mockValidateToken }}>
+        <MemoryRouter>
+          <LoginWithToken />
+        </MemoryRouter>
+      </AppContext.Provider>,
+    );
+
+    fireEvent.click(screen.getByLabelText('GitHub Docs'));
+
+    expect(openExternalMock).toHaveBeenCalledTimes(1);
+  });
 });
diff --git a/src/routes/__snapshots__/LoginEnterprise.test.tsx.snap b/src/routes/__snapshots__/LoginEnterprise.test.tsx.snap
index 50381c37..23943ff5 100644
--- a/src/routes/__snapshots__/LoginEnterprise.test.tsx.snap
+++ b/src/routes/__snapshots__/LoginEnterprise.test.tsx.snap
@@ -141,14 +141,76 @@ exports[`routes/LoginEnterprise.tsx renders correctly 1`] = `
           value=""
         />
       </div>
-      <button
-        className="float-right px-4 py-2 my-4 bg-gray-300 font-semibold rounded text-sm text-center hover:bg-gray-500 hover:text-white dark:text-black focus:outline-none"
-        disabled={true}
-        title="Login Button"
-        type="submit"
+      <div
+        className="flex justify-between items-center"
       >
-        Login
-      </button>
+        <div
+          className="text-xs italic hover:text-blue-500 justify-center items-center"
+        >
+          <button
+            aria-label="GitHub Docs"
+            className="px-2 py-1 text-xs rounded bg-gray-300 font-semibold rounded text-sm text-center hover:bg-gray-500 hover:text-white dark:text-black focus:outline-none cursor-pointer"
+            onClick={[Function]}
+            type="button"
+          >
+            <svg
+              aria-hidden="true"
+              className="octicon octicon-book"
+              fill="currentColor"
+              focusable="false"
+              height={12}
+              style={
+                {
+                  "display": "inline-block",
+                  "overflow": "visible",
+                  "userSelect": "none",
+                  "verticalAlign": "text-bottom",
+                }
+              }
+              viewBox="0 0 16 16"
+              width={12}
+            >
+              <path
+                d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"
+              />
+            </svg>
+             Docs
+          </button>
+        </div>
+        <div
+          className="justify-center items-center"
+        >
+          <button
+            className="float-right px-4 py-2 my-4 rounded bg-gray-300 font-semibold rounded text-sm text-center hover:bg-gray-500 hover:text-white dark:text-black focus:outline-none cursor-pointer"
+            disabled={true}
+            title="Login"
+            type="submit"
+          >
+            <svg
+              aria-hidden="true"
+              className="octicon octicon-sign-in"
+              fill="currentColor"
+              focusable="false"
+              height={14}
+              style={
+                {
+                  "display": "inline-block",
+                  "overflow": "visible",
+                  "userSelect": "none",
+                  "verticalAlign": "text-bottom",
+                }
+              }
+              viewBox="0 0 16 16"
+              width={14}
+            >
+              <path
+                d="M2 2.75C2 1.784 2.784 1 3.75 1h2.5a.75.75 0 0 1 0 1.5h-2.5a.25.25 0 0 0-.25.25v10.5c0 .138.112.25.25.25h2.5a.75.75 0 0 1 0 1.5h-2.5A1.75 1.75 0 0 1 2 13.25Zm6.56 4.5h5.69a.75.75 0 0 1 0 1.5H8.56l1.97 1.97a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L6.22 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734Z"
+              />
+            </svg>
+             Login
+          </button>
+        </div>
+      </div>
     </form>
   </div>
 </div>
diff --git a/src/routes/__snapshots__/LoginWithToken.test.tsx.snap b/src/routes/__snapshots__/LoginWithToken.test.tsx.snap
index cac0708b..45269da7 100644
--- a/src/routes/__snapshots__/LoginWithToken.test.tsx.snap
+++ b/src/routes/__snapshots__/LoginWithToken.test.tsx.snap
@@ -129,14 +129,76 @@ exports[`routes/LoginWithToken.tsx renders correctly 1`] = `
           </div>
         </div>
       </div>
-      <button
-        className="float-right px-4 py-2 my-4 rounded bg-gray-300 font-semibold rounded text-sm text-center hover:bg-gray-500 hover:text-white dark:text-black focus:outline-none cursor-pointer"
-        disabled={true}
-        title="Submit Button"
-        type="submit"
+      <div
+        className="flex justify-between items-center"
       >
-        Submit
-      </button>
+        <div
+          className="text-xs italic hover:text-blue-500 justify-center items-center"
+        >
+          <button
+            aria-label="GitHub Docs"
+            className="px-2 py-1 text-xs rounded bg-gray-300 font-semibold rounded text-sm text-center hover:bg-gray-500 hover:text-white dark:text-black focus:outline-none cursor-pointer"
+            onClick={[Function]}
+            type="button"
+          >
+            <svg
+              aria-hidden="true"
+              className="octicon octicon-book"
+              fill="currentColor"
+              focusable="false"
+              height={12}
+              style={
+                {
+                  "display": "inline-block",
+                  "overflow": "visible",
+                  "userSelect": "none",
+                  "verticalAlign": "text-bottom",
+                }
+              }
+              viewBox="0 0 16 16"
+              width={12}
+            >
+              <path
+                d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"
+              />
+            </svg>
+             Docs
+          </button>
+        </div>
+        <div
+          className="justify-center items-center"
+        >
+          <button
+            className="float-right px-4 py-2 my-4 rounded bg-gray-300 font-semibold rounded text-sm text-center hover:bg-gray-500 hover:text-white dark:text-black focus:outline-none cursor-pointer"
+            disabled={true}
+            title="Login"
+            type="submit"
+          >
+            <svg
+              aria-hidden="true"
+              className="octicon octicon-sign-in"
+              fill="currentColor"
+              focusable="false"
+              height={14}
+              style={
+                {
+                  "display": "inline-block",
+                  "overflow": "visible",
+                  "userSelect": "none",
+                  "verticalAlign": "text-bottom",
+                }
+              }
+              viewBox="0 0 16 16"
+              width={14}
+            >
+              <path
+                d="M2 2.75C2 1.784 2.784 1 3.75 1h2.5a.75.75 0 0 1 0 1.5h-2.5a.25.25 0 0 0-.25.25v10.5c0 .138.112.25.25.25h2.5a.75.75 0 0 1 0 1.5h-2.5A1.75 1.75 0 0 1 2 13.25Zm6.56 4.5h5.69a.75.75 0 0 1 0 1.5H8.56l1.97 1.97a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L6.22 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734Z"
+              />
+            </svg>
+             Login
+          </button>
+        </div>
+      </div>
     </form>
   </div>
 </div>