Skip to content

Commit 6d872bf

Browse files
authored
fix(datatable): fix ilike filter in string columns (#513)
* feat: add agents.md * chore: setup vitest * chore: remove jest * tests: update spinner tests * chore: update agents.md * chore: update agents.md * tests: add tests for data-table utils * refactor: move EmptyFilterValue from types file * chore: add type for RQL string operators * refactor: add type for DataFilter and InternalFilter * feat: add starts and ends with filters * tests: update tests * tests: add tests for new string filters * tests: add tests for wildcard characters * ci: add CI job for tests and lint * ci: remove lint check * ci: update vitest config file type * refactor: rename RQL to DataTableQuery
1 parent 49316e9 commit 6d872bf

File tree

17 files changed

+3537
-1938
lines changed

17 files changed

+3537
-1938
lines changed

.github/workflows/tests.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Tests
2+
3+
on:
4+
pull_request:
5+
branches: [main, develop]
6+
push:
7+
branches: [main, develop]
8+
9+
jobs:
10+
test-and-lint:
11+
runs-on: ubuntu-latest
12+
13+
strategy:
14+
matrix:
15+
node-version: [18.x, 20.x]
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
21+
- name: Setup pnpm
22+
uses: pnpm/action-setup@v4
23+
with:
24+
version: 9.3.0
25+
26+
- name: Setup Node.js ${{ matrix.node-version }}
27+
uses: actions/setup-node@v4
28+
with:
29+
node-version: ${{ matrix.node-version }}
30+
cache: "pnpm"
31+
32+
- name: Install dependencies
33+
run: pnpm install --frozen-lockfile
34+
35+
- name: Run tests
36+
run: pnpm --filter=@raystack/apsara test
37+
38+
- name: Check build
39+
run: pnpm build:apsara

agents.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Agent Guidelines
2+
3+
This file contains important guidelines and preferences for AI agents working on this project. Following these guidelines will help maintain code quality, consistency, and project standards.
4+
5+
## 🎯 Project Overview
6+
7+
This is **Apsara Design System** - a React component library built with:
8+
- **TypeScript** for type safety
9+
- **CSS Modules** for styling
10+
- **Vitest** for testing
11+
- **pnpm** for package management
12+
- **Biome** for code formatting and linting
13+
- **Monorepo structure** with documentation site
14+
15+
## 🏗️ Project Structure
16+
17+
```
18+
packages/raystack/ # Main component library
19+
├── components/ # React components
20+
├── hooks/ # Custom hooks
21+
├── icons/ # Icon components
22+
├── styles/ # Global styles
23+
├── types/ # Type definitions
24+
└── test-utils.tsx # Testing utilities
25+
26+
apps/www/ # Documentation site
27+
├── src/content/docs/ # Component documentation
28+
└── src/components/playground/ # Demo examples
29+
```
30+
31+
## 🚨 Critical Rules
32+
33+
- **NO `any` TYPES** - Use specific types, `unknown`, or generics
34+
- **NO direct npm/yarn** - Always use `pnpm`
35+
- **NO ignored linting errors** - Fix all issues properly
36+
- **NO outdated documentation** - Keep docs in sync with code
37+
- **NO CSS-in-JS or inline styles** - Use CSS Modules only
38+
- **NO Tailwind CSS** - Use CSS Modules for all styling
39+
40+
## 📦 Package Management
41+
42+
- **Always use `pnpm`** - Never use `npm` or `yarn`
43+
- Installing dependencies: `pnpm add <package>` or `pnpm add -D <package>`
44+
- Running scripts: `pnpm run <script>` or `pnpm <script>`
45+
46+
## 📝 TypeScript Best Practices
47+
48+
- **Never use `any` type** - This is a strict rule
49+
- Use specific types, interfaces, or union types instead
50+
- Prefer `unknown` over `any` when type is genuinely unknown
51+
- Use generic types `<T>` for reusable components/functions
52+
- Always provide explicit return types for functions
53+
54+
## 🎨 Styling & Design
55+
56+
- **Use CSS Modules exclusively** for component styling
57+
- File naming: `component-name.module.css`
58+
- Import: `import styles from './component-name.module.css'`
59+
- Usage: `className={styles.className}`
60+
- **Follow accessibility standards** (ARIA labels, keyboard navigation)
61+
- **Ensure responsive design** works across different screen sizes
62+
- **Use semantic HTML** elements appropriately
63+
64+
## 🧪 Testing Standards
65+
66+
- **Use Vitest** for all tests (migrated from Jest)
67+
- Write comprehensive tests for utilities and components
68+
- Use proper TypeScript types in tests (never use `any`)
69+
- Import from `vitest` not `jest`
70+
- Test file pattern: `__tests__/component.test.tsx`
71+
72+
## 📚 Documentation Requirements
73+
74+
When adding/updating components:
75+
- **Component docs**: Update `apps/www/src/content/docs/components/`
76+
- **Demo examples**: Update `apps/www/src/components/playground/`
77+
- **Props documentation**: Keep API docs in sync with implementation
78+
79+
Required for:
80+
- New components or hooks
81+
- API/props changes
82+
- New styling variants
83+
- Behavior modifications
84+
85+
## 🚀 Development Workflow
86+
87+
1. **Analyze existing patterns** before implementing new features
88+
2. **Run `pnpm format`** after making changes
89+
3. **Write tests** using Vitest for new utilities/components
90+
4. **Update documentation** when changing component APIs
91+
5. **Start with minimal viable implementation** then enhance
92+
6. **Test early and often** during development
93+
94+
## 🤝 Communication & Best Practices
95+
96+
- **Be transparent** about limitations or uncertainties
97+
- **Ask for clarification** when requirements are ambiguous
98+
- **Explain your reasoning** for architectural decisions
99+
- **Provide detailed error messages** with context
100+
- **Include relevant code snippets** when reporting issues
101+
102+
## ✅ Quality Checklist
103+
104+
Before completing any task:
105+
- [ ] Code follows TypeScript best practices (no `any`)
106+
- [ ] Styling uses CSS Modules correctly
107+
- [ ] Tests are written and passing
108+
- [ ] Documentation is updated
109+
- [ ] Code is formatted with `pnpm format`
110+
- [ ] Follows existing project patterns
111+
- [ ] No linting errors remain
112+
- [ ] Accessibility considerations addressed
113+
- [ ] Error states and edge cases handled
114+
115+
## 📋 Task Completion
116+
117+
When finishing a task:
118+
1. **Summarize what was implemented** - Key features and changes
119+
2. **Note any limitations** - What wasn't implemented or edge cases
120+
3. **Provide usage examples** - How to use the new functionality
121+
4. **Suggest next steps** - Potential improvements or related tasks
122+
5. **Confirm all requirements met** - Review against original request
123+
124+
---
125+
126+
Following these guidelines ensures high-quality, maintainable code that aligns with project standards and team expectations.

packages/raystack/components/data-table/data-table.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import { Toolbar } from './components/toolbar';
1515
import { TableContext } from './context';
1616
import {
1717
DataTableProps,
18-
DataTableQuery,
1918
GroupedData,
19+
InternalQuery,
2020
TableContextType,
2121
TableQueryUpdateFn,
2222
defaultGroupOption
@@ -28,7 +28,7 @@ import {
2828
groupData,
2929
hasQueryChanged,
3030
queryToTableState,
31-
sanitizeTableQuery
31+
transformToDataTableQuery
3232
} from './utils';
3333

3434
function DataTableRoot<TData, TValue>({
@@ -51,9 +51,9 @@ function DataTableRoot<TData, TValue>({
5151
initialColumnVisibility
5252
);
5353
const [tableQuery, setTableQuery] =
54-
useState<DataTableQuery>(defaultTableQuery);
54+
useState<InternalQuery>(defaultTableQuery);
5555

56-
const oldQueryRef = useRef<DataTableQuery | null>(null);
56+
const oldQueryRef = useRef<InternalQuery | null>(null);
5757

5858
const reactTableState = useMemo(
5959
() => queryToTableState(tableQuery),
@@ -84,7 +84,7 @@ function DataTableRoot<TData, TValue>({
8484
hasQueryChanged(oldQueryRef.current, tableQuery) &&
8585
mode === 'server'
8686
) {
87-
onTableQueryChange(sanitizeTableQuery(tableQuery));
87+
onTableQueryChange(transformToDataTableQuery(tableQuery));
8888
oldQueryRef.current = tableQuery;
8989
}
9090
}, [tableQuery, onTableQueryChange]);

packages/raystack/components/data-table/data-table.types.tsx

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,66 @@
1-
import type { Column, ColumnDef, Table } from "@tanstack/table-core";
1+
import type { Column, ColumnDef, Table } from '@tanstack/table-core';
22
import type {
3+
DataTableFilterOperatorTypes,
34
FilterOperatorTypes,
45
FilterSelectOption,
56
FilterTypes,
6-
FilterValueType,
7-
} from "~/types/filters";
7+
FilterValueType
8+
} from '~/types/filters';
89

9-
export type DataTableMode = "client" | "server";
10+
export type DataTableMode = 'client' | 'server';
1011

1112
export const SortOrders = {
12-
ASC: "asc",
13-
DESC: "desc",
13+
ASC: 'asc',
14+
DESC: 'desc'
1415
} as const;
1516

16-
export interface RQLFilterValues {
17+
export interface DataTableFilterValues {
1718
value: any;
1819
// Only one of these value fields should be present at a time
1920
boolValue?: boolean;
2021
stringValue?: string;
2122
numberValue?: number;
2223
}
23-
export interface RQLFilter extends RQLFilterValues {
24+
// Internal filter with UI operators and metadata
25+
export interface InternalFilter extends DataTableFilterValues {
2426
_type?: FilterTypes;
2527
_dataType?: FilterValueType;
2628
name: string;
2729
operator: FilterOperatorTypes;
2830
}
2931

32+
// Data table filter for backend API (no internal fields)
33+
export interface DataTableFilter extends DataTableFilterValues {
34+
name: string;
35+
operator: DataTableFilterOperatorTypes;
36+
}
37+
3038
type SortOrdersKeys = keyof typeof SortOrders;
31-
export type SortOrdersValues = typeof SortOrders[SortOrdersKeys];
39+
export type SortOrdersValues = (typeof SortOrders)[SortOrdersKeys];
3240

3341
export interface DataTableSort {
3442
name: string;
3543
order: SortOrdersValues;
3644
}
3745

38-
export interface DataTableQuery {
39-
filters?: RQLFilter[];
46+
// Internal query with UI operators and metadata
47+
export interface InternalQuery {
48+
filters?: InternalFilter[];
4049
sort?: DataTableSort[];
4150
group_by?: string[];
4251
offset?: number;
4352
limit?: number;
4453
search?: string;
4554
}
4655

56+
// Data table query for backend API (clean, no internal fields)
57+
export interface DataTableQuery extends Omit<InternalQuery, 'filters'> {
58+
filters?: DataTableFilter[];
59+
}
60+
4761
export type DataTableColumn<TData, TValue> = Omit<
4862
Column<TData, TValue>,
49-
"columnDef"
63+
'columnDef'
5064
> & {
5165
columnDef: DataTableColumnDef<TData, TValue>;
5266
};
@@ -80,7 +94,7 @@ export type DataTableColumnDef<TData, TValue> = ColumnDef<TData, TValue> & {
8094
export interface DataTableProps<TData, TValue> {
8195
columns: DataTableColumnDef<TData, TValue>[];
8296
data: TData[];
83-
query?: DataTableQuery;
97+
query?: DataTableQuery; // Initial query (will be transformed to internal format)
8498
mode?: DataTableMode;
8599
isLoading?: boolean;
86100
loadingRowCount?: number;
@@ -102,7 +116,7 @@ export type DataTableContentProps = {
102116
};
103117
};
104118

105-
export type TableQueryUpdateFn = (query: DataTableQuery) => DataTableQuery;
119+
export type TableQueryUpdateFn = (query: InternalQuery) => InternalQuery;
106120

107121
export type TableContextType<TData, TValue> = {
108122
table: Table<TData>;
@@ -111,7 +125,7 @@ export type TableContextType<TData, TValue> = {
111125
loadMoreData: () => void;
112126
mode: DataTableMode;
113127
defaultSort: DataTableSort;
114-
tableQuery?: DataTableQuery;
128+
tableQuery?: InternalQuery;
115129
loadingRowCount?: number;
116130
onDisplaySettingsReset: () => void;
117131
updateTableQuery: (fn: TableQueryUpdateFn) => void;
@@ -124,7 +138,7 @@ export interface ColumnData {
124138
isVisible?: boolean;
125139
}
126140

127-
interface SubRows<T> {}
141+
interface SubRows<_T> {}
128142

129143
export interface GroupedData<T> extends SubRows<T> {
130144
label: string;
@@ -135,8 +149,6 @@ export interface GroupedData<T> extends SubRows<T> {
135149
}
136150

137151
export const defaultGroupOption = {
138-
id: "--",
139-
label: "No grouping",
152+
id: '--',
153+
label: 'No grouping'
140154
};
141-
142-
export const EmptyFilterValue = "--empty--";
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
export { DataTable } from "./data-table";
22
export {
33
DataTableColumnDef,
4+
InternalQuery,
45
DataTableQuery,
56
DataTableSort,
6-
EmptyFilterValue,
7+
InternalFilter,
8+
DataTableFilter,
79
} from "./data-table.types";
10+
export { EmptyFilterValue } from "~/types/filters";
811
export { useDataTable } from "./hooks/useDataTable";

0 commit comments

Comments
 (0)