Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f9b33f8
Add feature for infinite scroll with data page loading in combo box. …
thekevinbrown Aug 27, 2025
0c1e89b
Fix type policies so that any change to pagination results in new dat…
thekevinbrown Aug 27, 2025
8957914
Got searchable filters working.
thekevinbrown Aug 28, 2025
afeca73
Add no options display, make input scroll horizontally if the content…
thekevinbrown Aug 28, 2025
eafa82f
Now that the bar is scrollable we can give the relationship filters m…
thekevinbrown Aug 28, 2025
b6dd96d
Apply same features to relationship field in detail panel
thekevinbrown Aug 28, 2025
d42dabb
Added infinite scroll and filtering for relationship fields as well a…
thekevinbrown Aug 28, 2025
c7c37a0
Remove unnecessary logging.
thekevinbrown Aug 28, 2025
cf71afa
Remove additional unnecessary logging.
thekevinbrown Aug 28, 2025
d636fcd
Fix expected data in test.
thekevinbrown Aug 29, 2025
6b9befd
Fix pagination issue with the combobox, and fix fallback label finder…
thekevinbrown Aug 29, 2025
8e1c471
Use a fragment to show the display value in the dropdown text filter.
thekevinbrown Aug 29, 2025
8f0253d
Simplify dropdown text filter with label and value being the same thing.
thekevinbrown Aug 29, 2025
7567d17
SonarQube Feedback.
thekevinbrown Aug 29, 2025
8f5e113
Sonarqube Feedback
thekevinbrown Aug 29, 2025
d1a6821
Chevron button is now a button.
thekevinbrown Aug 29, 2025
2d4050d
Ensure relatedEntity is defined before proceeding to grab summaryFiel…
thekevinbrown Aug 29, 2025
0ed3d58
Seems to work.
thekevinbrown Aug 29, 2025
b6685a2
Add additional logging when errors occur.
thekevinbrown Aug 29, 2025
0ece293
Fix for breaking tests.
thekevinbrown Aug 29, 2025
1db894c
Remove unused import.
thekevinbrown Aug 29, 2025
bb84114
Generated projects should use the exact version of MikroORM that we d…
thekevinbrown Aug 29, 2025
f960206
Re-added "40" track which is added by other tests.
thekevinbrown Aug 29, 2025
1e383ef
"40" only got added to the first album.
thekevinbrown Aug 29, 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
45 changes: 45 additions & 0 deletions src/packages/admin-ui-components/src/combo-box/combo-box.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,39 @@ Allow users to type to filter options.

<Canvas of={ComboBoxStories.WithFreeTyping} />

### Lazy Loading with Infinite Scroll

For large datasets, the ComboBox supports lazy loading with infinite scroll. This allows you to fetch data in pages as the user scrolls, improving performance and user experience.

<Canvas of={ComboBoxStories.LazyLoadingWithInfiniteScroll} />

#### Key Features:

- **Data Fetching**: Provide a `dataFetcher` function that returns paginated data
- **Infinite Scroll**: Automatically load more data when the user scrolls near the bottom
- **Search Integration**: Search terms are automatically passed to the data fetcher
- **Debounced Search**: Configurable debounce delay to prevent excessive API calls
- **Loading States**: Visual indicators for when more data is being fetched

#### Implementation:

```tsx
const dataFetcher: DataFetcher = async ({ page, searchTerm }) => {
const response = await fetch(`/api/items?page=${page}&pageSize=25&search=${searchTerm}`);
const result = await response.json();

return result.items || [];
};

<ComboBox
dataFetcher={dataFetcher}
searchDebounceMs={300}
allowFreeTyping={true}
mode={SelectMode.SINGLE}
placeholder="Search and scroll to load more..."
/>;
```

### In Form Context

ComboBoxes are typically used within forms to collect user input.
Expand All @@ -74,6 +107,15 @@ ComboBoxes are typically used within forms to collect user input.

<Controls of={ComboBoxStories.SingleSelect} />

### Lazy Loading Props

When using the lazy loading feature, you can configure these additional properties:

| Prop | Type | Default | Description |
| ------------------ | ------------- | ----------- | ---------------------------------------------- |
| `dataFetcher` | `DataFetcher` | `undefined` | Function to fetch paginated data |
| `searchDebounceMs` | `number` | `300` | Debounce delay for search input (milliseconds) |

## Usage Guidelines

- Use single select when only one option should be selected at a time
Expand All @@ -83,6 +125,9 @@ ComboBoxes are typically used within forms to collect user input.
- Use placeholder text to give users a hint about what to select
- Include a loading state for asynchronously loaded options
- For very short lists (2-5 items), consider using radio buttons or checkboxes instead
- **Lazy Loading**: Use lazy loading with infinite scroll for datasets with more than 100 items to improve performance
- **Search Integration**: When implementing lazy loading, ensure your data fetcher properly handles search terms for optimal user experience
- **Page Size**: Choose an appropriate page size (typically 20-50 items) in your data fetcher based on your data structure and API capabilities

## Accessibility

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react-vite';
import { ComboBox, SelectMode, SelectOption } from './component';
import { ComboBox, SelectMode, SelectOption, DataFetcher } from './component';

// Sample options for the stories
const fruitOptions: SelectOption[] = [
Expand All @@ -22,16 +22,36 @@ const colorOptions: SelectOption[] = [
{ value: 'purple', label: 'Purple' },
];

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
// Mock data fetcher for lazy loading demo
const mockDataFetcher: DataFetcher = async ({ page, searchTerm }) => {
// Simulate API delay
await new Promise((resolve) => setTimeout(resolve, 500));

// Generate mock data
const allItems: SelectOption[] = Array.from({ length: 1000 }, (_, i) => ({
value: `item-${i}`,
label: `Item ${i + 1}${searchTerm ? ` (${searchTerm})` : ''}`,
}));

// Filter by search term if provided
const filteredItems = searchTerm
? allItems.filter((item) => item.label?.toLowerCase().includes(searchTerm.toLowerCase()))
: allItems;

const pageSize = 20;
const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + pageSize;
const data = filteredItems.slice(startIndex, endIndex);

// Return just the data array - component will automatically fetch more if data.length > 0
return data;
};

const meta = {
title: 'Inputs/ComboBox',
component: ComboBox,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
layout: 'centered',
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
// More on argTypes: https://storybook.js.org/docs/api/argtypes
parameters: { layout: 'centered' },

argTypes: {
options: {
description: 'The list of options available in the dropdown',
Expand Down Expand Up @@ -193,3 +213,21 @@ export const InForm: Story = {
),
],
};

export const LazyLoadingWithInfiniteScroll: Story = {
args: {
mode: SelectMode.SINGLE,
placeholder: 'Type to search and scroll to load more...',
allowFreeTyping: true,
dataFetcher: mockDataFetcher,
searchDebounceMs: 300,
},
parameters: {
docs: {
description: {
story:
'This ComboBox demonstrates lazy loading with infinite scroll. It fetches data in pages as you scroll, and supports search with debouncing. The data fetcher simulates an API call with a 500ms delay.',
},
},
},
};
Loading
Loading