From 01e4cb2ef818a433f2b75c41c395f5adc0d7056b Mon Sep 17 00:00:00 2001 From: syd03098 Date: Mon, 21 Oct 2024 20:42:35 +0900 Subject: [PATCH] BreadCrumbs now pass through with useRenderProps --- .../react-aria-components/src/Breadcrumbs.tsx | 80 ++++++++++++++----- .../test/Breadcrumbs.test.js | 55 ++++++++++++- 2 files changed, 114 insertions(+), 21 deletions(-) diff --git a/packages/react-aria-components/src/Breadcrumbs.tsx b/packages/react-aria-components/src/Breadcrumbs.tsx index 0905ebbdbe6..b03cd76678c 100644 --- a/packages/react-aria-components/src/Breadcrumbs.tsx +++ b/packages/react-aria-components/src/Breadcrumbs.tsx @@ -9,17 +9,35 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -import {AriaBreadcrumbsProps, useBreadcrumbs} from 'react-aria'; -import {Collection, CollectionBuilder, createLeafComponent} from '@react-aria/collections'; +import {AriaBreadcrumbsProps, mergeProps, useBreadcrumbs, useFocusRing} from 'react-aria'; +import {BaseCollection, Collection, CollectionBuilder, createLeafComponent} from '@react-aria/collections'; import {CollectionProps, CollectionRendererContext} from './Collection'; -import {ContextValue, RenderProps, SlotProps, StyleProps, useContextProps, useRenderProps, useSlottedContext} from './utils'; +import { + ContextValue, + RenderProps, + SlotProps, + StyleRenderProps, + useContextProps, + useRenderProps, + useSlottedContext +} from './utils'; import {filterDOMProps} from '@react-aria/utils'; import {forwardRefType, Key} from '@react-types/shared'; import {LinkContext} from './Link'; import {Node} from 'react-stately'; -import React, {createContext, ForwardedRef, forwardRef, useContext} from 'react'; +import React, {createContext, ForwardedRef, forwardRef, RefObject, useContext} from 'react'; -export interface BreadcrumbsProps extends Omit, 'disabledKeys'>, AriaBreadcrumbsProps, StyleProps, SlotProps { +/** + * 1. Should I expose collection interface? + * 2. Maybe there's a possibility of unwanted props? + */ +export interface BreadCrumbsRenderProps { + isDisabled: boolean, + isFocusWithin: boolean, + isFocusVisible: boolean +} + +export interface BreadcrumbsProps extends Omit, 'disabledKeys'>, AriaBreadcrumbsProps, StyleRenderProps, SlotProps { /** Whether the breadcrumbs are disabled. */ isDisabled?: boolean, /** Handler that is called when a breadcrumb is clicked. */ @@ -30,24 +48,48 @@ export const BreadcrumbsContext = createContext(props: BreadcrumbsProps, ref: ForwardedRef) { [props, ref] = useContextProps(props, ref, BreadcrumbsContext); + return ( + }> + {(collection) => } + + ); +}; + +interface BreadCrumbsInnerProps { + props: BreadcrumbsProps, + collection: BaseCollection, + breadCrumbsRef: RefObject +} + +function BreadCrumbsInner({props, collection, breadCrumbsRef}: BreadCrumbsInnerProps) { let {CollectionRoot} = useContext(CollectionRendererContext); let {navProps} = useBreadcrumbs(props); + const {isFocused, isFocusVisible, focusProps} = useFocusRing({within: true}); + + let renderProps = useRenderProps({ + defaultClassName: 'react-aria-Breadcrumbs', + className: props.className, + style: props.style, + values: { + isDisabled: props.isDisabled || false, + isFocusWithin: isFocused, + isFocusVisible + } + }); return ( - }> - {collection => ( -
    - - - -
- )} -
+
    + + + +
); } diff --git a/packages/react-aria-components/test/Breadcrumbs.test.js b/packages/react-aria-components/test/Breadcrumbs.test.js index ae631ce87d2..44ce753c8cd 100644 --- a/packages/react-aria-components/test/Breadcrumbs.test.js +++ b/packages/react-aria-components/test/Breadcrumbs.test.js @@ -11,8 +11,9 @@ */ import {Breadcrumb, Breadcrumbs, BreadcrumbsContext, Link} from 'react-aria-components'; +import {pointerMap, render} from '@react-spectrum/test-utils-internal'; import React from 'react'; -import {render} from '@react-spectrum/test-utils-internal'; +import userEvent from '@testing-library/user-event'; let renderBreadcrumbs = (breadcrumbsProps, itemProps) => render( @@ -23,6 +24,11 @@ let renderBreadcrumbs = (breadcrumbsProps, itemProps) => render( ); describe('Breadcrumbs', () => { + let user; + beforeAll(() => { + user = userEvent.setup({delay: null, pointerMap}); + }); + it('should render with default class', () => { let {getByRole, getAllByRole} = renderBreadcrumbs(); let breadcrumbs = getByRole('list'); @@ -104,7 +110,7 @@ describe('Breadcrumbs', () => { expect(breadcrumbRef.current).toBe(item); }); - it('should support render props', () => { + it('should support isCurrent props', () => { let items = [ {id: 1, name: 'Item 1'}, {id: 2, name: 'Item 2'}, @@ -119,4 +125,49 @@ describe('Breadcrumbs', () => { expect(getAllByRole('listitem').map((it) => it.textContent)).toEqual(['Item 1', 'Item 2', 'Current']); }); + + it('should support isDisabled props', () => { + let {getByRole} = render( + `${defaultClassName}-${isDisabled}`}> + Item1 + + ); + + const breadCrumbs = getByRole('list'); + expect(breadCrumbs).toHaveAttribute('data-disabled', 'true'); + expect(breadCrumbs).toHaveClass('react-aria-Breadcrumbs-true'); + }); + + it('should support focus-ring', async () => { + let {getByRole} = render( + `${defaultClassName}-${isFocusVisible}`}> + Item1 + React Aria + + ); + let breadCrumbs = getByRole('list'); + + expect(breadCrumbs).not.toHaveAttribute('data-focus-visible'); + expect(breadCrumbs).toHaveClass('react-aria-Breadcrumbs-false'); + + await user.tab(); + expect(breadCrumbs).toHaveAttribute('data-focus-visible', 'true'); + expect(breadCrumbs).toHaveClass('react-aria-Breadcrumbs-true'); + + await user.tab(); + expect(breadCrumbs).not.toHaveAttribute('data-focus-visible'); + expect(breadCrumbs).toHaveClass('react-aria-Breadcrumbs-false'); + }); + + it('should have default data attributes', () => { + let {getByRole, getAllByRole} = renderBreadcrumbs(); + + let breadcrumbs = getByRole('list'); + expect(breadcrumbs).toHaveAttribute('data-rac'); + expect(breadcrumbs).not.toHaveAttribute('data-focus-within'); + expect(breadcrumbs).not.toHaveAttribute('data-disabled'); + + let items = getAllByRole('listitem'); + items.forEach((item) => expect(item).toHaveAttribute('data-rac')); + }); });