Skip to content
Merged
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
40 changes: 40 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module.exports = {
root: true,
env: {
browser: true,
es2022: true,
node: true,
},
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
},
settings: {
react: {
version: "detect",
},
},
plugins: ["@typescript-eslint", "react", "react-hooks"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
],
rules: {
"react/react-in-jsx-scope": "off",
},
overrides: [
{
files: ["**/*.test.ts", "**/*.test.tsx", "src/test/setupTests.ts"],
env: {
jest: true,
},
},
],
ignorePatterns: ["dist/", "node_modules/"],
};
32 changes: 32 additions & 0 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: PR Checks

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]

jobs:
validate:
name: Lint, Type Check, and Tests
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Run lint
run: npm run lint

- name: Run type-check
run: npm run type-check

- name: Run tests
run: npm test
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
node_modules
dist
dist
# Local Cursor settings/rules are intentionally not versioned.
.cursor/
6 changes: 6 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run type-check
npx lint-staged
npm test
99 changes: 49 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,73 +70,73 @@ export function ExampleTable() {

### TableComponent root props

| Prop Name | Prop Type | Is Mandatory | Default Value | Description |
| ---------- | ---------------------------------------------------------- | ------------ | ------------- | ----------- |
| headers | `Record<string, string>` | true | | Maps each object key to a table header label. |
| data | `Record<string, number \| string \| React.ReactNode>[]` | true | | Table rows. Keys should match `headers`. |
| title | `string` | false | | Optional table title. |
| pagination | `TablePaginationConfig` | false | `{}` | Pagination behavior and callbacks. |
| sorting | `TableSortingConfig` | false | `{}` | Sorting behavior and callbacks. |
| search | `TableSearchConfig` | false | `{}` | Search behavior and callbacks. |
| filter | `TableFilterConfig` | false | `{}` | Filter behavior and callbacks. |
| styling | `TableStylingConfig` | false | `{}` | Style and theme options. |
| Prop Name | Prop Type | Is Mandatory | Default Value | Description |
| ---------- | ------------------------------------------------------- | ------------ | ------------- | --------------------------------------------- |
| headers | `Record<string, string>` | true | | Maps each object key to a table header label. |
| data | `Record<string, number \| string \| React.ReactNode>[]` | true | | Table rows. Keys should match `headers`. |
| title | `string` | false | | Optional table title. |
| pagination | `TablePaginationConfig` | false | `{}` | Pagination behavior and callbacks. |
| sorting | `TableSortingConfig` | false | `{}` | Sorting behavior and callbacks. |
| search | `TableSearchConfig` | false | `{}` | Search behavior and callbacks. |
| filter | `TableFilterConfig` | false | `{}` | Filter behavior and callbacks. |
| styling | `TableStylingConfig` | false | `{}` | Style and theme options. |

### `pagination` config (`TablePaginationConfig`)

| Field | Type | Default | Description |
| ----------- | ----------------------- | -------- | ----------- |
| isExternal | `boolean` | `false` | Indicates if pagination state is controlled externally. |
| currentPage | `number` | `1` | Current page for controlled/external pagination. |
| pageSize | `number` | `10` | Number of rows per page. |
| totalPages | `number` | | Optional total pages value. |
| location | `"right" \| "left" \| "center"` | | Pagination alignment. |
| onChange | `(page: number) => void`| | Triggered whenever page changes. |
| Field | Type | Default | Description |
| ----------- | ------------------------------- | ------- | ------------------------------------------------------- |
| isExternal | `boolean` | `false` | Indicates if pagination state is controlled externally. |
| currentPage | `number` | `1` | Current page for controlled/external pagination. |
| pageSize | `number` | `10` | Number of rows per page. |
| totalPages | `number` | | Optional total pages value. |
| location | `"right" \| "left" \| "center"` | | Pagination alignment. |
| onChange | `(page: number) => void` | | Triggered whenever page changes. |

### `sorting` config (`TableSortingConfig`)

| Field | Type | Default | Description |
| --------------- | --------------------------------------------------- | ------- | ----------- |
| isExternal | `boolean` | `false` | Indicates if sorting is controlled externally. |
| sortableHeaders | `string[]` | `[]` | Keys that are allowed to be sorted. |
| onSort | `(sortKey: string, direction: "asc" \| "desc" \| null) => void` | | Callback for sorting changes. |
| Field | Type | Default | Description |
| --------------- | --------------------------------------------------------------- | ------- | ---------------------------------------------- |
| isExternal | `boolean` | `false` | Indicates if sorting is controlled externally. |
| sortableHeaders | `string[]` | `[]` | Keys that are allowed to be sorted. |
| onSort | `(sortKey: string, direction: "asc" \| "desc" \| null) => void` | | Callback for sorting changes. |

### `search` config (`TableSearchConfig`)

| Field | Type | Default | Description |
| ----------------- | ------------------------------------------------- | ------- | ----------- |
| show | `boolean` | `false` | Shows/hides the search input. |
| isExternal | `boolean` | `false` | Indicates if search filtering is controlled externally. |
| searchableHeaders | `string[]` | `[]` | Keys that can be searched. |
| searchAllFieldsLabel | `string` | | Custom label for the "All" option in search dropdown. |
| onSearch | `(searchTerm: string, searchKey: string) => void` | | Callback with current search term and selected key (`"All"` or a header key). |
| Field | Type | Default | Description |
| -------------------- | ------------------------------------------------- | ------- | ----------------------------------------------------------------------------- |
| show | `boolean` | `false` | Shows/hides the search input. |
| isExternal | `boolean` | `false` | Indicates if search filtering is controlled externally. |
| searchableHeaders | `string[]` | `[]` | Keys that can be searched. |
| searchAllFieldsLabel | `string` | | Custom label for the "All" option in search dropdown. |
| onSearch | `(searchTerm: string, searchKey: string) => void` | | Callback with current search term and selected key (`"All"` or a header key). |

### `filter` config (`TableFilterConfig`)

| Field | Type | Default | Description |
| ----------------- | ---------------------------------------------- | ---------- | ----------- |
| show | `boolean` | `false` | Shows/hides the filter trigger button. |
| location | `"right" \| "left" \| "center"` | `"center"` | Defines where the filter modal opens (left/right sidebar or centered modal). |
| filterableHeaders | `FilterableHeader[]` | `[]` | Headers and input types available in the filter modal. |
| onFilter | `(filters: ActiveTableFilters) => void` | | Callback fired whenever applied filters change. |
| applyFilterLabel | `string` | `"Apply"` | Custom label for the filter apply button. |
| cancelFilterLabel | `string` | `"Cancel"` | Custom label for the filter cancel button. |
| title | `string` | `"Filters"`| Custom title shown in the filter modal. |
| Field | Type | Default | Description |
| ----------------- | --------------------------------------- | ----------- | ---------------------------------------------------------------------------- |
| show | `boolean` | `false` | Shows/hides the filter trigger button. |
| location | `"right" \| "left" \| "center"` | `"center"` | Defines where the filter modal opens (left/right sidebar or centered modal). |
| filterableHeaders | `FilterableHeader[]` | `[]` | Headers and input types available in the filter modal. |
| onFilter | `(filters: ActiveTableFilters) => void` | | Callback fired whenever applied filters change. |
| applyFilterLabel | `string` | `"Apply"` | Custom label for the filter apply button. |
| cancelFilterLabel | `string` | `"Cancel"` | Custom label for the filter cancel button. |
| title | `string` | `"Filters"` | Custom title shown in the filter modal. |

#### `FilterableHeader`

| Field | Type | Is Mandatory | Description |
| ------------ | ----------------------------------------------------- | ------------ | ----------- |
| id | `string` | true | Header key used to bind filter input to row values. |
| type | `"input" \| "checkbox" \| "radio" \| "date" \| "datetime"` | true | Input type rendered for this header in the filter modal. |
| filterValues | `string[]` | false | Explicit options for `checkbox` and `radio`; if omitted, values are inferred from table data. |
| Field | Type | Is Mandatory | Description |
| ------------ | ---------------------------------------------------------- | ------------ | --------------------------------------------------------------------------------------------- |
| id | `string` | true | Header key used to bind filter input to row values. |
| type | `"input" \| "checkbox" \| "radio" \| "date" \| "datetime"` | true | Input type rendered for this header in the filter modal. |
| filterValues | `string[]` | false | Explicit options for `checkbox` and `radio`; if omitted, values are inferred from table data. |

### `styling` config (`TableStylingConfig`)

| Field | Type | Default | Description |
| ------------------- | ----------------------------------- | --------- | ----------- |
| containerClassNames | `string` | | Custom class names for table wrapper. |
| titleClassNames | `string` | | Custom class names for table title. |
| colorPalette | `"classic" \| "modernDark" \| "softEarth"` | `"classic"` | Built-in table color palette. |
| Field | Type | Default | Description |
| ------------------- | ------------------------------------------ | ----------- | ------------------------------------- |
| containerClassNames | `string` | | Custom class names for table wrapper. |
| titleClassNames | `string` | | Custom class names for table title. |
| colorPalette | `"classic" \| "modernDark" \| "softEarth"` | `"classic"` | Built-in table color palette. |

## Exports

Expand Down Expand Up @@ -172,7 +172,6 @@ import TableComponent, {
### Future Enhancements:

- Implement an export action with exportCallback.
- Implement a general search for a specific field, adjust the searchCallback to receive the field being searched.

## Contributing

Expand Down
63 changes: 63 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const { FlatCompat } = require("@eslint/eslintrc");
const js = require("@eslint/js");

const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});

module.exports = [
...compat.config({
env: {
browser: true,
es2022: true,
node: true,
},
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
},
settings: {
react: {
version: "detect",
},
},
plugins: ["@typescript-eslint", "react", "react-hooks"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
],
rules: {
"react/react-in-jsx-scope": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
],
"react-hooks/exhaustive-deps": "off",
"react-hooks/preserve-manual-memoization": "off",
"react-hooks/set-state-in-effect": "off",
},
overrides: [
{
files: ["**/*.test.ts", "**/*.test.tsx", "src/test/setupTests.ts"],
env: {
jest: true,
},
},
],
ignorePatterns: [
"dist/",
"node_modules/",
".eslintrc.js",
"eslint.config.js",
"rollup.config.js",
],
}),
];
Loading
Loading