Skip to content

Commit 5546738

Browse files
chore(frontend): enforce no-explicit-any and tidy JSDoc (#656)
- Flip @typescript-eslint/no-explicit-any from 'off' to 'error' to lock in the existing zero-`any` state; no violations surfaced. - Remove the stale .eslintrc.cjs entry from the flat-config ignores. - Add /** JSDoc to the Checkbox, Input, Spinner, EmptyState, and DateHierarchyBar primitives for doc consistency. Verified: pnpm lint:js, pnpm lint:css, pnpm typecheck all pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 169d8fd commit 5546738

6 files changed

Lines changed: 33 additions & 9 deletions

File tree

frontend/eslint.config.mjs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ export default tseslint.config(
2121
'**/node_modules/**',
2222
'**/*.config.{js,cjs,mjs,ts}',
2323
'vitest.setup.ts',
24-
'.eslintrc.cjs',
2524
],
2625
},
2726
js.configs.recommended,
@@ -34,10 +33,11 @@ export default tseslint.config(
3433
plugins: { 'react-hooks': reactHooks },
3534
rules: {
3635
...reactHooks.configs.recommended.rules,
37-
// `Any` is used deliberately at wire boundaries (parsed JSON,
38-
// Django-shaped signatures) — mirrors the Python side keeping
39-
// `Any`. Tightening the tractable cases is tracked separately.
40-
'@typescript-eslint/no-explicit-any': 'off',
36+
// The codebase carries zero `any` today; lock that in (#656). A
37+
// genuine wire boundary that needs `any` should add a narrowly
38+
// scoped per-line `eslint-disable-next-line` with a reason rather
39+
// than reopening the rule globally.
40+
'@typescript-eslint/no-explicit-any': 'error',
4141
// Align with tsconfig's `noUnusedParameters` underscore convention.
4242
'@typescript-eslint/no-unused-vars': [
4343
'error',

frontend/packages/list/src/DateHierarchyBar.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@ export interface DateHierarchyBarProps {
2020
onNavigate: (path: { year?: number | null; month?: number | null; day?: number | null }) => void;
2121
}
2222

23-
// date_hierarchy drill-down bar (#304 — Django changelist parity). Reads
24-
// `active` for the current drill path (breadcrumb, each crumb navigates
25-
// up) and `buckets` for the next level's options (drill down). The
26-
// backend caps the level by the field; clicking wires ?year/?month/?day.
23+
/**
24+
* `date_hierarchy` drill-down bar (#304 — Django changelist parity).
25+
* Reads `dh.active` for the current drill path (a breadcrumb whose crumbs
26+
* navigate up) and `dh.buckets` for the next level's options (drill down).
27+
* The backend caps the level by the field; clicking calls `onNavigate`
28+
* with the chosen `?year`/`?month`/`?day`.
29+
*/
2730
export function DateHierarchyBar({ dh, onNavigate }: DateHierarchyBarProps) {
2831
const { active, buckets } = dh;
2932
const level: 'year' | 'month' | 'day' | 'done' =

frontend/packages/ui/src/Checkbox.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ import type { InputHTMLAttributes } from 'react';
1212

1313
export type CheckboxProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type'>;
1414

15+
/**
16+
* Styled checkbox primitive: an `appearance-none` box that matches the
17+
* themed text inputs (border + transparent surface) with a primary fill
18+
* and inline-SVG tick when checked. Accepts all native checkbox input
19+
* attributes except `type`.
20+
*/
1521
export function Checkbox({ className = '', ...rest }: CheckboxProps) {
1622
return (
1723
<span className="relative inline-flex h-4 w-4 shrink-0 align-middle">

frontend/packages/ui/src/EmptyState.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ export interface EmptyStateProps {
66
action?: ReactNode;
77
}
88

9+
/**
10+
* Centered placeholder for empty collections: a required `title`, an
11+
* optional `description`, and an optional `action` node (e.g. an "Add"
12+
* button) rendered below.
13+
*/
914
export function EmptyState({ title, description, action }: EmptyStateProps) {
1015
return (
1116
<div className="flex flex-col items-center justify-center text-center py-12 px-4">

frontend/packages/ui/src/Input.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
66
error?: ReactNode;
77
}
88

9+
/**
10+
* Text input primitive with an optional label, help text, and error
11+
* message. Generates a stable-per-render `id` when none is supplied so the
12+
* label's `htmlFor` always resolves. Forwards all native input attributes.
13+
*/
914
export function Input({ label, helpText, error, id, className = '', ...rest }: InputProps) {
1015
const inputId = id ?? `dar-input-${Math.random().toString(36).slice(2, 8)}`;
1116
return (

frontend/packages/ui/src/Spinner.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ const SIZE_CLASSES: Record<NonNullable<SpinnerProps['size']>, string> = {
99
lg: 'h-10 w-10',
1010
};
1111

12+
/**
13+
* Animated loading spinner with an `aria-live` status region. `size`
14+
* selects one of three preset dimensions (default `md`); an optional
15+
* `label` renders beside the spinner and is announced to assistive tech.
16+
*/
1217
export function Spinner({ size = 'md', label }: SpinnerProps) {
1318
return (
1419
<span role="status" aria-live="polite" className="inline-flex items-center gap-2 text-gray-500">

0 commit comments

Comments
 (0)