Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
23726c7
chore(dependencies): update Radix UI components and integrate Bazza U…
jaruesink May 12, 2025
37f90a2
Fix Bazza UI filter integration issues
codegen-sh[bot] May 16, 2025
1752e5f
fix: add missing barrel file for useDataTableFilters hook
codegen-sh[bot] May 16, 2025
aa43159
fix: replace NodeJS.Timeout with ReturnType<typeof setTimeout> for be…
codegen-sh[bot] May 16, 2025
e17dbb8
chore: standardize sort parameter
jaruesink May 20, 2025
56d4c68
Fix useFilterSync to support functional updates
codegen-sh[bot] May 20, 2025
f97b817
feat: enhance Bazza UI filter stories with comprehensive examples
codegen-sh[bot] May 23, 2025
c609b8a
feat: Add comprehensive testing and documentation for Bazza UI Data T…
codegen-sh[bot] May 23, 2025
0672c0a
fix: Resolve test failures in Bazza UI Data Table Filter tests
codegen-sh[bot] May 23, 2025
c6a58bd
fix: Add waitFor import and use findByRole for better test reliability
codegen-sh[bot] May 23, 2025
52b8851
fix: resolve circular dependencies and fix unit tests
codegen-sh[bot] May 23, 2025
bf6a848
fix: resolve story file structure issues for Playwright tests
codegen-sh[bot] May 23, 2025
2e189ec
Fix Playwright tests: Add missing DataTableFilter import and fix Reac…
codegen-sh[bot] May 24, 2025
8874c05
Enhance Storybook testing rules and examples
jaruesink May 25, 2025
aaceaff
Update route path in ClientSide story for DataTableWithClientSideFilters
jaruesink May 25, 2025
184598a
Fix DataTable pagination and accessibility
codegen-sh[bot] May 26, 2025
7562128
Fix filter tests: Add screen import, improve timing, and try multiple…
codegen-sh[bot] May 26, 2025
1673cb6
Add React Router decorator and testSimpleFilter function
codegen-sh[bot] May 26, 2025
b86322e
Update GitHub Actions workflow to allow pull requests from any branch
jaruesink May 26, 2025
7ac5e5d
Update biome schema and clean up data-table stories
jaruesink May 26, 2025
21645cb
Improve date range formatting and validation in FilterValue components
jaruesink May 26, 2025
6d1e36e
Refactor DataTable component to remove pagination prop and simplify s…
jaruesink May 26, 2025
2697126
Add autodocs tags and enhance stories for client-side and server-driv…
jaruesink May 26, 2025
334aa26
remove legacy migration content
codegen-sh[bot] May 29, 2025
820d0d6
fix: Add react-router-dom as external dependency and install package
codegen-sh[bot] Jun 16, 2025
0b8a866
fix: Update react-router-dom imports to react-router for v7 compatibi…
codegen-sh[bot] Jun 16, 2025
c4cf1e9
chore: cleanup
dwene Jun 16, 2025
454ad1c
chore: bump package.json
dwene Jun 16, 2025
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
459 changes: 457 additions & 2 deletions .cursor/rules/storybook-testing.mdc

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [main]
pull_request:
branches: [main]
branches: ["*"]
workflow_dispatch:

jobs:
Expand Down
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cSpell.words": [
"autodocs",
"Bazza",
"biomejs",
"cleanbuild",
"Filenaming",
Expand Down
63 changes: 62 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,69 @@

Checkout our [Storybook Documentation](https://lambda-curry.github.io/forms/?path=/docs/0-1-hello-world-start-here--docs) to see the components in action and get started.

A form library for React applications.
A comprehensive form library for React applications with modern data table filtering capabilities.

## ✨ New: Bazza UI Data Table Filters

We've added a powerful, accessible filtering system inspired by Linear's interface:

- πŸŽ›οΈ **Multiple Filter Types**: Text, option, date, and number filters
- πŸ”— **URL State Synchronization**: Filter state persists across page refreshes
- πŸ“Š **Faceted Filtering**: Dynamic option counts based on current filters
- ⚑ **Client & Server-Side**: Flexible filtering strategies for any dataset size
- β™Ώ **Accessibility**: Full WCAG 2.1 AA compliance
- 🎨 **Modern UI**: Clean, Linear-inspired design

### Quick Example

```typescript
import { DataTableFilter } from '@lambdacurry/forms/ui/data-table-filter';
import { useDataTableFilters } from '@lambdacurry/forms/ui/data-table-filter/hooks/use-data-table-filters';
import { createColumnConfigHelper } from '@lambdacurry/forms/ui/data-table-filter/core/filters';

const dtf = createColumnConfigHelper<YourDataType>();

const columnConfigs = [
dtf.text().id('title').accessor(row => row.title).displayName('Title').build(),
dtf.option().id('status').accessor(row => row.status).displayName('Status')
.options([
{ value: 'active', label: 'Active' },
{ value: 'inactive', label: 'Inactive' },
]).build(),
];

const MyTable = () => {
const [filters, setFilters] = useFilterSync();
const { columns, actions, strategy } = useDataTableFilters({
columnsConfig: columnConfigs,
filters,
onFiltersChange: setFilters,
strategy: 'client',
data: yourData,
});

return <DataTableFilter columns={columns} filters={filters} actions={actions} strategy={strategy} />;
};
```

πŸ“– **[View Complete Filter Documentation](./packages/components/src/ui/data-table-filter/README.md)**

## Features

### Form Components
- Comprehensive form field components with validation
- React Hook Form integration
- Remix integration for server-side forms
- TypeScript support with excellent IntelliSense

### Data Table Filtering
- Modern Linear-inspired filter interface
- Multiple filter types (text, option, date, number)
- URL state synchronization for filter persistence
- Faceted filtering with dynamic option counts
- Client-side and server-side filtering strategies
- Full accessibility support (WCAG 2.1 AA)
- Comprehensive test coverage

## Getting Started

Expand Down
5 changes: 1 addition & 4 deletions apps/docs/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ function getAbsolutePath(value: string): string {
}
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
getAbsolutePath('@storybook/addon-links'),
getAbsolutePath("@storybook/addon-docs")
],
addons: [getAbsolutePath('@storybook/addon-links'), getAbsolutePath('@storybook/addon-docs')],
framework: {
name: getAbsolutePath('@storybook/react-vite'),
options: {},
Expand Down
51 changes: 18 additions & 33 deletions apps/docs/src/examples/middleware-example.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { TextField } from '@lambdacurry/forms/remix-hook-form';
// Example of using the new middleware feature in remix-hook-form v7.0.0
import { Form } from 'react-router';
import type { ActionFunctionArgs } from 'react-router';
import { RemixFormProvider, useRemixForm } from 'remix-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as zod from 'zod';
import { TextField } from '@lambdacurry/forms/remix-hook-form';
import { getValidatedFormData } from 'remix-hook-form/middleware';
import type { ActionFunctionArgs } from 'react-router';
import * as zod from 'zod';

// Define schema and types
const schema = zod.object({
name: zod.string().min(1, "Name is required"),
email: zod.string().email("Invalid email format").min(1, "Email is required"),
name: zod.string().min(1, 'Name is required'),
email: zod.string().email('Invalid email format').min(1, 'Email is required'),
});

type FormData = zod.infer<typeof schema>;
Expand All @@ -19,18 +19,15 @@ const resolver = zodResolver(schema);
// Action function using the new middleware
export const action = async ({ context }: ActionFunctionArgs) => {
// Use the middleware to extract and validate form data
const { errors, data, receivedValues } = await getValidatedFormData<FormData>(
context,
resolver
);

const { errors, data, receivedValues } = await getValidatedFormData<FormData>(context, resolver);

if (errors) {
return { errors, defaultValues: receivedValues };
}

// Process the validated data
console.log('Processing data:', data);

return { success: true, data };
};

Expand All @@ -41,34 +38,22 @@ export default function MiddlewareExample() {
formState: { errors },
register,
} = useRemixForm<FormData>({
mode: "onSubmit",
mode: 'onSubmit',
resolver,
});

return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4">Remix Hook Form v7 Middleware Example</h1>

<RemixFormProvider>
<Form method="POST" onSubmit={handleSubmit}>
<div className="space-y-4">
<TextField
label="Name"
{...register("name")}
error={errors.name?.message}
/>

<TextField
label="Email"
type="email"
{...register("email")}
error={errors.email?.message}
/>

<button
type="submit"
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
<TextField label="Name" {...register('name')} error={errors.name?.message} />

<TextField label="Email" type="email" {...register('email')} error={errors.email?.message} />

<button type="submit" className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
Submit
</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/main.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import 'tailwindcss';
@import "tailwindcss";
@source "../../../packages/components";

:root {
Expand Down
12 changes: 6 additions & 6 deletions apps/docs/src/remix-hook-form/checkbox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,11 @@ const ControlledCheckboxExample = () => {
const termsCheckbox = canvas.getByLabelText('Accept terms and conditions');
const marketingCheckbox = canvas.getByLabelText('Receive marketing emails');
const requiredCheckbox = canvas.getByLabelText('This is a required checkbox');

expect(termsCheckbox).not.toBeChecked();
expect(marketingCheckbox).not.toBeChecked();
expect(requiredCheckbox).not.toBeChecked();

// Verify submit button is present
const submitButton = canvas.getByRole('button', { name: 'Submit' });
expect(submitButton).toBeInTheDocument();
Expand All @@ -155,7 +155,7 @@ const ControlledCheckboxExample = () => {
// Submit form without checking required checkboxes
const submitButton = canvas.getByRole('button', { name: 'Submit' });
await userEvent.click(submitButton);

// Verify validation error messages appear
await expect(canvas.findByText('You must accept the terms and conditions')).resolves.toBeInTheDocument();
await expect(canvas.findByText('This field is required')).resolves.toBeInTheDocument();
Expand All @@ -165,14 +165,14 @@ const ControlledCheckboxExample = () => {
// Check required checkboxes
const termsCheckbox = canvas.getByLabelText('Accept terms and conditions');
const requiredCheckbox = canvas.getByLabelText('This is a required checkbox');

await userEvent.click(termsCheckbox);
await userEvent.click(requiredCheckbox);

// Submit form
const submitButton = canvas.getByRole('button', { name: 'Submit' });
await userEvent.click(submitButton);

// Verify success message
await expect(canvas.findByText('Form submitted successfully')).resolves.toBeInTheDocument();
});
Expand Down
Loading
Loading