diff --git a/packages/extras/docs/BottomSheet.md b/packages/extras/docs/components/BottomSheet.md similarity index 100% rename from packages/extras/docs/BottomSheet.md rename to packages/extras/docs/components/BottomSheet.md diff --git a/packages/extras/docs/components/Carousel.md b/packages/extras/docs/components/Carousel.md new file mode 100644 index 00000000..d31cfc0f --- /dev/null +++ b/packages/extras/docs/components/Carousel.md @@ -0,0 +1,93 @@ +import { Required } from '@site/src/components'; + +# Carousel + +AMA Provides an accessible Carousel component built on top +of [React Native FlatList](https://reactnative.dev/docs/flatlist) +using the [useCarousel](./../hooks/useCarousel.md) hook. + +## Example + +```tsx {2-15,22-23} +import { Carousel } from '@react-native-ama/extras'; + +const Component = () => { + const data = [ + { key: '1', image: image_1 }, + { key: '2', image: image_2 }, + { key: '3', image: image_3 }, + ]; + const ref = React.useRef>(null); + + const renderItem: ListRenderItem<(typeof data)[number]> = ({ item }) => { + return ; + }; + + return ( + + ); +}; +``` + +## Accessibility + +- Provides / abstracts `accessibilityActions` array to Carousel with `increment` and `decrement` actions +- Provides / abstracts `onAccessibilityAction` handler to Carousel to manage the `AccessibilityActionEvent` change and scroll to the new index in the FlatList +- Provides / abstracts `accessibilityRole` to Carousel as `adjustable`/ `slider` + +## Props + +### `ref` + +The carousel reference provides access to underlying `FlatList` methods and is required for accessibility actions to work on iOS. + +| Type | Default | +| ------------------------------------ | --------- | +| React.RefObject\\> | undefined | + +### `accessibilityLabel` + +The `accessibilityLabel` is required and should describe what the carousel displays, this is announced by the screen reader when the element gains focus, then it announces its role ('adjustable'). + +| Type | Default | +| ------ | --------- | +| string | undefined | + +### `data` (Inherited from FlatList) + +An array (or array-like list) of items to render. Other data types can be used by targeting VirtualizedList directly. + +| Type | Default | +| --------------------------------------- | --------- | +| ArrayLike\ \| null \| undefined | undefined | + +### `renderItem` (Inherited from FlatList) + +Takes an item from data and renders it into the list. Typical usage: + +```tsx +const renderItem = ({item}) => ( + onPress(item)}> + {item.title} + +); + +... + + +``` + +Provides additional metadata like `index` if you need it. + +| Type | Default | +| -------------------------------------------- | --------- | +| ListRenderItem\ \| null \| undefined | undefined | + +## Related guidelines + +- [Carousel](/guidelines/carousel) diff --git a/packages/extras/docs/components/_category_.json b/packages/extras/docs/components/_category_.json new file mode 100644 index 00000000..92f583da --- /dev/null +++ b/packages/extras/docs/components/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Components", + "collapsible": true, + "collapsed": false +} diff --git a/packages/extras/docs/extras.md b/packages/extras/docs/extras.md index 0fa7072e..ffe60468 100644 --- a/packages/extras/docs/extras.md +++ b/packages/extras/docs/extras.md @@ -15,3 +15,20 @@ Install the `@react-native-ama/extras` package with your favourite package manag ```bash npm2yarn npm install @react-native-ama/extras ``` + +### Dependencies + +Some components rely on [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated) and/or [react-native-gesture-handler](https://github.com/software-mansion/react-native-gesture-handler), +so these dependencies are required for the extras package to function and should also be installed. + +```bash npm2yarn +npm install react-native-reanimated +``` + +Follow the specific installation instructions for React Native Reanimated [here](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#installation). + +```bash npm2yarn +npm install react-native-gesture-handler +``` + +Follow the specific installation instructions for React Native Gesture Handler [here](https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation), specifically wrapping the entry point of the app in a `GestureHandlerRootView`. diff --git a/packages/extras/docs/hooks/_category_.json b/packages/extras/docs/hooks/_category_.json new file mode 100644 index 00000000..15dc8a9e --- /dev/null +++ b/packages/extras/docs/hooks/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Hooks", + "collapsible": true, + "collapsed": false +} diff --git a/packages/extras/docs/hooks/useCarousel.md b/packages/extras/docs/hooks/useCarousel.md new file mode 100644 index 00000000..7d95db17 --- /dev/null +++ b/packages/extras/docs/hooks/useCarousel.md @@ -0,0 +1,49 @@ +import { Required } from '@site/src/components' + +# useCarousel + +`useCarousel` is a hook that provides `a11yProps` for creating carousels from FlatLists or ScrollViews. + +:::info + +`a11yProps` here is an object that contains all the necessary accessibility props to make an accessible carousel, those being the role, actions and the onAction handler. See [Carousel guidelines](/guidelines/carousel) for more information. + +::: + +## Usage + +```tsx {2-4,8-10} +import { useCarousel } from '@react-native-ama/extras'; + +const ExampleCarousel = props => { + const ref = React.useRef(null); + const a11yProps = useCarousel({ + data: props.data, + flatListRef: ref, + }); + + return ; +}; +``` + +## Arguments + +### `data` + +The data passed to the Scrollable component, used to calculate the number of items in the carousel. + +| Type | Default | +| ------------------------------------- | --------- | +| ArrayLike\ \| null \| undefined | undefined | + +### `flatListRef` + +The ref passed to the FlatList or ScrollView, used to scroll to index's when accessibility actions are performed. + +| Type | Default | +| ------------------------------------ | --------- | +| React.Ref\ \| null\> | undefined | + +## Related guidelines + +- [Carousel](/guidelines/carousel) diff --git a/packages/extras/docs/hooks/useKeyboard.md b/packages/extras/docs/hooks/useKeyboard.md new file mode 100644 index 00000000..5083741a --- /dev/null +++ b/packages/extras/docs/hooks/useKeyboard.md @@ -0,0 +1,104 @@ +import { Required } from '@site/src/components' + +# useKeyboard + +`useKeyboard` is a hook that provides Reanimated shared values for the current and final heights of the keyboard, along with a boolean shared value indicating whether the keyboard is visible. These properties are `keyboardHeight`, `keyboardFinalHeight`, and `isKeyboardVisible` and can be utilized when building animations. + +:::info + +This hook is provided for convenience, however it is not necessary for accessibility or part of the official AMA guidelines. + +::: + +## Usage + +```tsx {2-5,12-24} +import { useKeyboard } from '@react-native-ama/extras'; +import Animated, { + useAnimatedStyle, + useDerivedValue, +} from 'react-native-reanimated'; + +const Example = props => { + const { keyboardHeight, keyboardFinalHeight, isKeyboardVisible } = + useKeyboard(props.shouldHandleKeyboardEvents); + + // Do something with values + const maxHeightValue = useDerivedValue(() => { + return maxHeight - keyboardHeight.value; + }, [keyboardHeight, maxHeight]); + + const animatedStyle = useAnimatedStyle(() => { + const keyboard = keyboardHeight.value; + + return { + transform: [{ translateY: translateY.value - keyboard }], + maxHeight: maxHeightValue.value, + }; + }, [maxHeightValue, translateY, keyboardHeight]); + + return ; +}; +``` + +## Arguments + +### `shouldHandleKeyboardEvents` _(optional)_ + +The data passed to the Scrollable component, used to calculate the number of items in the carousel. + +| Type | Default | +| ------- | ------- | +| boolean | true | + +## Returns + +### `keyboardHeight` + +A Reanimated shared value representing the current height of the keyboard. + +| Type | Initial | +| --------------------- | ------- | +| SharedValue\ | 0 | + +> You can access data stored in the shared value with either its value property or get and set methods. + +### `keyboardFinalHeight` + +A Reanimated shared value representing the final height of the keyboard. When the keyboard is not visible, this value will be 0. + +| Type | Initial | +| --------------------- | ------- | +| SharedValue\ | 0 | + +> You can access data stored in the shared value with either its value property or get and set methods. + +### `isKeyboardVisible` + +A Reanimated shared value representing whether the keyboard is visible. + +| Type | Initial | +| ---------------------- | ------- | +| SharedValue\ | false | + +> You can access data stored in the shared value with either its value property or get and set methods. + +### Related + +:::note SharedValue type + +```ts +interface SharedValue { + value: Value; + get(): Value; + set(value: Value | ((value: Value) => Value)): void; + addListener: (listenerID: number, listener: (value: Value) => void) => void; + removeListener: (listenerID: number) => void; + modify: ( + modifier?: (value: T) => T, + forceUpdate?: boolean, + ) => void; +} +``` + +::: diff --git a/packages/extras/src/hooks/useCarousel.ts b/packages/extras/src/hooks/useCarousel.ts index 2cac5bb9..e8cf59eb 100644 --- a/packages/extras/src/hooks/useCarousel.ts +++ b/packages/extras/src/hooks/useCarousel.ts @@ -1,5 +1,4 @@ import { useRef } from 'react'; -// import { FlatList } from 'react-native'; import { FlatList } from 'react-native-gesture-handler'; import { AccessibilityActionEvent, @@ -15,7 +14,10 @@ export const useCarousel = ({ data, flatListRef }: UseCarousel) => { const carouselIndexForScreenReader = useRef(0); const totalItems = data?.length || 0; - const accessibilityActions = [{ name: 'increment' }, { name: 'decrement' }]; + const accessibilityActions = [ + { name: 'increment' }, + { name: 'decrement' }, + ] as const; const onAccessibilityAction = (event: AccessibilityActionEvent) => { const value = event.nativeEvent.actionName === 'increment' ? 1 : -1; diff --git a/packages/react-native/docs/ExpandablePressable.mdx b/packages/react-native/docs/ExpandablePressable.mdx index 99183363..dbc779da 100644 --- a/packages/react-native/docs/ExpandablePressable.mdx +++ b/packages/react-native/docs/ExpandablePressable.mdx @@ -80,7 +80,7 @@ The component uses the [onLayout](https://reactnative.dev/docs/layoutevent) prop ### `accessibilityLabel` -The `accessibilityLabel` for the expandable button, this is announced by the screen reader when the element gains focus, then it announces its role. The accessibilityLabel is a required properties as if it is omitted, the user will have no context for the purpose of the button. +The `accessibilityLabel` for the expandable button, this is announced by the screen reader when the element gains focus, then it announces its role. The accessibilityLabel is a required properties as if it is omitted, the user may have no context for the purpose of the button. | Type | Default | | ------ | --------- | diff --git a/website/guidelines/carousel.md b/website/guidelines/carousel.md index c60722f6..4621a479 100644 --- a/website/guidelines/carousel.md +++ b/website/guidelines/carousel.md @@ -67,5 +67,5 @@ To implement this navigation behavior in React Native when the Screen Reader is ### Related AMA utility -- [CarouseWrapper](/extras/docs/components/carouse-wrapper) -- [useCarousel](/extras/docs/hooks/use-carousel) +- [Carouse](/extras/components/Carousel) +- [useCarousel](/extras/hooks/useCarousel)