Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ jobs:
id: install-playwright
run: npx playwright install --with-deps

- name: lint
id: npm-lint
run: npm run lint

- name: run tests
id: npm-test
run: CI=1 npm test
167 changes: 151 additions & 16 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,166 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
{ ignores: ['dist'] },
import eslint from '@eslint/js';
import globals from 'globals';
import tseslint from 'typescript-eslint';
import vitest from '@vitest/eslint-plugin';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import stylistic from '@stylistic/eslint-plugin';

import { defineConfig, globalIgnores } from 'eslint/config';

export default defineConfig([
{
files: ['**/*.{js,mjs,cjs}'],
extends: [
js.configs.recommended,
...tseslint.configs.recommended
eslint.configs.recommended,
],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
ecmaVersion: 'latest',
sourceType: 'module',
globals: { ...globals.browser, ...globals.node },
},
},
{
files: ['**/*.{ts,tsx}'],
extends: [
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
...tseslint.configs.stylisticTypeChecked,
],
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
languageOptions: {
parserOptions: {
projectService: {
allowDefaultProject: [
'vite.config.ts',
'vitest.config.ts',
'vitest.browser.config.ts',
'global.d.ts',
],
defaultProject: './tsconfig.test.json',
},
tsconfigRootDir: import.meta.dirname,
},
globals: { ...globals.browser, ...globals.node },
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],

'no-console': ['error', { allow: ['warn', 'error'] }],

'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['error', { varsIgnorePattern: '^_', argsIgnorePattern: '^_' }],

'@typescript-eslint/no-unused-expressions': ['error', { allowShortCircuit: true }],

'no-continue': 'off',
'no-param-reassign': 'off',
'prefer-destructuring': 'off',
'arrow-body-style': 'off',

'no-void': ['error', { allowAsStatement: true }],

'no-use-before-define': ['error', { functions: false, classes: false }],
'@typescript-eslint/no-use-before-define': ['error', { functions: false, classes: false }],

'@typescript-eslint/no-namespace': 'off',

'@typescript-eslint/space-infix-ops': 'off',

'@typescript-eslint/no-empty-object-type': ['warn', { allowInterfaces: 'always' }],

'@typescript-eslint/no-base-to-string': [
'error',
{ ignoredTypeNames: ['URI', 'Error', 'RegExp', 'URL', 'URLSearchParams'] },
],

'no-underscore-dangle': [
'warn',
{ allowConstantExport: true },
{
allow: ['_links', '_embedded', '_meta', '_type', '_destroy', '_tiptapEditor', '__dirname'],
allowAfterThis: true,
allowAfterSuper: false,
allowAfterThisConstructor: false,
enforceInMethodNames: true,
allowFunctionParams: true,
},
],

'no-return-assign': ['error', 'except-parens'],
'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],

'class-methods-use-this': 'off',
},
},
{
files: ['**/*.spec.ts', '**/*.test.ts', '**/*.test.tsx'],
...vitest.configs.recommended,
rules: {
...vitest.configs.recommended.rules,

'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/require-await': 'off',

'max-classes-per-file': 'off',
'vitest/no-commented-out-tests': 'off',
},
},
{
files: ['test/**/*.ts', 'test/**/*.tsx'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/require-await': 'off',
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/await-thenable': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'react-refresh/only-export-components': 'off',
},
},
{
files: ['lib/**/*.{ts,tsx}'],
rules: {
'react-refresh/only-export-components': 'off',
},
},
{
plugins: { '@stylistic': stylistic },
rules: {
'@stylistic/semi': ['error', 'always'],
'@stylistic/max-len': 'off',
'@stylistic/object-curly-newline': 'off',
'@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
'@stylistic/implicit-arrow-linebreak': 'off',
'@stylistic/lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }],
'@stylistic/indent': 'off',
'@stylistic/type-annotation-spacing': [
'error',
{
before: false,
after: false,
overrides: {
arrow: { before: true, after: true },
},
},
],
'@stylistic/spaced-comment': 'off',
},
},
)
globalIgnores([
'dist/',
'coverage/',
'**/vendor',
]),
]);
2 changes: 1 addition & 1 deletion global.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
declare module '*.css' {
const content: { [className: string]: string };
const content:Record<string, string>;
export default content;
}
26 changes: 13 additions & 13 deletions lib/components/BlockWorkPackage/BlockCard.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { forwardRef } from "react";
import type { WorkPackage } from "../../openProjectTypes";
import type { BlockWpSize } from "../WorkPackage/types";
import { BlockCardM, BlockCardL, BlockCardXL } from "./BlockCards";
import { forwardRef } from 'react';
import type { WorkPackage } from '../../openProjectTypes';
import type { BlockWpSize } from '../WorkPackage/types';
import { BlockCardM, BlockCardL, BlockCardXL } from './BlockCards';

export interface BlockCardProps {
workPackage: WorkPackage;
size?: BlockWpSize;
inDropdown?: boolean;
linkTitle?: boolean;
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
workPackage:WorkPackage;
size?:BlockWpSize;
inDropdown?:boolean;
linkTitle?:boolean;
onClick?:(e:React.MouseEvent<HTMLDivElement>) => void;
}

export const BlockCard = forwardRef<HTMLDivElement, BlockCardProps>(
({ workPackage, size = "m", inDropdown, linkTitle, onClick }, ref) => {
({ workPackage, size = 'm', inDropdown, linkTitle, onClick }, ref) => {
const shared = { workPackage, inDropdown, linkTitle, onClick, cardRef: ref };

if (size === "xl") return <BlockCardXL {...shared} />;
if (size === "l") return <BlockCardL {...shared} />;
if (size === 'xl') return <BlockCardXL {...shared} />;
if (size === 'l') return <BlockCardL {...shared} />;
return <BlockCardM {...shared} />;
}
);

BlockCard.displayName = "BlockCard";
BlockCard.displayName = 'BlockCard';
42 changes: 21 additions & 21 deletions lib/components/BlockWorkPackage/BlockCards.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import styled from "styled-components";
import type { WorkPackage } from "../../openProjectTypes";
import { linkToWorkPackage } from "../../services/openProjectApi";
import styled from 'styled-components';
import type { WorkPackage } from '../../openProjectTypes';
import { linkToWorkPackage } from '../../services/openProjectApi';
import {
defaultWpVariables,
WorkPackageId,
WorkPackageType,
WorkPackageStatus,
WorkPackageTitle,
WorkPackageTitleLink,
} from "../WorkPackage/atoms";
} from '../WorkPackage/atoms';
import {
typeColor,
statusColor,
statusBorderColor,
statusTextColor,
statusBackgroundColor,
} from "../../services/colors";
import { formatWorkPackageId } from "../../utils/id";
} from '../../services/colors';
import { formatWorkPackageId } from '../../utils/id';

const DESCRIPTION_MAX_CHARS = 300;

export interface BlockCardSharedProps {
workPackage: WorkPackage;
inDropdown?: boolean;
linkTitle?: boolean;
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
workPackage:WorkPackage;
inDropdown?:boolean;
linkTitle?:boolean;
onClick?:(e:React.MouseEvent<HTMLDivElement>) => void;
}

function buildTitle(workPackage: WorkPackage, linkTitle: boolean) {
function buildTitle(workPackage:WorkPackage, linkTitle:boolean) {
const href = linkToWorkPackage(workPackage.displayId);
if (!linkTitle) return workPackage.subject;
return (
Expand All @@ -36,15 +36,15 @@ function buildTitle(workPackage: WorkPackage, linkTitle: boolean) {
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
window.open(href, "_blank", "noopener,noreferrer");
window.open(href, '_blank', 'noopener,noreferrer');
}}
>
{workPackage.subject}
</WorkPackageTitleLink>
);
}

const CardBase = styled.div<{ $inDropdown: boolean }>`
const CardBase = styled.div<{ $inDropdown:boolean }>`
${defaultWpVariables}
padding: var(--spacer-m) var(--spacer-l);
background-color: var(--highlight-wp-background);
Expand All @@ -59,7 +59,7 @@ const CardBase = styled.div<{ $inDropdown: boolean }>`
`;

const CardDetails = styled.div.attrs({
className: "op-bn-work-package--details",
className: 'op-bn-work-package--details',
})`
display: flex;
flex-wrap: wrap;
Expand Down Expand Up @@ -97,14 +97,14 @@ export const BlockCardM = ({
linkTitle = false,
onClick,
cardRef,
}: BlockCardSharedProps & { cardRef?: React.Ref<HTMLDivElement> }) => (
}:BlockCardSharedProps & { cardRef?:React.Ref<HTMLDivElement> }) => (
<CardBase
ref={cardRef}
className="op-bn-work-package op-bn-work-package--m"
$inDropdown={inDropdown}
onClick={onClick}
data-testid="block-card"
style={onClick ? { cursor: "pointer" } : undefined}
style={onClick ? { cursor: 'pointer' } : undefined}
>
<CardDetails>
<WorkPackageType $color={typeColor(workPackage)}>
Expand Down Expand Up @@ -132,14 +132,14 @@ export const BlockCardL = ({
linkTitle = false,
onClick,
cardRef,
}: BlockCardSharedProps & { cardRef?: React.Ref<HTMLDivElement> }) => (
}:BlockCardSharedProps & { cardRef?:React.Ref<HTMLDivElement> }) => (
<CardBase
ref={cardRef}
className="op-bn-work-package op-bn-work-package--l"
$inDropdown={inDropdown}
onClick={onClick}
data-testid="block-card"
style={onClick ? { cursor: "pointer" } : undefined}
style={onClick ? { cursor: 'pointer' } : undefined}
>
<CardDetailsSpaced>
<WorkPackageType $color={typeColor(workPackage)}>
Expand Down Expand Up @@ -172,7 +172,7 @@ export const BlockCardXL = ({
linkTitle = false,
onClick,
cardRef,
}: BlockCardSharedProps & { cardRef?: React.Ref<HTMLDivElement> }) => {
}:BlockCardSharedProps & { cardRef?:React.Ref<HTMLDivElement> }) => {
const rawDescription = workPackage.description?.raw;
const snippetText = rawDescription
? rawDescription.slice(0, DESCRIPTION_MAX_CHARS)
Expand All @@ -188,7 +188,7 @@ export const BlockCardXL = ({
$inDropdown={inDropdown}
onClick={onClick}
data-testid="block-card"
style={onClick ? { cursor: "pointer" } : undefined}
style={onClick ? { cursor: 'pointer' } : undefined}
>
<CardDetailsSpaced>
<WorkPackageType $color={typeColor(workPackage)}>
Expand All @@ -214,7 +214,7 @@ export const BlockCardXL = ({
{snippetText && (
<DescriptionSnippet>
{snippetText}
{isTruncated && "…"}
{isTruncated && '…'}
</DescriptionSnippet>
)}
</CardBase>
Expand Down
Loading
Loading