diff --git a/.github/workflows/lint-ui.yml b/.github/workflows/lint-ui.yml new file mode 100644 index 00000000..a8c90ae6 --- /dev/null +++ b/.github/workflows/lint-ui.yml @@ -0,0 +1,36 @@ +name: Lint UI changes + +on: + push: + branches: ["main"] + paths: + - '.github/workflows/lint-ui.yml' + - 'ui/*' + - '!ui/README.md' + pull_request: + branches: ["main"] + paths: + - '.github/workflows/lint-ui.yml' + - 'ui/*' + - '!ui/README.md' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + npm-lint: + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Install dependencies + run: npm install + working-directory: ui + - name: Run UI linting + run: npm run lint + working-directory: ui diff --git a/ui/package.json b/ui/package.json index 5bd6e787..1190cc72 100644 --- a/ui/package.json +++ b/ui/package.json @@ -12,8 +12,8 @@ "build": "webpack --config webpack.prod.js", "start": "dotenv -e .env -- cross-env sirv dist --cors --single --host --port 8080", "start:dev": "webpack serve --color --progress --config webpack.dev.js", - "eslint": "eslint --ext .tsx,.js ./src/", - "lint": "npm run eslint", + "lint": "eslint --ext .tsx,.js ./src/", + "lint-fix": "eslint --ext .tsx,.js ./src/ --fix", "format": "prettier --check --write ./src/**/*.{tsx,ts}", "type-check": "tsc --noEmit", "clean": "rimraf dist", diff --git a/ui/src/app/AppLayout/AppLayout.tsx b/ui/src/app/AppLayout/AppLayout.tsx index adfdac06..592e5559 100644 --- a/ui/src/app/AppLayout/AppLayout.tsx +++ b/ui/src/app/AppLayout/AppLayout.tsx @@ -1,23 +1,22 @@ import * as React from 'react'; import { NavLink, useLocation, useNavigate } from 'react-router-dom'; -import { - Brand, - Button, - Masthead, - MastheadBrand, - MastheadMain, - MastheadToggle, - MastheadContent, - Nav, - NavExpandable, - NavItem, - NavList, - Page, - PageSidebar, - PageSidebarBody, - SkipToContent -} from '@patternfly/react-core'; -import { BarsIcon, SignOutAltIcon } from '@patternfly/react-icons'; +import { Brand } from '@patternfly/react-core/dist/dynamic/components/Brand' +import { Button } from '@patternfly/react-core/dist/dynamic/components/Button' +import { Masthead } from '@patternfly/react-core/dist/dynamic/components/Masthead' +import { MastheadBrand } from '@patternfly/react-core/dist/dynamic/components/Masthead' +import { MastheadContent } from '@patternfly/react-core/dist/dynamic/components/Masthead' +import { MastheadMain } from '@patternfly/react-core/dist/dynamic/components/Masthead' +import { MastheadToggle } from '@patternfly/react-core/dist/dynamic/components/Masthead' +import { Nav } from '@patternfly/react-core/dist/dynamic/components/Nav' +import { NavExpandable } from '@patternfly/react-core/dist/dynamic/components/Nav' +import { NavItem } from '@patternfly/react-core/dist/dynamic/components/Nav' +import { NavList } from '@patternfly/react-core/dist/dynamic/components/Nav' +import { Page } from '@patternfly/react-core/dist/dynamic/components/Page' +import { PageSidebar } from '@patternfly/react-core/dist/dynamic/components/Page' +import { PageSidebarBody } from '@patternfly/react-core/dist/dynamic/components/Page' +import { SkipToContent } from '@patternfly/react-core/dist/dynamic/components/SkipToContent' +import BarsIcon from '@patternfly/react-icons/dist/dynamic/icons/bars-icon' +import SignOutAltIcon from '@patternfly/react-icons/dist/dynamic/icons/sign-out-alt-icon' import { IAppRoute, IAppRouteGroup, routes } from '@app/routes'; import { useAuth } from '../common/AuthContext'; import { useTheme } from '../context/ThemeContext'; diff --git a/ui/src/app/Chat/Chat.tsx b/ui/src/app/Chat/Chat.tsx index 051cfc8d..fe6ccc23 100644 --- a/ui/src/app/Chat/Chat.tsx +++ b/ui/src/app/Chat/Chat.tsx @@ -1,9 +1,15 @@ // Chat.tsx -import React, { useState, useRef, useEffect } from 'react'; -import { ActionGroup, Button, Form, FormGroup, TextInput, Spinner } from '@patternfly/react-core'; +import React, { useEffect, useRef, useState } from 'react'; +import { ActionGroup } from '@patternfly/react-core/dist/dynamic/components/Form' +import { Button } from '@patternfly/react-core/dist/dynamic/components/Button' +import { Form } from '@patternfly/react-core/dist/dynamic/components/Form' +import { FormGroup } from '@patternfly/react-core/dist/dynamic/components/Form' +import { Spinner } from '@patternfly/react-core/dist/dynamic/components/Spinner' +import { TextInput } from '@patternfly/react-core/dist/dynamic/components/TextInput' import { usePostChat } from "@app/common/HooksPostChat"; import './ChatForm.css'; -import { UserIcon, CopyIcon } from "@patternfly/react-icons"; +import UserIcon from '@patternfly/react-icons/dist/dynamic/icons/user-icon' +import CopyIcon from '@patternfly/react-icons/dist/dynamic/icons/copy-icon' import botIconSrc from '../bgimages/bot-icon-chat-32x32.svg'; interface Message { diff --git a/ui/src/app/Contribute/Knowledge/Knowledge.tsx b/ui/src/app/Contribute/Knowledge/Knowledge.tsx index 1fa72651..6af49595 100644 --- a/ui/src/app/Contribute/Knowledge/Knowledge.tsx +++ b/ui/src/app/Contribute/Knowledge/Knowledge.tsx @@ -1,17 +1,15 @@ import React, { useState } from 'react'; import './Knowledge.css'; import { usePostKnowledgePR } from "@app/common/HooksPostKnowledgePR"; -import { - Alert, - AlertActionCloseButton, - ActionGroup, - Button, - Text, - TextInput, - Form, - FormGroup, - TextArea, -} from '@patternfly/react-core'; +import { ActionGroup } from '@patternfly/react-core/dist/dynamic/components/Form' +import { Alert } from '@patternfly/react-core/dist/dynamic/components/Alert' +import { AlertActionCloseButton } from '@patternfly/react-core/dist/dynamic/components/Alert' +import { Button } from '@patternfly/react-core/dist/dynamic/components/Button' +import { Form } from '@patternfly/react-core/dist/dynamic/components/Form' +import { FormGroup } from '@patternfly/react-core/dist/dynamic/components/Form' +import { Text } from '@patternfly/react-core/dist/dynamic/components/Text' +import { TextArea } from '@patternfly/react-core/dist/dynamic/components/TextArea' +import { TextInput } from '@patternfly/react-core/dist/dynamic/components/TextInput' export const KnowledgeForm: React.FunctionComponent = () => { const [email, setEmail] = useState(''); diff --git a/ui/src/app/Contribute/Skill/Skill.tsx b/ui/src/app/Contribute/Skill/Skill.tsx index 70e01bd8..f1ca08e6 100644 --- a/ui/src/app/Contribute/Skill/Skill.tsx +++ b/ui/src/app/Contribute/Skill/Skill.tsx @@ -1,17 +1,15 @@ import React, { useState } from 'react'; import './Skill.css'; import { usePostSkillPR } from "@app/common/HooksPostSkillPR"; -import { - Alert, - AlertActionCloseButton, - ActionGroup, - Button, - Text, - TextInput, - Form, - FormGroup, - TextArea, -} from '@patternfly/react-core'; +import { ActionGroup } from '@patternfly/react-core/dist/dynamic/components/Form' +import { Alert } from '@patternfly/react-core/dist/dynamic/components/Alert' +import { AlertActionCloseButton } from '@patternfly/react-core/dist/dynamic/components/Alert' +import { Button } from '@patternfly/react-core/dist/dynamic/components/Button' +import { Form } from '@patternfly/react-core/dist/dynamic/components/Form' +import { FormGroup } from '@patternfly/react-core/dist/dynamic/components/Form' +import { Text } from '@patternfly/react-core/dist/dynamic/components/Text' +import { TextArea } from '@patternfly/react-core/dist/dynamic/components/TextArea' +import { TextInput } from '@patternfly/react-core/dist/dynamic/components/TextInput' export const SkillForm: React.FunctionComponent = () => { const [email, setEmail] = useState(''); diff --git a/ui/src/app/Dashboard/Dashboard.tsx b/ui/src/app/Dashboard/Dashboard.tsx index 785a6bf6..63834514 100644 --- a/ui/src/app/Dashboard/Dashboard.tsx +++ b/ui/src/app/Dashboard/Dashboard.tsx @@ -1,7 +1,11 @@ // Dashboard.tsx import JobDurationChart from "@app/components/ChartJobDuration"; import * as React from 'react'; -import { PageSection, Title, Card, CardBody, CardTitle } from '@patternfly/react-core'; +import { Card } from '@patternfly/react-core/dist/dynamic/components/Card' +import { CardBody } from '@patternfly/react-core/dist/dynamic/components/Card' +import { CardTitle } from '@patternfly/react-core/dist/dynamic/components/Card' +import { PageSection } from '@patternfly/react-core/dist/dynamic/components/Page' +import { Title } from '@patternfly/react-core/dist/dynamic/components/Title' import JobStatusPieChart from '@app/components/ChartJobDistribution'; const Dashboard: React.FunctionComponent = () => { diff --git a/ui/src/app/Jobs/All/All.tsx b/ui/src/app/Jobs/All/All.tsx index fb32a00e..670fafe2 100644 --- a/ui/src/app/Jobs/All/All.tsx +++ b/ui/src/app/Jobs/All/All.tsx @@ -5,9 +5,12 @@ import JobsLayout from "@app/common/JobsLayout"; import { formatDate } from "@app/utils/dateUtils"; import { getSortParams } from "@app/utils/tableUtils"; import * as React from 'react'; -import { PageSection, Title } from '@patternfly/react-core'; -import { Table, Thead, Tr, Th, Td, Tbody, ExpandableRowContent } from '@patternfly/react-table'; -import { GithubIcon, CodeBranchIcon, AngleRightIcon } from '@patternfly/react-icons'; +import { PageSection } from '@patternfly/react-core/dist/dynamic/components/Page' +import { Title } from '@patternfly/react-core/dist/dynamic/components/Title' +import { ExpandableRowContent, Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; +import AngleRightIcon from '@patternfly/react-icons/dist/dynamic/icons/angle-right-icon' +import CodeBranchIcon from '@patternfly/react-icons/dist/dynamic/icons/code-branch-icon' +import GithubIcon from '@patternfly/react-icons/dist/dynamic/icons/github-icon' const AllJobs: React.FunctionComponent = () => { const jobs = useFetchJobs(); diff --git a/ui/src/app/Jobs/Failed/Failed.tsx b/ui/src/app/Jobs/Failed/Failed.tsx index 2f27faaf..ea7e7114 100644 --- a/ui/src/app/Jobs/Failed/Failed.tsx +++ b/ui/src/app/Jobs/Failed/Failed.tsx @@ -2,10 +2,13 @@ import { Columns } from "@app/common/DisplayColumns"; import useFetchJobs from "@app/common/HooksApiServer"; import JobsLayout from "@app/common/JobsLayout"; import { formatDate } from '@app/utils/dateUtils'; -import {GithubIcon, AngleRightIcon, CodeBranchIcon} from "@patternfly/react-icons"; +import AngleRightIcon from '@patternfly/react-icons/dist/dynamic/icons/angle-right-icon' +import CodeBranchIcon from '@patternfly/react-icons/dist/dynamic/icons/code-branch-icon' +import GithubIcon from '@patternfly/react-icons/dist/dynamic/icons/github-icon' import * as React from 'react'; -import { PageSection, Title } from '@patternfly/react-core'; -import { Table, Thead, Tr, Th, Td, Tbody, ExpandableRowContent } from '@patternfly/react-table'; +import { PageSection } from '@patternfly/react-core/dist/dynamic/components/Page' +import { Title } from '@patternfly/react-core/dist/dynamic/components/Title' +import { ExpandableRowContent, Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; import { getSortParams } from '@app/utils/tableUtils'; const FailedJobs: React.FunctionComponent = () => { diff --git a/ui/src/app/Jobs/Pending/Pending.tsx b/ui/src/app/Jobs/Pending/Pending.tsx index 601d4f4f..22c0c0a7 100644 --- a/ui/src/app/Jobs/Pending/Pending.tsx +++ b/ui/src/app/Jobs/Pending/Pending.tsx @@ -3,10 +3,13 @@ import { Columns } from "@app/common/DisplayColumns"; import useFetchJobs from "@app/common/HooksApiServer"; import JobsLayout from "@app/common/JobsLayout"; import { formatDate } from '@app/utils/dateUtils'; -import {GithubIcon, AngleRightIcon, CodeBranchIcon} from "@patternfly/react-icons"; +import AngleRightIcon from '@patternfly/react-icons/dist/dynamic/icons/angle-right-icon' +import CodeBranchIcon from '@patternfly/react-icons/dist/dynamic/icons/code-branch-icon' +import GithubIcon from '@patternfly/react-icons/dist/dynamic/icons/github-icon' import * as React from 'react'; -import { PageSection, Title } from '@patternfly/react-core'; -import { Table, Thead, Tr, Th, Td, Tbody, ExpandableRowContent } from '@patternfly/react-table'; +import { PageSection } from '@patternfly/react-core/dist/dynamic/components/Page' +import { Title } from '@patternfly/react-core/dist/dynamic/components/Title' +import { ExpandableRowContent, Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; import { getSortParams } from '@app/utils/tableUtils'; const PendingJobs: React.FunctionComponent = () => { diff --git a/ui/src/app/Jobs/Running/Running.tsx b/ui/src/app/Jobs/Running/Running.tsx index 8d96e47b..834bbaaa 100644 --- a/ui/src/app/Jobs/Running/Running.tsx +++ b/ui/src/app/Jobs/Running/Running.tsx @@ -3,10 +3,13 @@ import { Columns } from "@app/common/DisplayColumns"; import useFetchJobs from "@app/common/HooksApiServer"; import JobsLayout from "@app/common/JobsLayout"; import { formatDate } from '@app/utils/dateUtils'; -import {GithubIcon, AngleRightIcon, CodeBranchIcon} from "@patternfly/react-icons"; +import AngleRightIcon from '@patternfly/react-icons/dist/dynamic/icons/angle-right-icon' +import CodeBranchIcon from '@patternfly/react-icons/dist/dynamic/icons/code-branch-icon' +import GithubIcon from '@patternfly/react-icons/dist/dynamic/icons/github-icon' import * as React from 'react'; -import { PageSection, Title } from '@patternfly/react-core'; -import { Table, Thead, Tr, Th, Td, Tbody, ExpandableRowContent } from '@patternfly/react-table'; +import { PageSection } from '@patternfly/react-core/dist/dynamic/components/Page' +import { Title } from '@patternfly/react-core/dist/dynamic/components/Title' +import { ExpandableRowContent, Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; import { getSortParams } from '@app/utils/tableUtils'; const RunningJobs: React.FunctionComponent = () => { diff --git a/ui/src/app/Jobs/Success/Success.tsx b/ui/src/app/Jobs/Success/Success.tsx index 27d09601..b7fc8328 100644 --- a/ui/src/app/Jobs/Success/Success.tsx +++ b/ui/src/app/Jobs/Success/Success.tsx @@ -3,10 +3,13 @@ import { Columns } from "@app/common/DisplayColumns"; import useFetchJobs from "@app/common/HooksApiServer"; import JobsLayout from "@app/common/JobsLayout"; import { formatDate } from '@app/utils/dateUtils'; -import {GithubIcon, AngleRightIcon, CodeBranchIcon} from "@patternfly/react-icons"; +import AngleRightIcon from '@patternfly/react-icons/dist/dynamic/icons/angle-right-icon' +import CodeBranchIcon from '@patternfly/react-icons/dist/dynamic/icons/code-branch-icon' +import GithubIcon from '@patternfly/react-icons/dist/dynamic/icons/github-icon' import * as React from 'react'; -import { PageSection, Title } from '@patternfly/react-core'; -import { Table, Thead, Tr, Th, Td, Tbody, ExpandableRowContent } from '@patternfly/react-table'; +import { PageSection } from '@patternfly/react-core/dist/dynamic/components/Page' +import { Title } from '@patternfly/react-core/dist/dynamic/components/Title' +import { ExpandableRowContent, Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; import { getSortParams } from '@app/utils/tableUtils'; const SuccessJobs: React.FunctionComponent = () => { diff --git a/ui/src/app/Login/Login.tsx b/ui/src/app/Login/Login.tsx index bae9bba6..b9551770 100644 --- a/ui/src/app/Login/Login.tsx +++ b/ui/src/app/Login/Login.tsx @@ -1,13 +1,11 @@ // Login.tsx -import {ExclamationCircleIcon} from "@patternfly/react-icons"; -import React, { useState, ChangeEvent } from "react"; -import { - LoginForm, - LoginPage, - ListItem, - ListVariant, - LoginFooterItem -} from "@patternfly/react-core"; +import {ExclamationCircleIcon} from "@patternfly/react-icons/dist/dynamic/icons/exclamation-circle-icon"; +import React, { ChangeEvent, useState } from "react"; +import { LoginForm } from '@patternfly/react-core/dist/dynamic/components/LoginPage' +import { LoginPage } from '@patternfly/react-core/dist/dynamic/components/LoginPage' +import { ListItem } from '@patternfly/react-core/dist/dynamic/components/List' +import { ListVariant } from '@patternfly/react-core/dist/dynamic/components/List' +import { LoginFooterItem } from '@patternfly/react-core/dist/dynamic/components/LoginPage' import logo from '@app/bgimages/InstructLab-Logo.svg'; import { useNavigate } from 'react-router-dom'; import { useAuth } from "../common/AuthContext"; diff --git a/ui/src/app/NotFound/NotFound.tsx b/ui/src/app/NotFound/NotFound.tsx index 6a67b118..04d0d931 100644 --- a/ui/src/app/NotFound/NotFound.tsx +++ b/ui/src/app/NotFound/NotFound.tsx @@ -1,15 +1,13 @@ // NotFound.tsx import * as React from 'react'; -import { ExclamationTriangleIcon } from '@patternfly/react-icons'; -import { - Button, - EmptyState, - EmptyStateBody, - EmptyStateFooter, - EmptyStateHeader, - EmptyStateIcon, - PageSection, -} from '@patternfly/react-core'; +import { ExclamationTriangleIcon } from '@patternfly/react-icons/dist/dynamic/icons/exclamation-triangle-icon'; +import { Button } from '@patternfly/react-core/dist/dynamic/components/Button' +import { EmptyState } from '@patternfly/react-core/dist/dynamic/components/EmptyState' +import { EmptyStateBody } from '@patternfly/react-core/dist/dynamic/components/EmptyState' +import { EmptyStateFooter } from '@patternfly/react-core/dist/dynamic/components/EmptyState' +import { EmptyStateHeader } from '@patternfly/react-core/dist/dynamic/components/EmptyState' +import { EmptyStateIcon } from '@patternfly/react-core/dist/dynamic/components/EmptyState' +import { PageSection } from '@patternfly/react-core/dist/dynamic/components/Page' import { useNavigate } from 'react-router-dom'; const NotFound: React.FunctionComponent = () => { @@ -24,7 +22,7 @@ const NotFound: React.FunctionComponent = () => { } headingLevel="h1" /> - We didn't find a page that matches the address you navigated to. + We did not find a page that matches the address you navigated to. diff --git a/ui/src/app/common/AuthContext.tsx b/ui/src/app/common/AuthContext.tsx index b264f610..d9812962 100644 --- a/ui/src/app/common/AuthContext.tsx +++ b/ui/src/app/common/AuthContext.tsx @@ -1,5 +1,5 @@ // AuthContext.tsx -import React, { createContext, useContext, useState, useEffect } from 'react'; +import React, { createContext, useContext, useEffect, useState } from 'react'; const AuthContext = createContext(null); diff --git a/ui/src/app/components/ChartJobDistribution.tsx b/ui/src/app/components/ChartJobDistribution.tsx index da157eb6..39554421 100644 --- a/ui/src/app/components/ChartJobDistribution.tsx +++ b/ui/src/app/components/ChartJobDistribution.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { ChartPie, ChartLegend, ChartThemeColor } from '@patternfly/react-charts'; +import { ChartLegend, ChartPie, ChartThemeColor } from '@patternfly/react-charts'; import { useNavigate } from 'react-router-dom'; import useFetchJobs from '@app/common/HooksApiServer'; diff --git a/ui/src/app/components/ThemeToggle.tsx b/ui/src/app/components/ThemeToggle.tsx index 90055512..78604768 100644 --- a/ui/src/app/components/ThemeToggle.tsx +++ b/ui/src/app/components/ThemeToggle.tsx @@ -1,8 +1,9 @@ // ThemeToggle.tsx import React from 'react'; import { useTheme } from '../context/ThemeContext'; -import { Button } from '@patternfly/react-core'; -import { SunIcon, MoonIcon } from '@patternfly/react-icons'; +import { Button } from '@patternfly/react-core/dist/dynamic/components/Button'; +import MoonIcon from '@patternfly/react-icons/dist/dynamic/icons/moon-icon' +import SunIcon from '@patternfly/react-icons/dist/dynamic/icons/sun-icon' const ThemeToggle: React.FC = () => { const { theme, setTheme } = useTheme(); diff --git a/ui/src/app/context/ThemeContext.tsx b/ui/src/app/context/ThemeContext.tsx index 4992a4d4..e35370ea 100644 --- a/ui/src/app/context/ThemeContext.tsx +++ b/ui/src/app/context/ThemeContext.tsx @@ -1,5 +1,5 @@ // ThemeContext.tsx -import React, { createContext, useContext, useState, ReactNode } from 'react'; +import React, { ReactNode, createContext, useContext, useState } from 'react'; type Theme = 'light' | 'dark'; interface ThemeContextType { diff --git a/ui/src/app/index.tsx b/ui/src/app/index.tsx index 10c2a170..5b9dd68b 100644 --- a/ui/src/app/index.tsx +++ b/ui/src/app/index.tsx @@ -3,7 +3,7 @@ import { AuthProvider } from '@app/common/AuthContext'; import { ThemeProvider } from '@app/context/ThemeContext'; import * as React from 'react'; import '@patternfly/react-core/dist/styles/base.css'; -import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; +import { BrowserRouter as Route, Router, Routes } from 'react-router-dom'; import { AppLayout } from '@app/AppLayout/AppLayout'; import { AppRoutes } from '@app/routes'; import Login from '@app/Login/Login'; diff --git a/ui/src/app/routes.tsx b/ui/src/app/routes.tsx index b9410162..d42e95bd 100644 --- a/ui/src/app/routes.tsx +++ b/ui/src/app/routes.tsx @@ -1,6 +1,6 @@ // routes.tsx import React from 'react'; -import { Routes, Route, Navigate } from 'react-router-dom'; +import { Navigate, Route, Routes } from 'react-router-dom'; import { useAuth } from '@app/common/AuthContext'; import { NotFound } from '@app/NotFound/NotFound'; import { Dashboard } from '@app/Dashboard/Dashboard'; @@ -109,7 +109,7 @@ const routes: Array = [ export const AppRoutes = (): React.ReactElement => ( } /> - {routes.map((route, idx) => + {routes.map((route) => 'routes' in route ? ( route.routes?.map((subRoute) => ( } />