diff --git a/README.md b/README.md index 584baba..4e40856 100644 --- a/README.md +++ b/README.md @@ -340,27 +340,28 @@ function PostList() { ## Props, `InfiniteScroll` -| Prop | Type | Required | Default | Description | -| ---------------------------- | ------------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `dataLength` | `number` | yes | - | Current count of rendered items. The component resets its load guard each time this value changes, which allows `next()` to fire again on the next scroll. | -| `next` | `() => void` | yes | - | Called once when the sentinel enters the viewport. Append new items to your list state inside this callback; do not replace the existing items. | -| `hasMore` | `boolean` | yes | - | When `false`, the observer is disconnected and `next()` will not be called again. Set it to `false` when your data source has no more pages. | -| `loader` | `ReactNode` | yes | - | Rendered below the list while the next page is loading. Displayed between the last item and the bottom sentinel. | -| `endMessage` | `ReactNode` | no | - | Rendered below the list when `hasMore` is `false`. Use it for an "all caught up" or "no more items" message. | -| `height` | `number \| string` | no | - | Creates a fixed-height scroll container wrapping the list. Accepts a pixel number or any CSS length string. Omit this prop to scroll the window instead. | -| `scrollableTarget` | `HTMLElement \| string \| null` | no | - | The scrollable ancestor that already provides overflow scrollbars. Pass the element's `id` string or a direct `HTMLElement` reference. Required when the scroll container is neither the window nor the `height` wrapper. | -| `scrollThreshold` | `number \| string` | no | `0.8` | How close to the bottom the user must scroll before `next()` is called. A fraction like `0.8` means 80% scrolled; a string like `"200px"` means within 200 px of the bottom edge. | -| `inverse` | `boolean` | no | `false` | Reverse scroll direction for chat or messaging UIs. The sentinel moves to the top of the list. Use together with `flexDirection: column-reverse` on the scroll container. | -| `pullDownToRefresh` | `boolean` | no | `false` | Enable pull-to-refresh gesture on touch and mouse. Requires `refreshFunction` to also be set. | -| `refreshFunction` | `() => void` | no | - | Called once when the user pulls down past `pullDownToRefreshThreshold` pixels and releases. Only active when `pullDownToRefresh` is `true`. | -| `pullDownToRefreshThreshold` | `number` | no | `100` | How many pixels the user must pull down before `refreshFunction` is triggered on release. | -| `pullDownToRefreshContent` | `ReactNode` | no | - | Content shown inside the pull-to-refresh area while the user is pulling but has not yet reached the threshold. | -| `releaseToRefreshContent` | `ReactNode` | no | - | Content shown inside the pull-to-refresh area once the threshold is passed and the user can release to trigger a refresh. | -| `onScroll` | `(e: UIEvent) => void` | no | - | Callback fired on every scroll event on the container. Receives the native `UIEvent`. Useful for syncing UI state with scroll position. | -| `className` | `string` | no | `''` | CSS class name applied to the inner scroll container div. | -| `style` | `CSSProperties` | no | - | Inline style object applied to the inner scroll container div. Merged with the component's default layout styles. | -| `hasChildren` | `boolean` | no | - | Set to `true` when `children` is a single element or a fragment rather than an array. Helps the component detect whether visible content exists to determine scroll state. | -| `initialScrollY` | `number` | no | - | Scrolls the window to this Y offset on mount. Useful for restoring a user's scroll position when navigating back to a page. | +| Prop | Type | Required | Default | Description | +| ---------------------------- | ---------------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `dataLength` | `number` | yes | - | Current count of rendered items. The component resets its load guard each time this value changes, which allows `next()` to fire again on the next scroll. | +| `next` | `() => void` | yes | - | Called once when the sentinel enters the viewport. Append new items to your list state inside this callback; do not replace the existing items. | +| `hasMore` | `boolean` | yes | - | When `false`, the observer is disconnected and `next()` will not be called again. Set it to `false` when your data source has no more pages. | +| `loader` | `ReactNode` | yes | - | Rendered below the list while the next page is loading. Displayed between the last item and the bottom sentinel. | +| `endMessage` | `ReactNode` | no | - | Rendered below the list when `hasMore` is `false`. Use it for an "all caught up" or "no more items" message. | +| `height` | `number \| string` | no | - | Creates a fixed-height scroll container wrapping the list. Accepts a pixel number or any CSS length string. Omit this prop to scroll the window instead. | +| `scrollableTarget` | `HTMLElement \| string \| null` | no | - | The scrollable ancestor that already provides overflow scrollbars. Pass the element's `id` string or a direct `HTMLElement` reference. Required when the scroll container is neither the window nor the `height` wrapper. | +| `scrollThreshold` | `number \| string` | no | `0.8` | How close to the bottom the user must scroll before `next()` is called. A fraction like `0.8` means 80% scrolled; a string like `"200px"` means within 200 px of the bottom edge. | +| `inverse` | `boolean` | no | `false` | Reverse scroll direction for chat or messaging UIs. The sentinel moves to the top of the list. Use together with `flexDirection: column-reverse` on the scroll container. | +| `pullDownToRefresh` | `boolean` | no | `false` | Enable pull-to-refresh gesture on touch and mouse. Requires `refreshFunction` to also be set. | +| `refreshFunction` | `() => void` | no | - | Called once when the user pulls down past `pullDownToRefreshThreshold` pixels and releases. Only active when `pullDownToRefresh` is `true`. | +| `pullDownToRefreshThreshold` | `number` | no | `100` | How many pixels the user must pull down before `refreshFunction` is triggered on release. | +| `pullDownToRefreshContent` | `ReactNode` | no | - | Content shown inside the pull-to-refresh area while the user is pulling but has not yet reached the threshold. | +| `releaseToRefreshContent` | `ReactNode` | no | - | Content shown inside the pull-to-refresh area once the threshold is passed and the user can release to trigger a refresh. | +| `onScroll` | `(e: UIEvent) => void` | no | - | Callback fired on every scroll event on the container. Receives the native `UIEvent`. Useful for syncing UI state with scroll position. | +| `className` | `string` | no | `''` | CSS class name applied to the inner scroll container div. | +| `style` | `CSSProperties` | no | - | Inline style object applied to the inner scroll container div. Merged with the component's default layout styles. | +| Accessibility attributes | `aria-*`, `role`, `tabIndex`, `id` | no | - | Accessibility attributes applied to the inner scroll container div for labelling, semantics, and keyboard focus management. | +| `hasChildren` | `boolean` | no | - | Set to `true` when `children` is a single element or a fragment rather than an array. Helps the component detect whether visible content exists to determine scroll state. | +| `initialScrollY` | `number` | no | - | Scrolls the window to this Y offset on mount. Useful for restoring a user's scroll position when navigating back to a page. | ## Props, `useInfiniteScroll` diff --git a/src/__tests__/index.test.tsx b/src/__tests__/index.test.tsx index 99881cb..b07bf5d 100644 --- a/src/__tests__/index.test.tsx +++ b/src/__tests__/index.test.tsx @@ -45,6 +45,31 @@ describe('React Infinite Scroll Component', () => { expect(container.querySelectorAll('.custom-class').length).toBe(1); }); + it('passes accessibility attributes to the scroll container', () => { + const { container } = render( + {}} + id="loaded-items" + role="list" + tabIndex={0} + aria-label="Loaded items" + > +
+ + ); + + const scrollContainer = container.querySelector( + '.infinite-scroll-component' + ); + expect(scrollContainer?.getAttribute('id')).toBe('loaded-items'); + expect(scrollContainer?.getAttribute('role')).toBe('list'); + expect(scrollContainer?.getAttribute('tabindex')).toBe('0'); + expect(scrollContainer?.getAttribute('aria-label')).toBe('Loaded items'); + }); + it('renders children when passed in', () => { const { container } = render( any; -export interface Props { +export interface Props extends AriaAttributes { /** * Total number of items currently rendered. Unlocks the next load when it * changes. Always pass the length of your full accumulated list, not just @@ -117,6 +119,12 @@ export interface Props { initialScrollY?: number; /** CSS class name added to the inner scroll container element. */ className?: string; + /** Accessibility role applied to the inner scroll container element. */ + role?: AriaRole; + /** Tab order for keyboard focus management on the inner scroll container. */ + tabIndex?: number; + /** DOM id for labelling or controlling the inner scroll container. */ + id?: string; } export default function InfiniteScroll({ @@ -140,6 +148,10 @@ export default function InfiniteScroll({ dataLength, initialScrollY, className = '', + role, + tabIndex, + id, + ...ariaProps }: Props) { const [showLoader, setShowLoader] = useState(false); const [pullToRefreshThresholdBreached, setPullToRefreshThresholdBreached] = @@ -410,6 +422,10 @@ export default function InfiniteScroll({ return (