diff --git a/apps/docs/src/remix-hook-form/data-table/data-table-router-form.stories.tsx b/apps/docs/src/remix-hook-form/data-table/data-table-router-form.stories.tsx index fedd89a3..32a0f3ef 100644 --- a/apps/docs/src/remix-hook-form/data-table/data-table-router-form.stories.tsx +++ b/apps/docs/src/remix-hook-form/data-table/data-table-router-form.stories.tsx @@ -132,41 +132,11 @@ const bazzaFilterColumnConfigs: BazzaFilterColumnConfig[] = [ // Add more configs for other filterable columns as needed ]; -// Log all options for each config to help debug undefined labels -bazzaFilterColumnConfigs.forEach((config) => { - // Log the options array for each config, if present - console.log(`Config id: ${config.id}, options:`, config.options); - if (config.options) { - config.options.forEach((opt, idx) => { - // Log if label is missing or undefined - if (!opt || typeof opt.label !== 'string') { - // eslint-disable-next-line no-console - console.warn(`Option label is missing or not a string in config '${config.id}' at index ${idx}:`, opt); - } - }); - } -}); - function DataTableRouterFormExample() { const loaderData = useLoaderData(); const data = loaderData?.data ?? []; const pageCount = loaderData?.meta.pageCount ?? 0; - // Log options before rendering to catch runtime issues - bazzaFilterColumnConfigs.forEach((config) => { - if (config.options) { - config.options.forEach((opt, idx) => { - if (!opt || typeof opt.label !== 'string') { - // eslint-disable-next-line no-console - console.warn( - `[Render] Option label is missing or not a string in config '${config.id}' at index ${idx}:`, - opt, - ); - } - }); - } - }); - return (

Users Table (Bazza UI Filters)

diff --git a/apps/docs/src/remix-hook-form/date-picker.stories.tsx b/apps/docs/src/remix-hook-form/date-picker.stories.tsx index b7e39ef6..e2a4cd82 100644 --- a/apps/docs/src/remix-hook-form/date-picker.stories.tsx +++ b/apps/docs/src/remix-hook-form/date-picker.stories.tsx @@ -131,15 +131,15 @@ export const Default: Story = { // Find any available day button in the calendar (look for a button with a number) // We'll look for any day button that's not disabled const dayButtons = await dialogCanvas.findAllByRole('button'); - const availableDayButton = dayButtons.find(button => { + const availableDayButton = dayButtons.find((button) => { const text = button.textContent; return text && DAY_BUTTON_REGEX.test(text.trim()) && !button.hasAttribute('disabled'); }); - + if (!availableDayButton) { throw new Error('No available day button found in calendar'); } - + await userEvent.click(availableDayButton); }); diff --git a/apps/docs/src/remix-hook-form/form-error-basic.stories.tsx b/apps/docs/src/remix-hook-form/form-error-basic.stories.tsx index 7a148f90..be75db2e 100644 --- a/apps/docs/src/remix-hook-form/form-error-basic.stories.tsx +++ b/apps/docs/src/remix-hook-form/form-error-basic.stories.tsx @@ -16,11 +16,11 @@ const formSchema = z.object({ type FormData = z.infer; const BasicFormErrorExample = () => { - const fetcher = useFetcher<{ - message?: string; - errors?: Record + const fetcher = useFetcher<{ + message?: string; + errors?: Record; }>(); - + const methods = useRemixForm({ resolver: zodResolver(formSchema), defaultValues: { @@ -40,10 +40,10 @@ const BasicFormErrorExample = () => {

Login Form

- + {/* Form-level error display */} - + { placeholder="Enter your email" disabled={isSubmitting} /> - + { placeholder="Enter your password" disabled={isSubmitting} /> - + - + {fetcher.data?.message && (

{fetcher.data.message}

@@ -76,28 +76,28 @@ const BasicFormErrorExample = () => { const handleFormSubmission = async (request: Request) => { const { data, errors } = await getValidatedFormData(request, zodResolver(formSchema)); - + if (errors) { return { errors }; } - + // Simulate server-side authentication if (data.email === 'wrong@email.com' && data.password === 'wrongpass') { return { errors: { - _form: { message: 'Invalid email or password. Please try again.' } - } + _form: { message: 'Invalid email or password. Please try again.' }, + }, }; } - + if (data.email === 'user@example.com' && data.password === 'password123') { return { message: 'Login successful! Welcome back.' }; } - + return { errors: { - _form: { message: 'Invalid email or password. Please try again.' } - } + _form: { message: 'Invalid email or password. Please try again.' }, + }, }; }; @@ -191,7 +191,7 @@ The FormError component automatically displays when \`errors._form\` exists in t // Wait for form-level error to appear await expect(canvas.findByText(/invalid email or password/i)).resolves.toBeInTheDocument(); - + // Verify field-level errors are cleared expect(canvas.queryByText(/please enter a valid email address/i)).not.toBeInTheDocument(); }); diff --git a/apps/docs/src/remix-hook-form/form-error-custom.stories.tsx b/apps/docs/src/remix-hook-form/form-error-custom.stories.tsx index 26df7536..561dca6c 100644 --- a/apps/docs/src/remix-hook-form/form-error-custom.stories.tsx +++ b/apps/docs/src/remix-hook-form/form-error-custom.stories.tsx @@ -20,18 +20,22 @@ type FormData = z.infer; const AlertErrorMessage = (props: React.ComponentProps) => (
- +
); const CustomStyledFormErrorExample = () => { - const fetcher = useFetcher<{ - message?: string; - errors?: Record + const fetcher = useFetcher<{ + message?: string; + errors?: Record; }>(); - + const methods = useRemixForm({ resolver: zodResolver(formSchema), defaultValues: { @@ -51,14 +55,14 @@ const CustomStyledFormErrorExample = () => {

Sign In

- + {/* Custom styled form error */} - - + { placeholder="Enter your email" disabled={isSubmitting} /> - + { placeholder="Enter your password" disabled={isSubmitting} /> - + - + {fetcher.data?.message && (

{fetcher.data.message}

@@ -91,16 +95,16 @@ const CustomStyledFormErrorExample = () => { const handleFormSubmission = async (request: Request) => { const { data, errors } = await getValidatedFormData(request, zodResolver(formSchema)); - + if (errors) { return { errors }; } - + // Always show form error for demo purposes return { errors: { - _form: { message: 'Authentication service is temporarily unavailable. Please try again in a few minutes.' } - } + _form: { message: 'Authentication service is temporarily unavailable. Please try again in a few minutes.' }, + }, }; }; @@ -188,8 +192,8 @@ The custom component receives all the same props as the default FormMessage comp await userEvent.click(submitButton); // Wait for error to appear - await new Promise(resolve => setTimeout(resolve, 500)); - + await new Promise((resolve) => setTimeout(resolve, 500)); + // Check for error message const errorMessage = canvas.queryByText(/authentication service is temporarily unavailable/i); expect(errorMessage).toBeInTheDocument(); @@ -197,12 +201,20 @@ The custom component receives all the same props as the default FormMessage comp await step('Verify custom error styling and structure', async () => { // Wait for error message to be available - await new Promise(resolve => setTimeout(resolve, 500)); + await new Promise((resolve) => setTimeout(resolve, 500)); const errorMessage = canvas.queryByText(/authentication service is temporarily unavailable/i); expect(errorMessage).toBeInTheDocument(); const errorContainer = errorMessage?.closest('div'); - expect(errorContainer).toHaveClass('flex', 'items-center', 'p-4', 'bg-red-50', 'border-l-4', 'border-red-400', 'rounded-md'); + expect(errorContainer).toHaveClass( + 'flex', + 'items-center', + 'p-4', + 'bg-red-50', + 'border-l-4', + 'border-red-400', + 'rounded-md', + ); const icon = errorContainer?.querySelector('svg'); expect(icon).toBeInTheDocument(); diff --git a/apps/docs/src/remix-hook-form/form-error-mixed.stories.tsx b/apps/docs/src/remix-hook-form/form-error-mixed.stories.tsx index e9b76d6e..890a3f44 100644 --- a/apps/docs/src/remix-hook-form/form-error-mixed.stories.tsx +++ b/apps/docs/src/remix-hook-form/form-error-mixed.stories.tsx @@ -16,11 +16,11 @@ const formSchema = z.object({ type FormData = z.infer; const MixedErrorsExample = () => { - const fetcher = useFetcher<{ - message?: string; - errors?: Record + const fetcher = useFetcher<{ + message?: string; + errors?: Record; }>(); - + const methods = useRemixForm({ resolver: zodResolver(formSchema), defaultValues: { @@ -40,10 +40,10 @@ const MixedErrorsExample = () => {

Create Account

- + {/* Form-level error at top */} - + { placeholder="Enter your email" disabled={isSubmitting} /> - + { placeholder="Enter your password" disabled={isSubmitting} /> - + - + {/* Form-level error at bottom */} - + {fetcher.data?.message && (

{fetcher.data.message}

@@ -79,38 +79,38 @@ const MixedErrorsExample = () => { const handleFormSubmission = async (request: Request) => { const { data, errors } = await getValidatedFormData(request, zodResolver(formSchema)); - + if (errors) { return { errors }; } - + // Simulate mixed errors - both field and form level if (data.email === 'taken@example.com') { return { errors: { email: { message: 'This email address is already registered' }, - _form: { message: 'Registration failed. Please correct the errors above.' } - } + _form: { message: 'Registration failed. Please correct the errors above.' }, + }, }; } - + // Simulate server error only if (data.password === 'servererror') { return { errors: { - _form: { message: 'Server error occurred. Please try again later.' } - } + _form: { message: 'Server error occurred. Please try again later.' }, + }, }; } - + if (data.email === 'valid@example.com' && data.password === 'validpass123') { return { message: 'Account created successfully! Welcome aboard.' }; } - + return { errors: { - _form: { message: 'Registration failed. Please try again.' } - } + _form: { message: 'Registration failed. Please try again.' }, + }, }; }; @@ -193,12 +193,12 @@ This pattern is useful when you want to show form-level context alongside specif await userEvent.click(submitButton); // Wait for errors to appear - await new Promise(resolve => setTimeout(resolve, 500)); - + await new Promise((resolve) => setTimeout(resolve, 500)); + // Check for field-level error const fieldError = canvas.queryByText(/this email address is already registered/i); expect(fieldError).toBeInTheDocument(); - + // Check for form-level errors - use queryAllByText to avoid "multiple elements" error const formErrors = canvas.queryAllByText(/registration failed/i); expect(formErrors.length).toBeGreaterThanOrEqual(1); diff --git a/apps/docs/src/remix-hook-form/form-error-placement.stories.tsx b/apps/docs/src/remix-hook-form/form-error-placement.stories.tsx index 41ff154c..19ef4821 100644 --- a/apps/docs/src/remix-hook-form/form-error-placement.stories.tsx +++ b/apps/docs/src/remix-hook-form/form-error-placement.stories.tsx @@ -16,11 +16,11 @@ const formSchema = z.object({ type FormData = z.infer; const PlacementVariationsExample = () => { - const fetcher = useFetcher<{ - message?: string; - errors?: Record + const fetcher = useFetcher<{ + message?: string; + errors?: Record; }>(); - + const methods = useRemixForm({ resolver: zodResolver(formSchema), defaultValues: { @@ -40,10 +40,10 @@ const PlacementVariationsExample = () => {

Payment Form

- + {/* Top placement - Alert style */} - + { placeholder="Enter your email" disabled={isSubmitting} /> - + {/* Inline placement - Minimal style */} - + { placeholder="Enter your password" disabled={isSubmitting} /> - + - + {/* Bottom placement - Banner style */} - + {fetcher.data?.message && (

{fetcher.data.message}

@@ -82,16 +82,16 @@ const PlacementVariationsExample = () => { const handleFormSubmission = async (request: Request) => { const { data, errors } = await getValidatedFormData(request, zodResolver(formSchema)); - + if (errors) { return { errors }; } - + // Always show form error for demo purposes return { errors: { - _form: { message: 'Payment processing failed. Please check your information and try again.' } - } + _form: { message: 'Payment processing failed. Please check your information and try again.' }, + }, }; }; @@ -174,8 +174,8 @@ All three instances show the same error message but with different visual treatm await userEvent.click(submitButton); // Wait for error messages to appear - use queryAllByText to avoid "multiple elements" error - await new Promise(resolve => setTimeout(resolve, 500)); - + await new Promise((resolve) => setTimeout(resolve, 500)); + // Verify all three FormError instances are displayed const errorMessages = canvas.queryAllByText(/payment processing failed/i); expect(errorMessages.length).toBeGreaterThanOrEqual(1); @@ -184,18 +184,18 @@ All three instances show the same error message but with different visual treatm await step('Verify different styling for each placement', async () => { // Wait a moment for all error messages to be rendered - await new Promise(resolve => setTimeout(resolve, 100)); - + await new Promise((resolve) => setTimeout(resolve, 100)); + const errorMessages = canvas.getAllByText(/payment processing failed/i); expect(errorMessages).toHaveLength(3); - + // Top placement - alert style const topError = errorMessages[0]; expect(topError).toHaveClass('text-destructive'); const topContainer = topError.closest('div'); expect(topContainer).toHaveClass('p-4', 'bg-red-50', 'border', 'border-red-200', 'rounded-lg'); - // Inline placement - minimal style + // Inline placement - minimal style const inlineError = errorMessages[1]; expect(inlineError).toHaveClass('text-destructive'); diff --git a/apps/docs/src/remix-hook-form/form-error.stories.tsx b/apps/docs/src/remix-hook-form/form-error.stories.tsx index b5127e9f..e31d32f2 100644 --- a/apps/docs/src/remix-hook-form/form-error.stories.tsx +++ b/apps/docs/src/remix-hook-form/form-error.stories.tsx @@ -15,11 +15,11 @@ const formSchema = z.object({ type FormData = z.infer; const SimpleFormErrorExample = () => { - const fetcher = useFetcher<{ - message?: string; - errors?: Record + const fetcher = useFetcher<{ + message?: string; + errors?: Record; }>(); - + const methods = useRemixForm({ resolver: zodResolver(formSchema), defaultValues: { @@ -39,17 +39,11 @@ const SimpleFormErrorExample = () => {

Login

- + - - - + + + { placeholder="Enter your password" disabled={isSubmitting} /> - + - + {fetcher.data?.message && (

{fetcher.data.message}

@@ -74,15 +68,15 @@ const SimpleFormErrorExample = () => { const handleFormSubmission = async (request: Request) => { const { data, errors } = await getValidatedFormData(request, zodResolver(formSchema)); - + if (errors) { return { errors }; } - + return { errors: { - _form: { message: 'Invalid credentials. Please try again.' } - } + _form: { message: 'Invalid credentials. Please try again.' }, + }, }; }; @@ -145,4 +139,3 @@ For more comprehensive examples, see the related stories: }, }, }; - diff --git a/apps/docs/src/remix-hook-form/form-error.test.tsx b/apps/docs/src/remix-hook-form/form-error.test.tsx index 0747e32f..8e6c06a5 100644 --- a/apps/docs/src/remix-hook-form/form-error.test.tsx +++ b/apps/docs/src/remix-hook-form/form-error.test.tsx @@ -1,11 +1,10 @@ -import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { zodResolver } from '@hookform/resolvers/zod'; -import { RemixFormProvider, useRemixForm } from 'remix-hook-form'; -import { useFetcher } from 'react-router'; -import { z } from 'zod'; import { FormError, TextField } from '@lambdacurry/forms'; import { Button } from '@lambdacurry/forms/ui/button'; -import { FormMessage } from '@lambdacurry/forms/remix-hook-form/form'; +import { render, screen } from '@testing-library/react'; +import { useFetcher } from 'react-router'; +import { RemixFormProvider, useRemixForm } from 'remix-hook-form'; +import { z } from 'zod'; // Mock useFetcher jest.mock('react-router', () => ({ @@ -23,8 +22,8 @@ const testSchema = z.object({ type TestFormData = z.infer; // Test component wrapper -const TestFormWithError = ({ - initialErrors = {}, +const TestFormWithError = ({ + initialErrors = {}, formErrorName = '_form', customComponents = {}, className = '', @@ -53,11 +52,7 @@ const TestFormWithError = ({ return (
- + @@ -74,28 +69,28 @@ describe('FormError Component', () => { describe('Basic Functionality', () => { it('renders without errors when no form-level error exists', () => { render(); - + // Should not display any error message expect(screen.queryByText(/error/i)).not.toBeInTheDocument(); }); it('displays form-level error when _form error exists', () => { const errors = { - _form: { message: 'Server error occurred' } + _form: { message: 'Server error occurred' }, }; render(); - + expect(screen.getByText('Server error occurred')).toBeInTheDocument(); }); it('does not display error when _form error does not exist', () => { const errors = { - email: { message: 'Email is invalid' } + email: { message: 'Email is invalid' }, }; render(); - + expect(screen.queryByText('Server error occurred')).not.toBeInTheDocument(); }); }); @@ -103,21 +98,21 @@ describe('FormError Component', () => { describe('Custom Error Keys', () => { it('displays error for custom error key', () => { const errors = { - general: { message: 'General form error' } + general: { message: 'General form error' }, }; render(); - + expect(screen.getByText('General form error')).toBeInTheDocument(); }); it('does not display error when custom key does not match', () => { const errors = { - _form: { message: 'Default form error' } + _form: { message: 'Default form error' }, }; render(); - + expect(screen.queryByText('Default form error')).not.toBeInTheDocument(); }); }); @@ -125,22 +120,22 @@ describe('FormError Component', () => { describe('Styling and CSS Classes', () => { it('applies custom className to the error container', () => { const errors = { - _form: { message: 'Test error' } + _form: { message: 'Test error' }, }; render(); - + const errorElement = screen.getByText('Test error').closest('[class*="custom-error-class"]'); expect(errorElement).toBeInTheDocument(); }); it('renders with default styling when no className provided', () => { const errors = { - _form: { message: 'Test error' } + _form: { message: 'Test error' }, }; render(); - + expect(screen.getByText('Test error')).toBeInTheDocument(); }); }); @@ -154,27 +149,22 @@ describe('FormError Component', () => { ); const errors = { - _form: { message: 'Test error' } + _form: { message: 'Test error' }, }; - render( - - ); - + render(); + expect(screen.getByTestId('custom-form-message')).toBeInTheDocument(); expect(screen.getByText('Custom: Test error')).toBeInTheDocument(); }); it('falls back to default FormMessage when no custom component provided', () => { const errors = { - _form: { message: 'Test error' } + _form: { message: 'Test error' }, }; render(); - + expect(screen.getByText('Test error')).toBeInTheDocument(); // Should not have custom wrapper expect(screen.queryByTestId('custom-form-message')).not.toBeInTheDocument(); @@ -184,33 +174,33 @@ describe('FormError Component', () => { describe('Integration with Form State', () => { it('updates when form errors change', async () => { const { rerender } = render(); - + // Initially no error expect(screen.queryByText('New error')).not.toBeInTheDocument(); - + // Update with error const errors = { - _form: { message: 'New error' } + _form: { message: 'New error' }, }; - + rerender(); - + expect(screen.getByText('New error')).toBeInTheDocument(); }); it('hides error when form errors are cleared', async () => { const errors = { - _form: { message: 'Initial error' } + _form: { message: 'Initial error' }, }; const { rerender } = render(); - + // Initially shows error expect(screen.getByText('Initial error')).toBeInTheDocument(); - + // Clear errors rerender(); - + expect(screen.queryByText('Initial error')).not.toBeInTheDocument(); }); }); @@ -218,11 +208,11 @@ describe('FormError Component', () => { describe('Multiple FormError Components', () => { const MultipleFormErrorsComponent = () => { const mockFetcher = { - data: { - errors: { + data: { + errors: { _form: { message: 'General error' }, - custom: { message: 'Custom error' } - } + custom: { message: 'Custom error' }, + }, }, state: 'idle' as const, submit: jest.fn(), @@ -253,7 +243,7 @@ describe('FormError Component', () => { it('renders multiple FormError components with different error keys', () => { render(); - + expect(screen.getAllByText('General error')).toHaveLength(2); // top and bottom expect(screen.getByText('Custom error')).toBeInTheDocument(); // middle }); @@ -262,22 +252,22 @@ describe('FormError Component', () => { describe('Accessibility', () => { it('has proper ARIA attributes for error messages', () => { const errors = { - _form: { message: 'Accessibility test error' } + _form: { message: 'Accessibility test error' }, }; render(); - + const errorMessage = screen.getByText('Accessibility test error'); expect(errorMessage).toHaveAttribute('data-slot', 'form-message'); }); it('is properly associated with form context', () => { const errors = { - _form: { message: 'Form context error' } + _form: { message: 'Form context error' }, }; render(); - + const errorMessage = screen.getByText('Form context error'); expect(errorMessage.tagName.toLowerCase()).toBe('p'); expect(errorMessage).toHaveClass('form-message'); @@ -287,34 +277,35 @@ describe('FormError Component', () => { describe('Error Message Content', () => { it('handles empty error messages gracefully', () => { const errors = { - _form: { message: '' } + _form: { message: '' }, }; render(); - + // Should not render anything for empty message expect(screen.queryByText('')).not.toBeInTheDocument(); }); it('handles long error messages', () => { - const longMessage = 'This is a very long error message that should still be displayed properly even when it contains a lot of text and might wrap to multiple lines in the user interface.'; + const longMessage = + 'This is a very long error message that should still be displayed properly even when it contains a lot of text and might wrap to multiple lines in the user interface.'; const errors = { - _form: { message: longMessage } + _form: { message: longMessage }, }; render(); - + expect(screen.getByText(longMessage)).toBeInTheDocument(); }); it('handles special characters in error messages', () => { const specialMessage = 'Error with special chars: <>&"\''; const errors = { - _form: { message: specialMessage } + _form: { message: specialMessage }, }; render(); - + expect(screen.getByText(specialMessage)).toBeInTheDocument(); }); }); @@ -322,32 +313,24 @@ describe('FormError Component', () => { describe('Performance', () => { it('does not re-render unnecessarily when unrelated form state changes', () => { const renderSpy = jest.fn(); - + const CustomFormMessage = ({ children, ...props }: any) => { renderSpy(); return
{children}
; }; const errors = { - _form: { message: 'Performance test' } + _form: { message: 'Performance test' }, }; const { rerender } = render( - + , ); const initialRenderCount = renderSpy.mock.calls.length; // Re-render with same errors (should not cause additional renders) - rerender( - - ); + rerender(); expect(renderSpy.mock.calls.length).toBe(initialRenderCount); }); @@ -386,14 +369,14 @@ describe('FormError Integration Tests', () => { }; render(); - + // Form should render without errors initially expect(screen.queryByText(/error/i)).not.toBeInTheDocument(); - + // Submit button should be present and functional const submitButton = screen.getByRole('button', { name: /submit/i }); expect(submitButton).toBeInTheDocument(); - + // Form fields should be present expect(screen.getByLabelText(/email/i)).toBeInTheDocument(); expect(screen.getByLabelText(/password/i)).toBeInTheDocument(); diff --git a/apps/docs/src/remix-hook-form/textarea.stories.tsx b/apps/docs/src/remix-hook-form/textarea.stories.tsx index f3105279..d8995279 100644 --- a/apps/docs/src/remix-hook-form/textarea.stories.tsx +++ b/apps/docs/src/remix-hook-form/textarea.stories.tsx @@ -2,7 +2,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { Textarea } from '@lambdacurry/forms/remix-hook-form/textarea'; import { Button } from '@lambdacurry/forms/ui/button'; import type { Meta, StoryContext, StoryObj } from '@storybook/react-vite'; -import { expect, userEvent, within } from '@storybook/test'; +import { expect, userEvent } from '@storybook/test'; import { type ActionFunctionArgs, useFetcher } from 'react-router'; import { RemixFormProvider, createFormData, getValidatedFormData, useRemixForm } from 'remix-hook-form'; import { z } from 'zod'; @@ -103,7 +103,7 @@ const testInvalidSubmission = async ({ canvas }: StoryContext) => { await userEvent.clear(messageInput); await userEvent.type(messageInput, 'Short'); await userEvent.click(submitButton); - + // Check for validation error await expect(await canvas.findByText('Message must be at least 10 characters')).toBeInTheDocument(); }; diff --git a/package.json b/package.json index 1a9afef4..16f2631c 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,7 @@ "name": "forms", "version": "0.2.0", "private": true, - "workspaces": [ - "apps/*", - "packages/*" - ], + "workspaces": ["apps/*", "packages/*"], "scripts": { "start": "yarn dev", "dev": "turbo run dev", diff --git a/packages/components/package.json b/packages/components/package.json index 59a76647..afc4cb8e 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@lambdacurry/forms", - "version": "0.18.0", + "version": "0.18.1", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/components/src/data-table/index.ts b/packages/components/src/data-table/index.ts index 53517835..c5c7803c 100644 --- a/packages/components/src/data-table/index.ts +++ b/packages/components/src/data-table/index.ts @@ -12,4 +12,3 @@ export * from '../ui/data-table'; // Re-export types if needed export type * from '../ui/data-table-filter/core/types'; - diff --git a/packages/components/src/remix-hook-form/data-table-router-parsers.ts b/packages/components/src/remix-hook-form/data-table-router-parsers.ts index 52a641ab..bbd0153d 100644 --- a/packages/components/src/remix-hook-form/data-table-router-parsers.ts +++ b/packages/components/src/remix-hook-form/data-table-router-parsers.ts @@ -11,7 +11,6 @@ export type BazzaFiltersState = BazzaFilterItem[]; // Runtime parser for BazzaFiltersState const parseBazzaFiltersState = (value: unknown): BazzaFiltersState => { if (!Array.isArray(value)) { - // console.warn('Expected array for filters, got:', value); return []; // Return empty array or throw error based on desired strictness } return value.reduce((acc: BazzaFiltersState, item) => { @@ -34,7 +33,6 @@ const parseBazzaFiltersState = (value: unknown): BazzaFiltersState => { values: item.values, }); } else { - // console.warn('Invalid filter item:', item); } return acc; }, []); diff --git a/packages/components/src/remix-hook-form/form-error.tsx b/packages/components/src/remix-hook-form/form-error.tsx index fbd6a7ff..a4a322c6 100644 --- a/packages/components/src/remix-hook-form/form-error.tsx +++ b/packages/components/src/remix-hook-form/form-error.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { useRemixFormContext } from 'remix-hook-form'; import { FormErrorField } from '../ui/form-error-field'; import type { FormErrorFieldProps } from '../ui/form-error-field'; diff --git a/packages/components/src/ui/button.tsx b/packages/components/src/ui/button.tsx index 993bd0d5..7ec32f71 100644 --- a/packages/components/src/ui/button.tsx +++ b/packages/components/src/ui/button.tsx @@ -1,6 +1,6 @@ import { Slot } from '@radix-ui/react-slot'; import { type VariantProps, cva } from 'class-variance-authority'; -import * as React from 'react'; +import type * as React from 'react'; import type { ButtonHTMLAttributes } from 'react'; import { cn } from './utils'; @@ -39,14 +39,7 @@ export interface ButtonProps extends ButtonHTMLAttributes, Va export function Button({ className, variant, size, asChild = false, ref, ...props }: ButtonProps) { const Comp = asChild ? Slot : 'button'; - return ( - - ); + return ; } Button.displayName = 'Button'; diff --git a/packages/components/src/ui/data-table-filter/components/active-filters.tsx b/packages/components/src/ui/data-table-filter/components/active-filters.tsx index 773c2572..d4023084 100644 --- a/packages/components/src/ui/data-table-filter/components/active-filters.tsx +++ b/packages/components/src/ui/data-table-filter/components/active-filters.tsx @@ -110,11 +110,6 @@ export function ActiveFiltersMobileContainer({ children }: { children: React.Rea } }; - // Log blur states for debugging - // useEffect(() => { - // console.log('left:', showLeftBlur, ' right:', showRightBlur) - // }, [showLeftBlur, showRightBlur]) - // Set up ResizeObserver to monitor container size // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect(() => { diff --git a/packages/components/src/ui/data-table-filter/components/filter-selector.tsx b/packages/components/src/ui/data-table-filter/components/filter-selector.tsx index 4fcd2166..f9a66456 100644 --- a/packages/components/src/ui/data-table-filter/components/filter-selector.tsx +++ b/packages/components/src/ui/data-table-filter/components/filter-selector.tsx @@ -138,7 +138,7 @@ export function FilterableColumn({ // eslint-disable-next-line no-console console.warn('FilterableColumn: id is not a string', column); } - console.log('FilterableColumn CommandItem value:', column.id, 'keywords:', [displayName]); + const itemRef = useRef(null); const prefetch = useCallback(() => { @@ -237,8 +237,6 @@ function __QuickSearchFilters({ const checked = Boolean(filter?.values.includes(v.value)); const count = optionsCount?.get(v.value) ?? 0; - console.log('QuickSearchFilters option value:', v.value, 'label:', v.label); - // Defensive check for label if (typeof v.label !== 'string') { // eslint-disable-next-line no-console @@ -289,5 +287,3 @@ function __QuickSearchFilters({ ); } - -console.log('=== filter-selector.tsx loaded ==='); diff --git a/packages/components/src/ui/data-table-filter/core/filters.ts b/packages/components/src/ui/data-table-filter/core/filters.ts index 3b8fcad9..b9e20a50 100644 --- a/packages/components/src/ui/data-table-filter/core/filters.ts +++ b/packages/components/src/ui/data-table-filter/core/filters.ts @@ -341,7 +341,7 @@ export function createColumns( setTimeout(() => { const options = getOptions(); column._prefetchedOptionsCache = options; - // console.log(`Prefetched options for ${columnConfig.id}`) + resolve(undefined); }, 0), ); @@ -354,7 +354,7 @@ export function createColumns( setTimeout(() => { const values = getValues(); column._prefetchedValuesCache = values; - // console.log(`Prefetched values for ${columnConfig.id}`) + resolve(undefined); }, 0), ); @@ -367,9 +367,7 @@ export function createColumns( setTimeout(() => { const facetedMap = getUniqueValues(); column._prefetchedFacetedUniqueValuesCache = facetedMap ?? null; - // console.log( - // `Prefetched faceted unique values for ${columnConfig.id}`, - // ) + resolve(undefined); }, 0), ); @@ -382,9 +380,7 @@ export function createColumns( setTimeout(() => { const value = getMinMaxValues(); column._prefetchedFacetedMinMaxValuesCache = value ?? null; - // console.log( - // `Prefetched faceted min/max values for ${columnConfig.id}`, - // ) + resolve(undefined); }, 0), ); diff --git a/packages/components/src/ui/data-table-filter/hooks/use-debounce-callback.tsx b/packages/components/src/ui/data-table-filter/hooks/use-debounce-callback.tsx index 6159044e..d9ab1987 100644 --- a/packages/components/src/ui/data-table-filter/hooks/use-debounce-callback.tsx +++ b/packages/components/src/ui/data-table-filter/hooks/use-debounce-callback.tsx @@ -24,7 +24,9 @@ export function useDebounceCallback any>( delay = 500, options?: DebounceOptions, ): DebouncedState { - const debouncedFunc = useRef<(((...args: Parameters) => ReturnType | undefined) & ControlFunctions) | null>(null); + const debouncedFunc = useRef<(((...args: Parameters) => ReturnType | undefined) & ControlFunctions) | null>( + null, + ); useUnmount(() => { if (debouncedFunc.current) { diff --git a/packages/components/src/ui/data-table-filter/lib/memo.ts b/packages/components/src/ui/data-table-filter/lib/memo.ts index c8732c72..6e23fce5 100644 --- a/packages/components/src/ui/data-table-filter/lib/memo.ts +++ b/packages/components/src/ui/data-table-filter/lib/memo.ts @@ -7,17 +7,13 @@ export function memo( let cachedResult: TResult | undefined; return () => { - // console.log(`[memo] Calling memoized function: ${options.key}`) - const deps = getDeps(); // If no previous deps or deps have changed, recompute if (!prevDeps || !shallowEqual(prevDeps, deps)) { - // console.log(`[memo] Cache MISS - ${options.key}`) cachedResult = compute(deps); prevDeps = deps; } else { - // console.log(`[memo] Cache HIT - ${options.key}`) } return cachedResult!; diff --git a/packages/components/src/ui/form-error-field.tsx b/packages/components/src/ui/form-error-field.tsx index 9f23bacd..7b9383e5 100644 --- a/packages/components/src/ui/form-error-field.tsx +++ b/packages/components/src/ui/form-error-field.tsx @@ -1,5 +1,4 @@ -import * as React from 'react'; -import type { FieldPath, FieldValues, Control } from 'react-hook-form'; +import type { Control, FieldPath, FieldValues } from 'react-hook-form'; import { FormField, FormItem, FormMessage } from './form'; import type { FieldComponents } from './form'; @@ -29,9 +28,7 @@ export const FormErrorField = < render={({ fieldState }) => ( {fieldState.error && ( - - {fieldState.error.message} - + {fieldState.error.message} )} )} diff --git a/packages/components/src/ui/textarea.tsx b/packages/components/src/ui/textarea.tsx index 7f9a16b1..77c13b5f 100644 --- a/packages/components/src/ui/textarea.tsx +++ b/packages/components/src/ui/textarea.tsx @@ -22,7 +22,7 @@ const Textarea = React.forwardRef( data-slot="textarea" /> ); - } + }, ); Textarea.displayName = 'Textarea';