Skip to content

Commit

Permalink
refactor: modernize codebase style
Browse files Browse the repository at this point in the history
  • Loading branch information
TheChristophe committed Sep 13, 2023
1 parent d6be063 commit 39623ce
Show file tree
Hide file tree
Showing 132 changed files with 16,020 additions and 10,844 deletions.
106 changes: 101 additions & 5 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,103 @@
{
"extends": [ "next/core-web-vitals", "prettier" ],

"rules": {
"semi": "warn"
}
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"next/core-web-vitals",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/strict",
"plugin:sonarjs/recommended",
"plugin:eslint-comments/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:cypress/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"ecmaFeatures": {
"jsx": true,
"modules": true
},
"sourceType": "module"
},
"plugins": ["@typescript-eslint", "sonarjs", "jsx-a11y", "github", "cypress", "import"],
"rules": {
"@next/next/no-img-element": "off",
"@typescript-eslint/ban-ts-comment": "warn",
"curly": "error",
"no-console": [
"warn",
{
"allow": ["warn", "error"]
}
],
"quotes": [
"warn",
"single",
{
"avoidEscape": true
}
],
"prefer-template": "warn",
"react/jsx-curly-brace-presence": [
"warn",
{
"props": "never"
}
],
"react/jsx-boolean-value": ["error", "never"],
"react/jsx-no-useless-fragment": "warn",
"react/jsx-fragments": "warn",
"eqeqeq": ["warn", "smart"],
"no-lonely-if": "warn",
"no-multi-assign": "warn",
"@typescript-eslint/no-shadow": "warn",
"no-useless-return": "warn",
"no-useless-rename": "warn",
"one-var-declaration-per-line": "warn",
"prefer-object-spread": "warn",
"no-unreachable-loop": "warn",
"no-template-curly-in-string": "warn",
"default-case-last": "warn",
"no-array-constructor": "warn",
"no-else-return": "warn",
// disabled: too generic to really provide helpful warnings
//'no-negated-condition': 'warn',
"array-callback-return": "warn",
"@typescript-eslint/consistent-type-definitions": ["warn", "type"],

// check if useful
//"no-secrets/no-secrets": "warn",

// based on https://github.com/github/eslint-plugin-github/blob/main/lib/configs/react.js
"github/a11y-no-generic-link-text": "error",

// TODO: why is this one not working?
//"github/role-supports-aria-props": "error",
// disabled for the one above
//"jsx-a11y/role-supports-aria-props": "off"

// maybe consider https://github.com/brendanmorrell/eslint-plugin-styled-components-a11y

"import/first": "error",
"import/no-absolute-path": "error",
// disabled: slow linting
/*"import/no-cycle": "error",*/
"import/no-duplicates": "error",

"@typescript-eslint/consistent-type-imports": [
"error",
{
"prefer": "type-imports",
"fixStyle": "inline-type-imports"
}
],

"sonarjs/cognitive-complexity": "off",
// typically only triggered on more complex switch cases where introducing a variable only worsens readability
"sonarjs/no-duplicate-string": "off"
},
"ignorePatterns": [".eslintrc.js"]
}
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,7 @@ yarn-error.log*
/.idea/
.npmrc

utils/generatedGitInfo.json
lib/generatedGitInfo.json

.yarn/
!.yarn/releases
3 changes: 3 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-3.6.3.cjs
37 changes: 37 additions & 0 deletions components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { type FC } from 'react';
import Link from 'next/link';
import generatedGitInfo from 'lib/generatedGitInfo.json';

/**
* Static footer component rendered at the bottom of every page
*/
const Footer: FC = () => (
<footer className="footer mt-auto py-3 bg-light">
<div className="text-center">
<ul className="list-unstyled list-inline my-0">
<li className="list-inline-item mx-5">
<Link href="/terms-of-service" passHref>
<a className="text-muted">Terms of service</a>
</Link>
</li>
<li className="list-inline-item mx-5">
<Link href="/privacy-policy" passHref>
<a className="text-muted">Privacy policy</a>
</Link>
</li>
<li className="list-inline-item mx-5">
<a href="mailto:[email protected]" className="text-muted">
Email Support
</a>
</li>
</ul>
<ul className="list-unstyled list-inline my-0">
<li className="list-inline-item mx-5 text-muted">
<small>{generatedGitInfo.gitTag}</small>
</li>
</ul>
</div>
</footer>
);

export default Footer;
5 changes: 5 additions & 0 deletions components/Identifiable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type Identifiable = {
readonly id: string;
};

export default Identifiable;
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react';
import { InlineCloseButton } from 'components/inlineCloseButton';
import InlineCloseButton from 'components/InlineCloseButton';
import { act, render, screen } from '@testing-library/react';

test('interaction', () => {
Expand Down
26 changes: 26 additions & 0 deletions components/InlineCloseButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { type FC } from 'react';
import { X } from 'react-bootstrap-icons';
import actionable from 'styles/actionable.module.css';
import clsx from 'clsx';

type InlineCloseButtonProps = { onClose: () => void };

// TODO: use unstyled button instead of div

/**
* Small X button representing a close action that is rendered inline
*
* @param props
* @param props.onClose callback to call when button is pressed
*/
const InlineCloseButton: FC<InlineCloseButtonProps> = ({ onClose }) => (
<div
onClick={() => onClose()}
className={clsx('d-inline-block', actionable['actionable'])}
role="button"
>
<X />
</div>
);

export default InlineCloseButton;
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import React, { ReactElement, ReactNode, useState } from 'react';
import { type FC, type PropsWithChildren, useState } from 'react';
import { Dropdown, FormControl, InputGroup } from 'react-bootstrap';
import { Suggestion } from './resultSearch/jsonSchema';
import { type Suggestion } from 'components/result-search/jsonSchema';
import { truthyOrNoneTag } from './utility';

export function InputWithSuggestions(props: {
type InputWithSuggestionsProps = {
setInput: (input: string) => void;
suggestions?: Suggestion[];
placeholder?: string;
children?: ReactNode;
value?: string;
}): ReactElement {
const [input, setInput] = useState(props.value);
};

function updateInput(input: string) {
setInput(input);
props.setInput(input);
}
const InputWithSuggestions: FC<PropsWithChildren<InputWithSuggestionsProps>> = ({
setInput,
suggestions,
placeholder,
value,
children,
}) => {
const [input, setLocalInput] = useState(value);

const updateInput = (newInput: string) => {
setLocalInput(newInput);
setInput(newInput);
};

return (
<Dropdown
Expand All @@ -25,18 +32,16 @@ export function InputWithSuggestions(props: {
}}
>
<FormControl
placeholder={props.placeholder}
aria-label={props.placeholder ?? 'Input field with suggestions'}
placeholder={placeholder}
aria-label={placeholder ?? 'Input field with suggestions'}
value={input}
onChange={(e) => {
updateInput(e.target.value);
}}
onChange={(e) => updateInput(e.target.value)}
/>
{props.suggestions !== undefined && props.suggestions.length > 0 && (
{suggestions !== undefined && suggestions.length > 0 && (
<>
<Dropdown.Toggle split variant="outline-secondary" />
<Dropdown.Menu>
{props.suggestions.map((suggestion) => (
{suggestions.map((suggestion) => (
<Dropdown.Item key={suggestion.field} eventKey={suggestion.field}>
{suggestion.field}
<br />
Expand All @@ -52,7 +57,9 @@ export function InputWithSuggestions(props: {
</>
)}
{/* TODO: clean up, find alternative for this (this is used in filters) */}
{props.children}
{children}
</Dropdown>
);
}
};

export default InputWithSuggestions;
24 changes: 24 additions & 0 deletions components/JsonHighlight.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { type FC, type PropsWithChildren, useEffect } from 'react';

import prism from 'prismjs';
import 'prismjs/components/prism-json';

/**
* Display JSON text with syntax highlighting
*
* @param props
* @param props.children
*/
const JsonHighlight: FC<PropsWithChildren> = ({ children }) => {
useEffect(() => {
prism.highlightAll();
}, []);

return (
<pre>
<code className="language-json">{children}</code>
</pre>
);
};

export default JsonHighlight;
41 changes: 41 additions & 0 deletions components/JsonPreviewModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { type FC } from 'react';
import { Button, Modal } from 'react-bootstrap';
import JsonHighlight from 'components/JsonHighlight';
import { type Result } from '@eosc-perf/eosc-perf-client';

type JsonPreviewModalProps = {
result: Result | null;
show: boolean;
closeModal: () => void;
};

/**
* Modal to view the JSON data of a result
*
* @param props
* @param props.result
* @param props.show
* @param props.closeModal
*/
const JsonPreviewModal: FC<JsonPreviewModalProps> = ({ result, show, closeModal }) => {
return (
<Modal show={show} scrollable size="lg" onHide={closeModal}>
<Modal.Header>
<Modal.Title>JSON Data</Modal.Title>
</Modal.Header>
<Modal.Body>
{result !== null && (
<JsonHighlight>{JSON.stringify(result.json, null, 4)}</JsonHighlight>
)}
{result == null && <div className="text-muted">Loading...</div>}
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={closeModal}>
Close
</Button>
</Modal.Footer>
</Modal>
);
};

export default JsonPreviewModal;
27 changes: 16 additions & 11 deletions components/jsonSelection.tsx → components/JsonSelection.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
import React, { ChangeEvent, ReactElement, useState } from 'react';
import { type ChangeEvent, type FC, useState } from 'react';
import { Form, ProgressBar } from 'react-bootstrap';

type JsonSelectionProps = {
fileContents?: string;
setFileContents: (file?: string) => void;
};

/**
* Form component to select a JSON file for upload
*
* @param props
* @param props.fileContents string containing the json file
* @param props.setFileContents callback to update the string containing the json
* @constructor
*/
export function JsonSelection(props: {
fileContents?: string;
setFileContents: (file?: string) => void;
}): ReactElement {
const JsonSelection: FC<JsonSelectionProps> = ({ fileContents, setFileContents }) => {
const [progress, setProgress] = useState(100.0);

function loadFile(file?: File) {
if (file === undefined) {
props.setFileContents(undefined);
setFileContents(undefined);
return;
}

const reader = new FileReader();
reader.addEventListener('load', (e) => {
if (e.target && e.target.result) {
if (e.target?.result) {
// readAsText guarantees string
props.setFileContents(e.target.result as string);
setFileContents(e.target.result as string);
} else {
props.setFileContents(undefined);
setFileContents(undefined);
}
setProgress(100.0);
});
Expand All @@ -52,4 +55,6 @@ export function JsonSelection(props: {
{/*props.fileContents !== undefined ? props.fileContents : <div className="text-muted">No file loaded.</div>*/}
</div>
);
}
};

export default JsonSelection;
Loading

0 comments on commit 39623ce

Please sign in to comment.