Add formatting and linting for typescript components (#32)

* Add formatting and linting for typescript components

* Add lint GitHub Action

* Add push to GitHub action trigger

* Update GitHub action node version

* Add Husky

* Update husky settings

* Fix Husky settings

* Should not pass

* Test

* Test

* Finalize

* Add --legacy-peer-deps flag to npm ci

* Fix lint pre-commit hook

* Compile

* Remove eslint and prettier command quotes
This commit is contained in:
Jim Su 2024-03-17 14:57:20 -04:00 committed by GitHub
parent 2a12619f3a
commit 38628c106f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 989 additions and 160 deletions

18
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 20
- run: |
cd frontend
npm ci --legacy-peer-deps
- run: |
cd frontend
npm run lint

27
frontend/.eslintrc Normal file
View File

@ -0,0 +1,27 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"extends": [
"airbnb",
"airbnb-typescript",
"prettier",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": ["error"]
},
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"rules": {
"jsx-a11y/no-static-element-interactions": "off",
"jsx-a11y/click-events-have-key-events": "off",
"react/no-array-index-key": "off"
}
}
]
}

2
frontend/.husky/pre-commit Executable file
View File

@ -0,0 +1,2 @@
cd frontend
npx lint-staged

View File

@ -0,0 +1,3 @@
{
"trailingComma": "all"
}

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@types/react-syntax-highlighter": "^15.5.11",
"eslint-config-airbnb-typescript": "^18.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
@ -22,7 +23,20 @@
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
"eject": "react-scripts eject",
"lint": "eslint src/**/*.ts* && prettier --check src/**/*.ts*",
"prepare": "cd .. && husky install frontend/.husky"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.ts*": [
"eslint --fix",
"prettier --write"
]
},
"eslintConfig": {
"extends": [
@ -41,5 +55,19 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@typescript-eslint/parser": "^5.62.0",
"eslint": "^8.57.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^8.0.0",
"lint-staged": "^15.2.2",
"prettier": "^3.2.5"
}
}

View File

@ -1,8 +1,8 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
import React from "react";
import { render, screen } from "@testing-library/react";
import App from "./App";
test('renders learn react link', () => {
test("renders learn react link", () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();

View File

@ -1,12 +1,14 @@
// App.tsx
import React, { useState } from 'react';
import './App.css';
import ChatInterface from './components/ChatInterface';
import Terminal from './components/Terminal';
import Planner from './components/Planner';
import React, { useState } from "react";
import "./App.css";
import ChatInterface from "./components/ChatInterface";
import Terminal from "./components/Terminal";
import Planner from "./components/Planner";
const App: React.FC = () => {
const [activeTab, setActiveTab] = useState<'terminal' | 'planner'>('terminal');
function App(): JSX.Element {
const [activeTab, setActiveTab] = useState<"terminal" | "planner">(
"terminal",
);
return (
<div className="app">
@ -16,24 +18,24 @@ const App: React.FC = () => {
<div className="right-pane">
<div className="tab-container">
<div
className={`tab ${activeTab === 'terminal' ? 'active' : ''}`}
onClick={() => setActiveTab('terminal')}
className={`tab ${activeTab === "terminal" ? "active" : ""}`}
onClick={() => setActiveTab("terminal")}
>
Shell
</div>
<div
className={`tab ${activeTab === 'planner' ? 'active' : ''}`}
onClick={() => setActiveTab('planner')}
className={`tab ${activeTab === "planner" ? "active" : ""}`}
onClick={() => setActiveTab("planner")}
>
Planner
</div>
</div>
<div className="tab-content">
{activeTab === 'terminal' ? <Terminal /> : <Planner />}
{activeTab === "terminal" ? <Terminal /> : <Planner />}
</div>
</div>
</div>
);
};
}
export default App;
export default App;

View File

@ -1,28 +1,43 @@
import React, { useState } from 'react';
import './ChatInterface.css';
import userAvatar from '../assets/user-avatar.png';
import assistantAvatar from '../assets/assistant-avatar.png';
import React, { useState } from "react";
import "./ChatInterface.css";
import userAvatar from "../assets/user-avatar.png";
import assistantAvatar from "../assets/assistant-avatar.png";
interface Message {
content: string;
sender: 'user' | 'assistant';
sender: "user" | "assistant";
}
const ChatInterface: React.FC = () => {
function ChatInterface(): JSX.Element {
const [messages, setMessages] = useState<Message[]>([
{ content: "I want you to setup this project: https://github.com/mckaywrigley/assistant-ui", sender: 'user' },
{ content: "Got it, I'll get started on setting up the assistant UI project from the GitHub link you provided. I'll update you on my progress.", sender: 'assistant' },
{ content: "Cloned repo from GitHub.", sender: 'assistant' },
{ content: "You're doing great! Keep it up :)", sender: 'user' },
{ content: "Thanks! I've cloned the repo and am currently going through the README to make sure we get everything set up right. There's a detailed guide for local setup as well as instructions for hosting it. I'll follow the steps and keep you posted on the progress! If there are any specific configurations or features you want to prioritize, just let me know.", sender: 'assistant' },
{ content: "Installed project dependencies using npm.", sender: 'assistant' }
{
content:
"I want you to setup this project: https://github.com/mckaywrigley/assistant-ui",
sender: "user",
},
{
content:
"Got it, I'll get started on setting up the assistant UI project from the GitHub link you provided. I'll update you on my progress.",
sender: "assistant",
},
{ content: "Cloned repo from GitHub.", sender: "assistant" },
{ content: "You're doing great! Keep it up :)", sender: "user" },
{
content:
"Thanks! I've cloned the repo and am currently going through the README to make sure we get everything set up right. There's a detailed guide for local setup as well as instructions for hosting it. I'll follow the steps and keep you posted on the progress! If there are any specific configurations or features you want to prioritize, just let me know.",
sender: "assistant",
},
{
content: "Installed project dependencies using npm.",
sender: "assistant",
},
]);
const [inputMessage, setInputMessage] = useState('');
const [inputMessage, setInputMessage] = useState("");
const handleSendMessage = () => {
if (inputMessage.trim() !== '') {
setMessages([...messages, { content: inputMessage, sender: 'user' }]);
setInputMessage('');
if (inputMessage.trim() !== "") {
setMessages([...messages, { content: inputMessage, sender: "user" }]);
setInputMessage("");
}
};
@ -32,7 +47,7 @@ const ChatInterface: React.FC = () => {
{messages.map((msg, index) => (
<div key={index} className="message">
<img
src={msg.sender === 'user' ? userAvatar : assistantAvatar}
src={msg.sender === "user" ? userAvatar : assistantAvatar}
alt={`${msg.sender} avatar`}
className="avatar"
/>
@ -47,12 +62,12 @@ const ChatInterface: React.FC = () => {
onChange={(e) => setInputMessage(e.target.value)}
placeholder="Send a message (won't interrupt the Assistant)"
/>
<button onClick={handleSendMessage}>
<button type="button" onClick={handleSendMessage}>
<span className="button-text">Send</span>
</button>
</div>
</div>
);
};
}
export default ChatInterface;
export default ChatInterface;

View File

@ -1,13 +1,17 @@
import React from 'react';
import React from "react";
const Planner: React.FC = () => {
function Planner(): JSX.Element {
return (
<div className="planner">
<h3>Current Focus: Set up the development environment according to the project's instructions.</h3>
<h3>
Current Focus: Set up the development environment according to the
project&apos;s instructions.
</h3>
<ul>
<li>
<input type="checkbox" checked readOnly />
Clone the repository and review the README for project setup instructions.
Clone the repository and review the README for project setup
instructions.
</li>
<li>
<input type="checkbox" checked readOnly />
@ -15,12 +19,13 @@ const Planner: React.FC = () => {
</li>
<li>
<input type="checkbox" />
Set up the development environment according to the project's instructions.
Set up the development environment according to the project&apos;s
instructions.
</li>
{/* Add more tasks */}
</ul>
</div>
);
};
}
export default Planner;

View File

@ -1,8 +1,8 @@
import React from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import React from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { atomDark } from "react-syntax-highlighter/dist/esm/styles/prism";
const Terminal: React.FC = () => {
function Terminal(): JSX.Element {
const terminalOutput = `> chatbot-ui@2.0.0 prepare
> husky install
@ -25,6 +25,6 @@ npm notice New minor version of npm available! 10.7.3 -> 10.9.0
</SyntaxHighlighter>
</div>
);
};
}
export default Terminal;
export default Terminal;

View File

@ -1,16 +1,16 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
document.getElementById("root") as HTMLElement,
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
</React.StrictMode>,
);
// If you want to start measuring performance in your app, pass a function

View File

@ -1,8 +1,8 @@
import { ReportHandler } from 'web-vitals';
import { ReportHandler } from "web-vitals";
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);

View File

@ -2,4 +2,4 @@
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
import "@testing-library/jest-dom";

View File

@ -18,7 +18,7 @@
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
"jsx": "react"
},
"include": [
"src"