mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
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:
parent
2a12619f3a
commit
38628c106f
18
.github/workflows/lint.yml
vendored
Normal file
18
.github/workflows/lint.yml
vendored
Normal 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
27
frontend/.eslintrc
Normal 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
2
frontend/.husky/pre-commit
Executable file
@ -0,0 +1,2 @@
|
||||
cd frontend
|
||||
npx lint-staged
|
||||
3
frontend/.prettierrc.json
Normal file
3
frontend/.prettierrc.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"trailingComma": "all"
|
||||
}
|
||||
925
frontend/package-lock.json
generated
925
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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'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's
|
||||
instructions.
|
||||
</li>
|
||||
{/* Add more tasks */}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default Planner;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user