Skip to content
This repository was archived by the owner on Mar 25, 2026. It is now read-only.
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/add-pr-to-project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:

} catch (error) {
console.error('Failed to add PR to project:', error.message);

// If it fails because it's already in the project, that's fine
if (error.message.includes('already exists')) {
console.log('PR already in project');
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/deploy-demo-preview-smart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ jobs:
issue_number: prNumber
});

const botComment = comments.data.find(comment =>
comment.user.type === 'Bot' &&
const botComment = comments.data.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('Demo Preview Ready!')
);

Expand Down Expand Up @@ -300,10 +300,10 @@ jobs:
console.log(`Updating project "${project.title}"`);

// Find the Preview URL field
const previewUrlField = project.fields.nodes.find(f =>
const previewUrlField = project.fields.nodes.find(f =>
f.name === 'Preview URL' && f.dataType === 'TEXT'
);

if (!previewUrlField) {
console.log('Preview URL field not found in project');
continue;
Expand Down Expand Up @@ -331,7 +331,7 @@ jobs:
fieldId: previewUrlField.id,
value: { text: previewUrl }
});

console.log(`✅ Updated Preview URL field to: ${previewUrl}`);
}
} catch (error) {
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/manual-preview-control.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ permissions:
jobs:
handle-preview-command:
if: |
github.event.issue.pull_request &&
github.event.issue.pull_request &&
(contains(github.event.comment.body, '/preview') || contains(github.event.comment.body, '/skip-preview'))
runs-on: ubuntu-latest
steps:
Expand All @@ -31,7 +31,7 @@ jobs:
repo,
username: commenter
});

if (['admin', 'write'].includes(permissionLevel.permission)) {
core.setOutput('has-permission', 'true');
} else {
Expand Down Expand Up @@ -65,7 +65,7 @@ jobs:
issue_number: issueNumber,
labels: ['deploy-preview']
});

// Remove skip-preview if present
try {
await github.rest.issues.removeLabel({
Expand All @@ -75,14 +75,14 @@ jobs:
name: 'skip-preview'
});
} catch (e) {}

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: '🚀 Preview deployment triggered! The preview will be ready in a few minutes.'
});

} else if (comment.includes('/skip-preview')) {
// Add skip-preview label
await github.rest.issues.addLabels({
Expand All @@ -91,7 +91,7 @@ jobs:
issue_number: issueNumber,
labels: ['skip-preview']
});

// Remove deploy-preview if present
try {
await github.rest.issues.removeLabel({
Expand All @@ -101,7 +101,7 @@ jobs:
name: 'deploy-preview'
});
} catch (e) {}

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/update-preview-url-field.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ jobs:
console.log(`Updating project "${project.title}"`);

// Find the Preview URL field
const previewUrlField = project.fields.nodes.find(f =>
const previewUrlField = project.fields.nodes.find(f =>
f.name === 'Preview URL' && f.dataType === 'TEXT'
);

if (!previewUrlField) {
console.log('Preview URL field not found in project');
continue;
Expand Down Expand Up @@ -127,7 +127,7 @@ jobs:
fieldId: previewUrlField.id,
value: { text: previewUrl }
});

console.log(`✅ Updated Preview URL field to: ${previewUrl}`);
}
} catch (error) {
Expand Down Expand Up @@ -203,13 +203,13 @@ jobs:
});

const projectItems = projectData.repository.pullRequest?.projectItems?.nodes || [];

for (const projectItem of projectItems) {
const project = projectItem.project;
const previewUrlField = project.fields.nodes.find(f =>
const previewUrlField = project.fields.nodes.find(f =>
f.name === 'Preview URL' && f.dataType === 'TEXT'
);

if (!previewUrlField) continue;

// Clear the field
Expand All @@ -234,7 +234,7 @@ jobs:
fieldId: previewUrlField.id,
value: { text: "" }
});

console.log('✅ Cleared Preview URL field');
}
} catch (error) {
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ dist-ssr
# Testing
coverage
.vitest-cache
test-results/
playwright-report/

# Deployment
dist-demo
Expand All @@ -38,7 +40,8 @@ dist-demo
ryans_notes/

# Screenshots and temporary files
# *.png - Removed to allow hero screenshot
# *.png - Removed to allow hero screenshot in demo assets
screenshots/
simple-test.html
clause copy.md
clause*.md
Expand Down
152 changes: 123 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

🎉 **Pre-release: Modular Architecture with Minimal Bundle Size!**

A collection of powerful, tree-shakeable React components for AG Grid (v33.3.0+) that enhance your data grid with advanced filtering and state management capabilities. Start with just 25KB or add features as needed.
A collection of powerful, headless React components for AG Grid (v33.3.0+) built on top of Headless UI. Enhance your data grid with advanced filtering and state management capabilities while maintaining complete control over styling. Tree-shakeable architecture lets you use only what you need.

📖 **[Full Documentation →](./docs/)**
🚀 **[Live Demo →](https://demo.rozich.net/ag-grid-react-components/)**
Expand Down Expand Up @@ -38,6 +38,17 @@ A component that displays active filters as removable pills:
- **Filter Types**: Handles date, text, and set filters
- **Customizable**: Style with CSS classes

### 💾 Saved Views Management

Complete saved views system with categories and defaults:

- **Save Views**: Save filters-only or full grid state
- **Categories**: Organize views with custom categories
- **Default View**: Set a default view that loads on startup
- **Import/Export**: Share views between users
- **Management UI**: Full CRUD operations on saved views
- **Grid Reset**: Reset to default view or factory settings

### 🔗 URL State Persistence

Comprehensive grid state persistence with URL synchronization:
Expand All @@ -51,27 +62,19 @@ Comprehensive grid state persistence with URL synchronization:

## 📦 Installation

Choose your installation based on your needs:

### Minimal Installation (25KB)

```bash
# Install the complete package (tree-shakeable)
npm install ag-grid-react-components
```

### With React DatePicker (65KB)
### Optional Dependencies

```bash
# Install with peer dependencies for date picker
npm install ag-grid-react-components react-datepicker
```
# For enhanced date picker UI (adds ~40KB)
npm install react-datepicker

### Full Installation (85KB)

```bash
# Install with all optional dependencies
npm install ag-grid-react-components react-datepicker lz-string
# For URL compression support (adds ~20KB)
npm install lz-string
```

## 📋 Requirements
Expand Down Expand Up @@ -138,16 +141,21 @@ function App() {

return (
<div>
<QuickFilterDropdown
api={gridApi}
columnId="date"
options={[
{ id: "today", label: "Today" },
{ id: "week", label: "This Week" },
]}
/>

<AgGridReact columnDefs={columnDefs} onGridReady={onGridReady} rowData={rowData} />
{/* Filter toolbar */}
<div className="toolbar">
<QuickFilterDropdown
api={gridApi}
columnId="date"
options={[
{ id: "today", label: "Today" },
{ id: "week", label: "This Week" },
]}
/>

<ActiveFilters api={gridApi} filterModel={filterModel} />
</div>

<AgGridReact columnDefs={columnDefs} onGridReady={onGridReady} onFilterChanged={() => setFilterModel(gridApi?.getFilterModel() || {})} rowData={rowData} />
</div>
);
}
Expand Down Expand Up @@ -252,6 +260,35 @@ const QuickFilterDropdown = createQuickFilterDropdown();
/>
```

### Saved Views Dropdown

Complete saved views management with categories:

```typescript
import { SavedViewsDropdown } from "ag-grid-react-components";

<SavedViewsDropdown
api={gridApi}
columnId="dateCreated"
placeholder="My Views"
showManagementMenu={true}
onViewChange={(view) => {
console.log('Applied view:', view);
}}
/>
```

This single component provides:

- Dropdown to select saved views
- Three-dots menu with actions
- Save/manage views modals
- Import/export functionality
- Default view support
- Grid reset options

See [Saved Views Documentation](./docs/SAVEDVIEWS_API.md) for full API.

### Grid State Persistence

```typescript
Expand Down Expand Up @@ -537,16 +574,73 @@ const customOptions = [

### Styling

The components use CSS modules and can be customized via CSS variables:
All components are **headless** - they ship with no styles, giving you complete control over appearance while providing excellent accessibility through Headless UI.

#### Option 1: Tailwind CSS (Used in Demo)

Our demo showcases styling with Tailwind CSS:

```tsx
<SaveViewModal
panelClassName="bg-gray-900 border border-gray-700/50 rounded-xl shadow-2xl"
titleClassName="text-xl font-semibold text-gray-100"
buttonClassName="px-4 py-2 text-sm font-medium rounded-md"
/>

<CategorySelector
inputClassName="w-full rounded-lg border border-gray-300"
dropdownClassName="absolute mt-1 bg-white shadow-lg"
/>
```

See the [Tailwind Styling Guide](./docs/TAILWIND_STYLING_EXAMPLES.md) for examples.

#### Option 2: Custom CSS

```css
:root {
--agrc-primary: #2563eb;
--agrc-border: #e5e7eb;
--agrc-hover: #f3f4f6;
/* Your custom styles */
.my-dropdown {
background: white;
border: 1px solid #e5e7eb;
border-radius: 6px;
}

.my-input {
width: 100%;
padding: 0.5rem;
border: 1px solid #d1d5db;
}
```

```tsx
<QuickFilterDropdown className="my-dropdown" triggerClassName="my-input" />
```

#### Option 3: CSS-in-JS

```tsx
import styled from "styled-components";

// Style the components with your CSS-in-JS solution
<SaveViewModal panelClassName={styledPanelClass} titleClassName={styledTitleClass} />;
```

## 🎨 Styling Philosophy

We ship **zero CSS** - you have complete control:

1. **Headless by Design**: No built-in styles or opinions
2. **Built on Headless UI**: Accessible, keyboard-navigable components
3. **Framework Agnostic**: Use CSS, Sass, Tailwind, CSS-in-JS, etc.
4. **Demo Uses Tailwind**: Shows one way to style components

This approach ensures:

- No style conflicts with your design system
- Smaller bundle size (no unused CSS)
- Complete customization freedom
- Built-in accessibility features

## 🧪 Testing

Components are thoroughly tested with:
Expand Down
Loading
Loading