--- id: brandmark category: Brand title: Brandmark description: Logos/Brandmarks for Optum brands. design: 'https://www.figma.com/file/tk08Md4NBBVUPNHQYthmqp/Abyss-Web?type=design&node-id=0-21&mode=design&t=uWtmzWwvT2Kv5MMG-0' --- ```jsx import { Brandmark } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Brandmark', inputs: [ { prop: 'size', type: 'string', default: '$brandmark.sizing.lg' }, { prop: 'affiliate', type: 'select', options: [ { label: 'optum', value: 'optum' }, { label: 'optum_financial', value: 'optum_financial' }, { label: 'optum_frontier_therapies', value: 'optum_frontier_therapies' }, { label: 'optum_health-education', value: 'optum_health-education' }, { label: 'optum_now', value: 'optum_now' }, { label: 'optum_perks', value: 'optum_perks' }, { label: 'optum_prescription', value: 'optum_prescription' }, { label: 'optum_serve', value: 'optum_serve' }, { label: 'optum_store', value: 'optum_store' }, ], }, { prop: 'variant', type: 'select', options: [ { label: 'lockup', value: 'lockup' }, ] }, { prop: 'color', type: 'select', options: [ { label: 'white', value: 'white' }, { label: 'black', value: 'black' }, { label: 'orange', value: 'orange' }, ] }, ] } // Disclaimer: not all affiliate variant/color combinations are applicable, and inapplicable combinations will display as empty ``` ## Brand Use the `brand` property to adjust which brand is being selected. ```jsx live ``` ## Size Use the `size` property to adjust the size of the brandmark. ```jsx live ``` ## Affiliate Use the `affiliate` property to select the required brandmark affiliates. ```jsx live ``` ## Variant Use the `variant` property to select the required brandmark variants. ```jsx live ``` ## Color Use the `color` property to select available brandmark colors. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render

Brandmarks

```
The source for these brandmarks can be found in the Brandmark Library. You can use the search functionality to find the required brandmark. Brandmarks can be searched using their affiliates, variants or colors.
```jsx render ``` --- id: icon-brand category: Brand title: IconBrand description: Used to implement Brand icons and adapt their properties. design: https://www.figma.com/file/anZoHg026SyKJHWGJ7Vf4Q/Abyss-Icons?node-id=5%3A159&t=yumeSCoKoGQjs5PJ-0 --- ```jsx import { IconBrand } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'IconBrand', inputs: [ { prop: 'size', type: 'string', }, { prop: 'variant', type: 'select', options: [ { label: 'one tone', value: 'onetone' }, { label: 'two tone', value: 'twotone' }, { label: 'one tone w/ dark circle', value: 'onetonedarkcircle' }, { label: 'two tone w/ dark circle', value: 'twotonedarkcircle' }, { label: 'two tone w/ light circle', value: 'twotonelightcircle' }, ], }, { prop: 'icon', type: 'string', }, ] } ``` ## Icons Use the `icon` property to adjust which icon is being selected. ```jsx live ``` ## Size Use the `size` property to adjust the size of an icon by setting it to a specific number, or using a token. The default is `$md`. Token sizes: `$xs`: 40 `$sm`: 64 `$md`: 96 `$lg`: 112 `$xl`: 136 ```jsx live ``` ## Brand Icon Variants Use the `variant` property to change the style of Brand icons. Available variants are `twotonedarkcircle`, `twotonelightcircle`, `twotone`, `onetonedarkcircle`, and `onetone`. The default variant is `twotonedarkcircle`. **Note:** The `variant` prop is ignored if `useDeprecated` is set to false or if the `deprecatedOptumIcons` override is set in `createTheme`, as the new icons only have one variant. ```jsx live onetonedarkcircle twotonedarkcircle twotonelightcircle onetone twotone ``` ```jsx render ``` ```jsx render ``` ```jsx render

Brand Icons

```
Abyss uses branded iconography iconography that is designed to aid wayfinding, draw attention, and support messaging. The source for these design icons can be found in the Brand Icons Library.
```jsx render ``` --- id: icon-custom category: Brand title: IconCustom description: Used to implement custom icons and adapt their properties. design: https://www.figma.com/file/anZoHg026SyKJHWGJ7Vf4Q/Abyss-Icons?node-id=5%3A159&t=yumeSCoKoGQjs5PJ-0 --- ```jsx import { IconCustom } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'IconCustom', inputs: [ { prop: 'size', type: 'string', }, { prop: 'variant', type: 'select', options: [ { label: 'light', value: 'light' }, { label: 'lightactive', value: 'lightactive' }, ], }, { prop: 'icon', type: 'string', }, ] } ``` ## Size Use the `size` property to adjust the size of an icon by setting it to a specific number or token. The default size is set to 24. Token sizes: `$xs`: 16 `$sm`: 20 `$md`: 24 `$lg`: 40 `$xl`: 48 ```jsx live ``` ## Variants Use the `variant` prop to change the style of the custom icons. The prop takes in either a `light` or `lightactive` value. The default is `light`. ```jsx live light lightactive ``` ## Brand Use the `brand` property to adjust which brands icons are being selected. Note that some of the icons unique to certain brands. The default is the theme brand, then falls back to `uhc`. ```jsx live ``` ## Active When placing an icon inside of a clickable element, the icon should be toggled between its normal and active state. ```jsx live {({ pressed }) => ( Home )} ``` ## Indicator The [indicator component](/mobile/ui/indicator) can be used as a wrapper to add a notification badge. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render

Custom Icons

```
```jsx render ``` --- id: illustration-brand category: Media title: IllustrationBrand description: Used to implement Brand illustrations and adapt their properties. design: 'https://www.figma.com/file/34gRQMq2NxFgeRynjqd24C/Documentation-%7C-Abyss-Mobile?type=design&node-id=1218-19509&mode=design&t=eobYuZOIhhms55Vc-0' --- ```jsx import { IllustrationBrand } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'IllustrationBrand', inputs: [ { prop: 'size', type: 'string', }, { prop: 'illustration', type: 'string', }, { prop: 'variant', type: 'select', options: [ { label: 1, value: 1 }, { label: 2, value: 2 }, ], }, { prop: 'color', type: 'select', options: [ { label: 'primary', value: 'primary' }, { label: 'pacific', value: 'pacific' }, { label: 'white', value: 'white' }, ] }, ] } // Disclaimer: not all brand color combinations or variants are applicable, and inapplicable combinations will display as empty ``` ## Brand Use the `brand` property to adjust which brand is being selected. ```jsx live ``` ## Size Use the `size` property to adjust the size of the illustration. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render

Illustration Source

```
The source for these illustrations can be found in the brand libraries.

UnitedHealthCare Library
Optum Library

You can use the search functionality to find the required illustration. Illustrations can be searched using their title, variants or colors.

--- id: elevation category: Brand title: Elevation description: Creates depth on screen for users design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=12804%3A65751 --- ```jsx import { Elevation } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Elevation', inputs: [ { prop: 'level', type: 'select', options: [ { label: "0", value: 0 }, { label: "1", value: 1 }, { label: "2", value: 2 }, { label: "3", value: 3 }, { label: "4", value: 4 }, ] }, ] } ``` ## Level The `level` prop determines the magnitude of the shadow effect applied to the surface of the component. The prop accepts numbers between `0` and `4`. The default is `1` ```jsx live Level 0 Level 1 Level 2 Level 3 Level 4 ``` ```jsx render ``` ```jsx render ``` --- id: tokens title: Tokens category: Brand --- ## Overview Design tokens are the visual sub-atom variables of a design system. They contain UI data such as colors, border width, elevation, and even motion. They are used in the place of hard-coded values such as hex codes or pixels to maintain scalability and consistency. Think about them as recipe ingredients - you could add chocolate to a salad, but it won't be very tasty. You would only consider what is a standard salad ingredient - it's the same with tokens, they are a limited set of options that make sense for our product. **Further reading:**
[Nathan Curtis on Tokens in design systems](https://medium.com/eightshapes-llc/tokens-in-design-systems-25dd82d58421). ### Token Hierarchy Abyss uses a 3-tier token system: - Core tier - the WHAT or the OPTIONS: contains primitive values, with no specific meaning - the name of the token and its raw value (HEX code for colors, and numbers for borders, corner radius, opacity, etc.) - Semantic tier - the HOW or the DECISIONS: communicates design decisions on the exact usage of a Core token system-wide. - Brand tokens - shorthand tokens to define common colors used throughout Abyss. ### Using Tokens Before you can consume Abyss tokens, your project must be configured with our [themeProvider](/mobile/ui/theme-provider). This will allow you to access the tokens in your project. Tokens are used in place of hard-coded values such as hex codes or pixels. To use a token, you can reference it in your code using the `$` symbol followed by the token name. All Abyss components can accept tokens, when they are passed in using the `style`, or [styles](/mobile/developers/style-customization) prop. Non-Abyss components can use the [tokenize](/mobile/tools/tokenize) function to accept tokens. #### Examples Brand tokens are useful for quickly defining the color of components. ```jsx sandbox ``` #### Styled Function To create a `View` component with a background color of `$core.color.brand.100`, you can leverage our [styled function](/mobile/tools/styled) and do the following: ```jsx sandbox () => { const Example = styled('View', { backgroundColor: '$core.color.brand.100', height: 100, width: 100, }); return ; }; ``` #### useToken hook Alternatively, you can use our [useToken Hook](/mobile/hooks/use-token) to directly access the value of a token. This is useful when you need the value of multiple tokens. ```jsx sandbox () => { const getColorToken = useToken('colors'); const green = getColorToken('$core.color.green.100'); const red = getColorToken('$core.color.red.100'); return ( <> ); }; ``` #### Tokenize Function Our [tokenize](/mobile/tools/tokenize) function is useful for mapping any non-Abyss component's props to accept Abyss tokens. ```jsx live const TokenizedTouchableHighlight = tokenize(TouchableHighlight, { underlayColor: 'colors', }); render(() => { return ( {}} accessibilityRole="button" > Press Me ); }); ``` #### Useful Links - [Custom Theme Tutorial](/mobile/developers/tutorials/custom-themes/): This tutorial will guide you through creating a custom Abyss theme for your project. - [createTheme Function](/mobile/tools/create-theme): This tool allows you to create and modify themes to fit your design needs. - [Theme Provider](/mobile/ui/theme-provider): Provider that passes the theme object down the component tree giving your project access to Abyss tokens. - [styled Function](/mobile/tools/styled): Tool that allows you to create styled components. - [useToken Hook](/mobile/hooks/use-token): Hook that allows you to access the value of a token in your project. - [tokenize](/mobile/tools/tokenize): Function that allows you to map a component's props to accept Abyss tokens.
## Core Tokens Below is a list of core tokens used throughout Abyss, These are split into the categories `color`, `border-width`, `border-radius`, `opacity`, `spacing`, `sizing`. _**Note:** Click on the token row to copy the token to your clipboard._ #### Border Width Tokens `border-width` tokens are used to define the `borderWidth` on components. ```jsx const Example = styled('View', { borderWidth: '$core.border-width.md', }); ``` ```jsx render ``` #### Border Radius Tokens `border-radius` tokens are used to define the `borderRadius` on components. ```jsx const Example = styled('View', { borderRadius: '$core.border-radius.md', }); ``` ```jsx render ``` --- #### Opacity Tokens `opacity` tokens are used to define the opacity of a component. ```jsx const Example = styled('View', { opacity: '$core.opacity.md', }); ``` ```jsx render ``` --- #### Spacing Tokens `spacing` tokens define the space between components. Generally, these are used for the `padding`, `margin`, or `gap` of components. ```jsx const Example = styled('View', { padding: '$core.spacing.200', }); ``` ```jsx render ``` --- #### Sizing Tokens `sizing` tokens define the size of components. Generally, these will be used to define the `width` or `height`. ```jsx const Example = styled('View', { width: '$core.sizing.600', height: '$core.sizing.600', }); ``` ```jsx render ``` --- #### Color Tokens `color` tokens are used to define the color of components. ```jsx const Example = styled('View', { backgroundColor: '$core.color.brand.100', }); ``` ```jsx render ``` ## Brand Tokens Brand tokens are useful for quickly defining the color of components. _**Note:** Click on the token row to copy the token to your clipboard._ #### Colors Below is a list of brand tokens and their intended use cases. ```jsx const Example = styled('View', { backgroundColor: '$primary1', }); ``` ```jsx render ``` --- #### Spacing `spacing` tokens define the space between components. Generally, these will be used to define the `padding`, `margin`, or `gap` of components. ```jsx const Example = styled('View', { padding: '$md', }); ``` ```jsx render ``` --- #### Border Radius `border-radius` tokens define the `borderRadius` on components. ```jsx const Example = styled('View', { borderRadius: '$md', }); ``` ```jsx render ``` --- ### Accessibility Color choices that are accessible ensure everyone can not only see every element on a page but also understand a specific, intended meaning. You want everyone to be able to see the difference between two colors right next to or on top of each other. Contrast refers to the perceived difference between foreground and background colors. People with low vision or color blindness and those with difficulty seeing the differences between colors can have trouble seeing where one element ends and another begins. As we age, the shape of our eyes changes and affects how we perceive color and how well we can distinguish color variations. If the contrast between different elements is too low, people may not be able to see them at all. Contrast is expressed as a ratio with the first number representing the foreground color and the second representing the background color. For example, 3:1 means the foreground item color is three times more intense or visible than the background value. Contrast rules apply to text as well as any content that conveys meaning, including icons, graphics, and form elements. Tools such as Colour Contrast Analyzer or WebAIM's online color contrast tool are useful for verifying contrast ratios. Color and contrast choices within a digital experience are accessible when people can: - See UI elements and content - Understand and interpret information - Take action We aim to provide a contrast ratio perceivable by all users. For this reason, UnitedHealthcare has embraced a minimum contrast ratio of 4.5:1 (foreground vs background) for UI elements and content that conveys meaning. Recommendations - Include color combinations, good contrast and poor contrast, in design documentation - Communicate meaning with more than just color, such as with color and descriptive text - Give focus indicators a unique presentation that meets contrast requirements on all backgrounds Test for a minimum contrast ratio of 4.5 to 1 for: - Non-bolded text smaller than 24 pixels (18 points) - Bold text smaller than 18 pixels (14 points) - Essential icons that are close to the body text size Test that non-text elements that communicate information meet a minimum contrast ratio of 3 to 1 for all states: - Icons - Data visualizations - Focus indicators - Controls, including their borders or boundaries - Non-bolded text at or above 24 pixels (18 points) - Bold text at or above 18 pixels (14 points) Don't worry about contrast for logos and disabled elements. Watch out for using color alone to communicate meaning. People who are color blind or blind cannot perceive the meaning by color alone. --- id: brandmark category: Brand title: Brandmark description: Logos/Brandmarks for UHC brands. design: 'https://www.figma.com/file/tk08Md4NBBVUPNHQYthmqp/Abyss-Web?type=design&node-id=0-21&mode=design&t=uWtmzWwvT2Kv5MMG-0' --- ```jsx import { Brandmark } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Brandmark', inputs: [ { prop: 'size', type: 'string', }, { prop: 'affiliate', type: 'select', options: [ { label: 'aarp_extra_assurance_benefits', value: 'aarp_extra_assurance_benefits' }, { label: 'aarp_medicare_advantage_walgreens', value: 'aarp_medicare_advantage_walgreens' }, { label: 'aarp_medicare_advantage', value: 'aarp_medicare_advantage' }, { label: 'aarp_medicare_plans', value: 'aarp_medicare_plans' }, { label: 'aarp_medicare_prescription', value: 'aarp_medicare_prescription' }, { label: 'aarp_medicare_prescription_walgreens', value: 'aarp_medicare_prescription_walgreens' }, { label: 'aarp_medicare_supplement', value: 'aarp_medicare_supplement' }, { label: 'aarp_supplemental_personal_health', value: 'aarp_supplemental_personal_health' }, { label: 'community_plan', value: 'community_plan' }, { label: 'dental', value: 'dental' }, { label: 'dual_complete', value: 'dual_complete' }, { label: 'global', value: 'global' }, { label: 'hearing', value: 'hearing' }, { label: 'medicare_advantage', value: 'medicare_advantage' }, { label: 'group_medicare_advantage', value: 'group_medicare_advantage' }, { label: 'medicare_plans', value: 'medicare_plans' }, { label: 'medicare_solutions', value: 'medicare_solutions' }, { label: 'oxford', value: 'oxford' }, { label: 'student_resources', value: 'student_resources' }, { label: 'uhc', value: 'uhc' }, { label: 'vision', value: 'vision' }, ], }, { prop: 'variant', type: 'select', options: [ { label: 'lockup', value: 'lockup' }, { label: 'lockup_horizontal', value: 'lockup_horizontal' }, { label: 'u_mark', value: 'u_mark' }, { label: 'u_mark_horizontal', value: 'u_mark_horizontal' }, { label: 'monogram', value: 'monogram' }, { label: 'stacked_wordmark', value: 'stacked_wordmark' }, { label: 'wordmark', value: 'wordmark' }, ] }, { prop: 'color', type: 'select', options: [ { label: 'red', value: 'red' }, { label: 'white', value: 'white' }, { label: 'black', value: 'black' }, { label: 'blue', value: 'blue' }, { label: 'full', value: 'full' }, ] }, ] } // Disclaimer: not all affiliate variant/color combinations are applicable, and inapplicable combinations will display as empty ``` ## Brand Use the `brand` property to adjust which brand is being selected. ```jsx live ``` ## Size Use the `size` property to adjust the size of the brandmark. ```jsx live ``` ## Affiliate Use the `affiliate` property to select the required brandmark affiliates. ```jsx live ``` ## Variant Use the `variant` property to select the required brandmark variants. ```jsx live ``` ## Color Use the `color` property to select available brandmark colors. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render

Brandmarks

```
The source for these brandmarks can be found in the Brandmark Library. You can use the search functionality to find the required brandmark. Brandmarks can be searched using their affiliates, variants or colors.
```jsx render ``` --- id: icon-brand category: Brand title: IconBrand description: Used to implement Brand icons and adapt their properties. design: https://www.figma.com/file/anZoHg026SyKJHWGJ7Vf4Q/Abyss-Icons?node-id=5%3A159&t=yumeSCoKoGQjs5PJ-0 --- ```jsx import { IconBrand } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'IconBrand', inputs: [ { prop: 'size', type: 'string', }, { prop: 'variant', type: 'select', options: [ { label: 'one tone', value: 'onetone' }, { label: 'two tone', value: 'twotone' }, { label: 'one tone w/ dark circle', value: 'onetonedarkcircle' }, { label: 'two tone w/ dark circle', value: 'twotonedarkcircle' }, { label: 'two tone w/ light circle', value: 'twotonelightcircle' }, ], }, { prop: 'icon', type: 'string', }, ] } ``` ## Icons Use the `icon` property to adjust which icon is being selected. ```jsx live ``` ## Size Use the `size` property to adjust the size of an icon by setting it to a specific number, or using a token. The default is `$md`. Token sizes: `$xs`: 40 `$sm`: 64 `$md`: 96 `$lg`: 112 `$xl`: 136 ```jsx live ``` ## Brand Icon Variants Use the `variant` property to change the style of Brand icons. Available variants are `twotonedarkcircle`, `twotonelightcircle`, `twotone`, `onetonedarkcircle`, and `onetone`. The default variant is `twotonedarkcircle`. ```jsx live onetonedarkcircle twotonedarkcircle twotonelightcircle onetone twotone ``` ```jsx render ``` ```jsx render ``` ```jsx render

Brand Icons

```
Abyss uses branded iconography that is designed to aid way-finding, draw attention, and support messaging. The source for these design icons can be found in the Brand Icons Library.
## Dynamic Type Brand icons do not scale. When using IconBrand, the `disabledScaling` prop should be set to `true`. ```jsx render ``` --- id: icon-custom category: Brand title: IconCustom description: Used to implement custom icons and adapt their properties. design: https://www.figma.com/file/anZoHg026SyKJHWGJ7Vf4Q/Abyss-Icons?node-id=5%3A159&t=yumeSCoKoGQjs5PJ-0 --- ```jsx import { IconCustom } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'IconCustom', inputs: [ { prop: 'size', type: 'string', }, { prop: 'variant', type: 'select', options: [ { label: 'light', value: 'light' }, { label: 'lightactive', value: 'lightactive' }, ], }, { prop: 'icon', type: 'string', }, ] } ``` ## Size Use the `size` property to adjust the size of an icon by setting it to a specific number or token. The default size is set to 24. Token sizes: `$xs`: 16 `$sm`: 20 `$md`: 24 `$lg`: 40 `$xl`: 48 ```jsx live ``` ## Variants Use the `variant` prop to change the style of the custom icons. The prop takes in a `light` and `dark` value, as well as `lightactive` and `darkactive` counterparts. The default is `light`. ```jsx live light lightactive dark darkactive ``` ## Brand Use the `brand` property to adjust which brands icons are being selected. Note that some of the icons unique to certain brands. The default is the theme brand, then falls back to `uhc`. ```jsx live ``` ## Active When placing an icon inside of a clickable element, the icon should be toggled between its normal and active state. ```jsx live {({ pressed }) => ( Home )} ``` ## Indicator The [indicator component](/mobile/ui/indicator) can be used as a wrapper to add a notification badge. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render

Custom Icons

```
```jsx render ``` --- id: illustrated-icon-brand slug: /mobile/brand/uhc/illustrated-icon-brand category: Brand title: IllustratedIconBrand description: Used to implement UHC brand illustrated icons and adapt their properties. sourceIsTS: true --- ```jsx import { IllustratedIconBrand } from '@uhg-abyss/mobile'; ``` ```tsx sandbox { component: 'IllustratedIconBrand', inputs: [ { prop: 'icon', type: 'string', }, { prop: 'size', type: 'string', }, { prop: 'color', type: 'select', options: [ { label: 'none', value: undefined }, { label: 'gold', value: 'gold' }, { label: 'orange', value: 'orange' }, { label: 'multicolor', value: 'multicolor' }, ] }, ], } // Disclaimer: Not all icon/color combinations are applicable; inapplicable combinations will display as empty ``` ## Size Use the `size` property to adjust the width of the illustrated icon. The default value is `100`. The height of the illustration will scale proportionally to the width. ```tsx live ``` ## Color Use the `color` property to select available illustrated icon colors. The available colors are `"gold"`, `"orange"`, and `"multicolor"`. **Note:** Not all illustrated icons have any color variants. In such cases, omit the `color` prop; otherwise, the icon will not display. ```tsx live ``` ```jsx render ``` ```jsx render ```

Screen Reader Support

Illustrated icons are intended to be used as decorative images and as such, are ignored by screen readers by default. However, should a case arise in which an illustrated icon needs to be accessible, use the `accessibilityLabel` prop to provide accessible text to the image. This text should be descriptive enough to convey the meaning of the illustrated icon. ```tsx live ```
```jsx render

Illustrated Icon Source

```
The source for these illustrated icons can be found in the brand libraries.

UnitedHealthCare Library

You can use the search functionality to find the required illustrated icons. Icons can be searched using their title or colors.
--- id: illustration-brand category: Brand title: IllustrationBrand description: Used to implement Brand illustrations and adapt their properties. design: 'https://www.figma.com/file/34gRQMq2NxFgeRynjqd24C/Documentation-%7C-Abyss-Mobile?type=design&node-id=1218-19509&mode=design&t=eobYuZOIhhms55Vc-0' --- ```jsx import { IllustrationBrand } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'IllustrationBrand', inputs: [ { prop: 'size', type: 'string', }, { prop: 'illustration', type: 'string', }, { prop: 'variant', type: 'select', options: [ { label: '1', value: '1' }, { label: '2', value: '2' }, ], }, { prop: 'color', type: 'select', options: [ { label: 'primary', value: 'primary' }, { label: 'pacific', value: 'pacific' }, { label: 'white', value: 'white' }, ] }, ] } // Disclaimer: not all brand color combinations or variants are applicable, and inapplicable combinations will display as empty ``` ## Brand Use the `brand` property to adjust which brand is being selected. ```jsx live ``` ## Size Use the `size` property to adjust the size of the illustration. ```jsx live ``` ## Color Use the `color` property to select available illustration colors. ```jsx live ``` ## Variant Some UHC illustrations have multiple variants of accent colors on the same background color. Use the `variant` prop to select the color combination. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render

Illustration Source

```
The source for these illustrations can be found in the brand libraries.

UnitedHealthCare Library
Optum Library

You can use the search functionality to find the required illustration. Illustrations can be searched using their title, variants or colors.

--- id: elevation category: Brand title: Elevation description: Creates depth on screen for users design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=12804%3A65751 --- ```jsx import { Elevation } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Elevation', inputs: [ { prop: 'level', type: 'select', options: [ { label: "0", value: 0 }, { label: "1", value: 1 }, { label: "2", value: 2 }, { label: "3", value: 3 }, { label: "4", value: 4 }, ] }, ] } ``` ## Level The `level` prop determines the magnitude of the shadow effect applied to the surface of the component. The prop accepts numbers between `0` and `4`. The default is `1` ```jsx live Level 0 Level 1 Level 2 Level 3 Level 4 ``` ```jsx render ``` ```jsx render ``` --- id: tokens title: Tokens category: Brand --- ## Overview Design tokens are the visual sub-atom variables of a design system. They contain UI data such as colors, border width, elevation, and even motion. They are used in the place of hard-coded values such as hex codes or pixels to maintain scalability and consistency. Think about them as recipe ingredients - you could add chocolate to a salad, but it won't be very tasty. You would only consider what is a standard salad ingredient - it's the same with tokens, they are a limited set of options that make sense for our product. **Further reading:**
[Nathan Curtis on Tokens in design systems](https://medium.com/eightshapes-llc/tokens-in-design-systems-25dd82d58421). ### Token Hierarchy Abyss uses a 3-tier token system: - Core tier - the WHAT or the OPTIONS: contains primitive values, with no specific meaning - the name of the token and its raw value (HEX code for colors, and numbers for borders, corner radius, opacity, etc.) - Semantic tier - the HOW or the DECISIONS: communicates design decisions on the exact usage of a Core token system-wide. - Brand tokens - shorthand tokens to define common colors used throughout Abyss. ### Using Tokens Before you can consume Abyss tokens, your project must be configured with our [themeProvider](/mobile/ui/theme-provider). This will allow you to access the tokens in your project. Tokens are used in place of hard-coded values such as hex codes or pixels. To use a token, you can reference it in your code using the `$` symbol followed by the token name. All Abyss components can accept tokens, when they are passed in using the `style`, or [styles](/mobile/developers/style-customization) prop. Non-Abyss components can use the [tokenize](/mobile/tools/tokenize) function to accept tokens. #### Examples Brand tokens are useful for quickly defining the color of components. ```jsx sandbox ``` #### Styled Function To create a `View` component with a background color of `$core.color.brand.100`, you can leverage our [styled function](/mobile/tools/styled) and do the following: ```jsx sandbox () => { const Example = styled('View', { backgroundColor: '$core.color.brand.100', height: 100, width: 100, }); return ; }; ``` #### useToken hook Alternatively, you can use our [useToken Hook](/mobile/hooks/use-token) to directly access the value of a token. This is useful when you need the value of multiple tokens. ```jsx sandbox () => { const getColorToken = useToken('colors'); const green = getColorToken('$core.color.green.100'); const red = getColorToken('$core.color.red.100'); return ( <> ); }; ``` #### Tokenize Function Our [tokenize](/mobile/tools/tokenize) function is useful for mapping any non-Abyss component's props to accept Abyss tokens. ```jsx live const TokenizedTouchableHighlight = tokenize(TouchableHighlight, { underlayColor: 'colors', }); render(() => { return ( {}} accessibilityRole="button" > Press Me ); }); ``` #### Useful Links - [Custom Theme Tutorial](/mobile/developers/tutorials/custom-themes/): This tutorial will guide you through creating a custom Abyss theme for your project. - [createTheme Function](/mobile/tools/create-theme): This tool allows you to create and modify themes to fit your design needs. - [Theme Provider](/mobile/ui/theme-provider): Provider that passes the theme object down the component tree giving your project access to Abyss tokens. - [styled Function](/mobile/tools/styled): Tool that allows you to create styled components. - [useToken Hook](/mobile/hooks/use-token): Hook that allows you to access the value of a token in your project. - [tokenize](/mobile/tools/tokenize): Function that allows you to map a component's props to accept Abyss tokens.
## Core Tokens Below is a list of core tokens used throughout Abyss, These are split into the categories `color`, `border-width`, `border-radius`, `opacity`, `spacing`, `sizing`. _**Note:** Click on the token row to copy the token to your clipboard._ #### Border Width Tokens `border-width` tokens are used to define the `borderWidth` on components. ```jsx const Example = styled('View', { borderWidth: '$core.border-width.md', }); ``` ```jsx render ``` #### Border Radius Tokens `border-radius` tokens are used to define the `borderRadius` on components. ```jsx const Example = styled('View', { borderRadius: '$core.border-radius.md', }); ``` ```jsx render ``` --- #### Opacity Tokens `opacity` tokens are used to define the opacity of a component. ```jsx const Example = styled('View', { opacity: '$core.opacity.md', }); ``` ```jsx render ``` --- #### Spacing Tokens `spacing` tokens define the space between components. Generally, these are used for the `padding`, `margin`, or `gap` of components. ```jsx const Example = styled('View', { padding: '$core.spacing.200', }); ``` ```jsx render ``` --- #### Sizing Tokens `sizing` tokens define the size of components. Generally, these will be used to define the `width` or `height`. ```jsx const Example = styled('View', { width: '$core.sizing.600', height: '$core.sizing.600', }); ``` ```jsx render ``` --- #### Color Tokens `color` tokens are used to define the color of components. ```jsx const Example = styled('View', { backgroundColor: '$core.color.brand.100', }); ``` ```jsx render ``` ## Brand Tokens Brand tokens are useful for quickly defining the color of components. _**Note:** Click on the token row to copy the token to your clipboard._ #### Colors Below is a list of brand tokens and their intended use cases. ```jsx const Example = styled('View', { backgroundColor: '$primary1', }); ``` ```jsx render ``` --- #### Spacing `spacing` tokens define the space between components. Generally, these will be used to define the `padding`, `margin`, or `gap` of components. ```jsx const Example = styled('View', { padding: '$md', }); ``` ```jsx render ``` --- #### Border Radius `border-radius` tokens define the `borderRadius` on components. ```jsx const Example = styled('View', { borderRadius: '$md', }); ``` ```jsx render ``` --- ### Accessibility Color choices that are accessible ensure everyone can not only see every element on a page but also understand a specific, intended meaning. You want everyone to be able to see the difference between two colors right next to or on top of each other. Contrast refers to the perceived difference between foreground and background colors. People with low vision or color blindness and those with difficulty seeing the differences between colors can have trouble seeing where one element ends and another begins. As we age, the shape of our eyes changes and affects how we perceive color and how well we can distinguish color variations. If the contrast between different elements is too low, people may not be able to see them at all. Contrast is expressed as a ratio with the first number representing the foreground color and the second representing the background color. For example, 3:1 means the foreground item color is three times more intense or visible than the background value. Contrast rules apply to text as well as any content that conveys meaning, including icons, graphics, and form elements. Tools such as Colour Contrast Analyzer or WebAIM's online color contrast tool are useful for verifying contrast ratios. Color and contrast choices within a digital experience are accessible when people can: - See UI elements and content - Understand and interpret information - Take action We aim to provide a contrast ratio perceivable by all users. For this reason, UnitedHealthcare has embraced a minimum contrast ratio of 4.5:1 (foreground vs background) for UI elements and content that conveys meaning. Recommendations - Include color combinations, good contrast and poor contrast, in design documentation - Communicate meaning with more than just color, such as with color and descriptive text - Give focus indicators a unique presentation that meets contrast requirements on all backgrounds Test for a minimum contrast ratio of 4.5 to 1 for: - Non-bolded text smaller than 24 pixels (18 points) - Bold text smaller than 18 pixels (14 points) - Essential icons that are close to the body text size Test that non-text elements that communicate information meet a minimum contrast ratio of 3 to 1 for all states: - Icons - Data visualizations - Focus indicators - Controls, including their borders or boundaries - Non-bolded text at or above 24 pixels (18 points) - Bold text at or above 18 pixels (14 points) Don't worry about contrast for logos and disabled elements. Watch out for using color alone to communicate meaning. People who are color blind or blind cannot perceive the meaning by color alone. --- id: typography title: Typography category: Brand description: Typography for UHC brands --- ## Overview Typography is the art and technique of arranging type to make written language legible. In the Abyss library, [Heading](/mobile/ui/heading) and [Text](/mobile/ui/text) dive into the detail behind text formatting for UHC branding. More in depth guidance on typography can be found below and in the UHC Brand Page. ## Setting Global Fonts Fonts can be set globally throughout an application by using the createTheme function in conjunction with the ThemeProvider component. The second argument of createTheme function allows you to extend the base theme. Below is an example of setting a token named `customFont`. ```jsx import { ThemeProvider } from '@uhg-abyss/mobile'; import { createTheme } from '@uhg-abyss/mobile'; const theme = createTheme('uhc', { theme: { fonts: { customFont: 'RobotoFlex', }, }, }); const App = () => { return ...; }; ``` This would allow you to consume the font as a token globally. ```jsx Filler Text ``` There are 2 tokens that are reserved for consumer usage: `$text` and `$heading`. Setting the tokens for these two will set the default font for the Text and Heading components globally. If no font is set, the Text and Heading components will default to the system font. ```jsx import { ThemeProvider } from '@uhg-abyss/mobile'; import { createTheme } from '@uhg-abyss/mobile'; const theme = createTheme('uhc', { theme: { fonts: { heading: 'UHCSerif', text: 'UHCSans', }, }, }); const App = () => { return ...; }; ``` ## Headings Headings identify chunks of related content on a page and establish the hierarchy showing how those chunks of content relate to each other. If someone reads only the headings on a page, they will get a general understanding of the information presented. HTML defines six heading levels: H1 to H6. H1 identifies an entire page, or overall topic, and is the most important level. There should only be 1 H1 per page. Find further documentation in the [Heading](/mobile/ui/heading) component. ```jsx render H | 1 | SemiBold H | 2 | SemiBold H | 3 | SemiBold H | 4 | SemiBold H | 5 | Bold H | 6 | Heavy ``` #### Recommendations IMPORTANT: For way-finding, every page must have an H1 available (especially for screen readers) that describes the main purpose of the page such as “Claims & Benefits” --- ## Body Copy Text SF Pro is our primary iOS typeface and Roboto is our primary Android typeface for body copy. All weights are available in italics. Regular copy is the default style for the majority of text on pages. Small copy is the secondary style for context on pages and is used for secondary text styles, as well as footnotes and legal messaging or less important content. Find further documentation in the [Text Component](/web/ui/text). ```jsx render () => { const lorem = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt.'; return ( P | LG | SemiBold {lorem} P | LG | Regular {lorem} P | MD | Bold {lorem} P | MD | SemiBold {lorem} P | MD | Regular {lorem} P | SM | Heavy {lorem} P | SM | Bold {lorem} P | SM | SemiBold {lorem} P | SM | Regular {lorem} P | XS | Bold {lorem} P | XS | SemiBold {lorem} P | XS | Medium {lorem} P | 2XS | SemiBold {lorem} P | 2XS | Medium {lorem} ); }; ``` --- id: brandmark category: Brand title: Brandmark description: Logos/Brandmarks for UHG brands. design: 'https://www.figma.com/file/tk08Md4NBBVUPNHQYthmqp/Abyss-Web?type=design&node-id=0-21&mode=design&t=uWtmzWwvT2Kv5MMG-0' --- ```jsx import { Brandmark } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Brandmark', inputs: [ { prop: 'size', type: 'string', default: '$brandmark.sizing.lg' }, { prop: 'affiliate', type: 'select', options: [ { label: 'uhg', value: 'uhg' }, ], }, { prop: 'variant', type: 'select', options: [ { label: 'lockup', value: 'lockup' }, ] }, { prop: 'color', type: 'select', options: [ { label: 'blue', value: 'blue' }, { label: 'black', value: 'black' }, { label: 'white', value: 'white' }, ] }, ] } // Disclaimer: not all affiliate variant/color combinations are applicable, and inapplicable combinations will display as empty ``` ## Brand Use the `brand` property to adjust which brand is being selected. ```jsx live ``` ## Size Use the `size` property to adjust the size of the brandmark. ```jsx live ``` ## Variant Use the `variant` property to select the required brandmark variants. ```jsx live ``` ## Color Use the `color` property to select available brandmark colors. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render

Brandmarks

```
You can use the search functionality to find the required brandmark. Brandmarks can be searched using their affiliates, variants or colors.
```jsx render ``` --- id: flat-list category: Core title: FlatList description: A performant interface for rendering basic flat lists. sourceIsTS: true --- ## Usage The `FlatList` component is a high-performance list component that efficiently renders only the items currently visible on the screen, regardless of the size of the data set. It is ideal for rendering basic, flat lists and supports the most handy features like: - Full tokenization support. - Fully cross-platform. - Optional horizontal mode. - Configurable viewability callbacks. - Header support. - Footer support. - Separator support. - Pull to Refresh. - Scroll loading. - ScrollToIndex support. - Multiple column support. _If you need section support, consider using the [SectionList](/mobile/core/section-list) component._ ```jsx live const data = [ { id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba', title: 'One', }, { id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63', title: 'Two', }, { id: '58694a0f-3da1-471f-bd96-145571e29d72', title: 'Three', }, { id: '9b7acbea-c1b1-46c2-aed5-3ad53abb28ba', title: 'Four', }, { id: '8ac68afc-c605-48d3-a4f8-fbd91aa97f63', title: 'Five', }, { id: '78694a0f-3da1-471f-bd96-145571e29d72', title: 'Six', }, { id: '6b7acbea-c1b1-46c2-aed5-3ad53abb28ba', title: 'Seven', }, { id: '5ac68afc-c605-48d3-a4f8-fbd91aa97f63', title: 'Eight', }, ]; const ItemWrapper = styled('View', { width: '50%', padding: '$md', }); const ItemContainer = styled('View', { backgroundColor: '$pastel1', padding: '$md', borderWidth: 4, borderColor: '$interactive1', alignItems: 'center', borderRadius: '$lg', }); const Category = styled('View', { height: 50, alignItems: 'center', justifyContent: 'center', }); const Item = ({ title }) => { return ( {title} ); }; render(() => { return ( { return ; }} keyExtractor={(item) => { return item.id; }} numColumns={2} ListHeaderComponent={ FlatList Header } ListHeaderComponentStyle={{ backgroundColor: '$interactive2', borderColor: '$interactive1', borderWidth: 6, marginHorizontal: '$md', borderRadius: '$lg', }} columnWrapperStyle={{ borderWidth: 6, borderColor: '$interactive1', marginVertical: '$xs', backgroundColor: '$interactive2', borderRadius: '$lg', }} contentContainerStyle={{ backgroundColor: '$gray1', padding: '$md' }} ListFooterComponentStyle={{ backgroundColor: '$interactive2', borderColor: '$interactive1', borderWidth: 6, marginHorizontal: '$md', borderRadius: '$lg', }} ListFooterComponent={ FlatList Footer } /> ); }); ``` ### Best Practices - **Use Memoization:** Use [React.memo()](https://react.dev/reference/react/memo) to avoid unnecessary re-renders of list items. - **Pagination:** For large datasets, implement pagination with `onEndReached` to load additional data dynamically. - **Key Extraction:** Ensure `keyExtractor` returns a unique and stable key to avoid performance degradation caused by reordering or re-rendering items unnecessarily. ## Considerations _`FlatList` is a convenience wrapper around [VirtualizedList](/mobile/core/virtualized-list), and thus inherits its props (as well as those of [ScrollView](/mobile/core/scroll-view)) that aren't explicitly listed here, along with the following caveats:_ - **Internal State:** Internal state is not preserved when content scrolls out of the render window. Ensure all your data is captured in the item data or external stores like Flux, Redux, or Relay. - **Prop Updates:** This is a `PureComponent` meaning it will not re-render if props remain shallow-equal. Make sure that everything your `renderItem` function depends on is passed as a prop (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on changes. This includes the `data` prop and parent component state. - **Item Rendering:** In order to constrain memory and enable smooth scrolling, content is rendered asynchronously offscreen. This means it's possible to scroll faster than the fill rate and momentarily see blank content. This is a tradeoff that can be adjusted to suit the needs of each application. - **Key Management:** By default, the list looks for a `key` prop on each item and uses that for the React key. Alternatively, you can provide a custom `keyExtractor` prop. ### Accessibility Considerations - **Screen Reader Support:** Ensure that list items have accessible labels and descriptions, especially if they contain interactive elements. - **Focus Management:** When dynamically loading data, manage focus properly so users can navigate the list without losing track of their position. ### Performance Considerations - **Windowing:** Use `initialNumToRender` and `maxToRenderPerBatch` props to control how many items are rendered initially and in each batch to avoid overloading the UI with too many items at once. - **Recycling Cells:** Consider using `CellRenderComponent` to recycle rendered items and improve rendering performance for large lists. - **Avoid Excessive Renders:** Leverage `shouldComponentUpdate` to prevent unnecessary renders of list items. ```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the FlatList component with design tokens', }, { name: 'hitSlop', type: 'Insets', description: 'This defines how far a touch event can start away from the FlatList', }, { name: 'contentContainerStyle', type: 'Abyss.Style<"View">', description: 'Object or array of objects defining the style of the content container within the FlatList component with design tokens. These styles will be applied to the FlatList content container which wraps all of the child views', }, { name: 'contentInset', type: 'Insets', description: 'The amount by which the FlatList content is inset from the edges of the FlatList', }, { name: 'endFillColor', type: 'Abyss.Color', description: 'The color of the background of the list when there are not enough items to fill the content', }, { name: 'ListFooterComponentStyle', type: 'Abyss.Style<"View">', description: 'Styling for internal View for ListFooterComponent. Accepts an object or array of objects which defines the style of the ListFooterComponent within the FlatList component with design tokens', }, { name: 'ListHeaderComponentStyle', type: 'Abyss.Style<"View">', description: 'Styling for internal View for ListHeaderComponent. Accepts an object or array of objects which defines the style of the ListFooterComponent within the FlatList component with design tokens', }, { name: 'columnWrapperStyle', type: 'Abyss.Style<"View">', description: 'Optional custom style for multi-item rows generated when `numColumns > 1`. Accepts an object or array of objects which defines the style of the column wrapper within the FlatList component with design tokens', }, ]} /> ``` --- id: image category: Core title: Image description: Displays different types of images, including network images, static resources, temporary local images, and images from local disk, such as the camera roll. sourceIsTS: true --- ```jsx import { Image } from '@uhg-abyss/mobile'; ``` ## Usage The Image component is a fundamental UI element that displays images in your application. This component is a customized extension of React Native's `Image` component, which is enhanced to fit seamlessly within our design system. It supports various image formats, allows for custom styling, and includes optimizations for loading performance. ```jsx render Note that for network and data images, you will need to manually specify the dimensions of your image! ```
```jsx live-expanded ``` You can also use the `style` prop with custom design tokens to style the image. ```jsx live ``` ## GIF and WebP support on Android When building your own native code, GIF and WebP are not supported by default on Android. You will need to add some optional modules in `android/app/build.gradle`, depending on the needs of your app. ``` dependencies { // If your app supports Android versions before Ice Cream Sandwich (API level 14) implementation 'com.facebook.fresco:animated-base-support:1.3.0' // For animated GIF support implementation 'com.facebook.fresco:animated-gif:3.1.3' // For WebP support, including animated WebP implementation 'com.facebook.fresco:animated-webp:3.1.3' implementation 'com.facebook.fresco:webpsupport:3.1.3' // For WebP support, without animations implementation 'com.facebook.fresco:webpsupport:3.1.3' } ``` ## Best Practices - **Use Accessibility Label:** Provide meaningful accessibility labels for accessibility. This is especially important for images that convey important information. - **Optimize Images:** Use appropriately sized images to reduce load times and improve performance. Compress images and use formats that balance quality and file size. - **Minimize Re-Renders:** Avoid unnecessary re-renders of the Image component, especially for large or high-resolution images.
```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the Image component with design tokens.', }, { name: 'capInsets', type: 'Insets', description: 'When the image is resized, the corners of the size specified by `capInsets` will stay a fixed size, but the center content and borders of the image will not be stretched.', }, { name: 'height', type: 'Abyss.Size', description: 'Height of the image.', }, { name: 'width', type: 'Abyss.Size', description: 'Width of the image.', }, { name: 'tintColor', type: 'Abyss.Color', description: 'Changes the color of all non-transparent pixels to the `tintColor`.', }, ]} /> ``` --- id: image-background category: Core title: ImageBackground description: Display an image as the background of another component. sourceIsTS: true --- ```jsx import { ImageBackground } from '@uhg-abyss/mobile'; ``` ## Usage A common feature request from developers familiar with the web is a `background-image`. To handle this use case, you can use the `ImageBackground` component, which has the same props as `Image`, and add whatever children to it you would like to layer on top of it. The `ImageBackground` component is a specialized container that displays an image as the background of a view. This component is an enhancement of React Native's core `ImageBackground`, tailored to integrate seamlessly with our design system. ## Example ```jsx live Inside ``` ## Resize Mode The `resizeMode` prop controls how the image is resized within the bounds set by the `style` prop. It can be one of the following values: ```jsx live () => { const [mode, setMode] = useState('cover'); const changeMode = (newMode) => { return () => setMode(newMode); }; const modes = ['cover', 'contain', 'stretch', 'repeat', 'center']; return ( Abyss {modes.map((m) => { return ( ); })} ); }; ``` ```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the ImageBackground component with design tokens.', }, { name: 'capInsets', type: 'Insets', description: 'When the image is resized, the corners of the size specified by `capInsets` will stay a fixed size, but the center content and borders of the image will not be stretched.', }, { name: 'height', type: 'Abyss.Size', description: 'Height of the image.', }, { name: 'width', type: 'Abyss.Size', description: 'Width of the image.', }, { name: 'tintColor', type: 'Abyss.Color', description: 'Changes the color of all non-transparent pixels to the `tintColor`.', }, { name: 'imageStyle', type: 'Abyss.Style<"Image">', description: 'Object or array of objects defining the style of the Image component within the ImageBackground component with design tokens.', }, ]} /> ``` --- id: keyboard-avoiding-view category: Core title: KeyboardAvoidingView description: Automatically adjust its height, position, or bottom padding based on the keyboard height to remain visible while the virtual keyboard is displayed. sourceIsTS: true --- ```jsx import { KeyboardAvoidingView } from '@uhg-abyss/mobile'; ``` ## Usage The `KeyboardAvoidingView` component is designed to automatically adjust the layout of your application when the on-screen keyboard appears, ensuring that the content remains visible and accessible to the user. This component extends the `KeyboardAvoidingView` core component present in React Native, while also supporting tokens in the `style`, `contentContainerStyle`, and `keyboardVerticalOffset` props. ```jsx () => { const [value, setValue] = useState(''); return ( Press the Input ); }; const styles = StyleSheet.create({ heading: { marginBottom: '$sm', }, innerView: { padding: 24, flex: 1, justifyContent: 'space-between', }, code: { backgroundColor: '$gray1', lineHeight: '$md', fontSize: '$sm', fontWeight: '$bold', }, }); ``` ```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the KeyboardAvoidingView component with design tokens.', }, { name: 'contentContainerStyle', type: 'Abyss.Style<"View">', description: 'Style object for the content container within the KeyboardAvoidingView.', }, { name: 'keyboardVerticalOffset', type: 'Abyss.Space', description: 'The distance between the keyboard and the view.', }, ]} /> ``` --- id: keyboard-aware-scroll-view category: Core title: KeyboardAwareScrollView description: A ScrollView component that automatically handles keyboard appearance and scrolls to focused TextInput. sourceIsTS: true --- ```jsx import { KeyboardAwareScrollView } from '@uhg-abyss/mobile'; ``` ## Usage The `KeyboardAwareScrollView` component is a wrapper around [this package's](https://www.npmjs.com/package/react-native-keyboard-aware-scroll-view) `KeyboardAwareScrollView` component that automatically adjusts its content when the keyboard appears. It's particularly useful for forms and other input-heavy screens where you want to ensure that the focused input remains visible when the keyboard is shown. ```jsx () => { const [value1, setValue1] = useState(''); const [value2, setValue2] = useState(''); return ( Form Example ); }; ``` ## Platform Differences - On iOS, the component uses `react-native-keyboard-aware-scroll-view` which provides smooth keyboard handling. - On Android, it falls back to the native `ScrollView` with basic keyboard handling. ```jsx render ``` ```jsx render ``` --- id: pressable category: Core title: Pressable description: A pressable component wrapper that can be used to provide touch feedback. sourceIsTS: true --- ## Usage Pressable is a Core Component that can detect various stages of press interactions on any of its defined children. ```jsx I'm pressable! ``` ### How It Works **On an element wrapped by Pressable:** - [onPressIn](https://reactnative.dev/docs/pressable#onpressin) is called when a press is activated. - [onPressOut](https://reactnative.dev/docs/pressable#onpressout) is called when the press gesture is deactivated. **After onPressIn, the user will either:** - Remove their finger, triggering [onPressOut](https://reactnative.dev/docs/pressable#onpressout) followed by [onPress](https://reactnative.dev/docs/pressable#onpress). - Hold their finger longer than 500 milliseconds before removing it, [onLongPress](https://reactnative.dev/docs/pressable#onlongpress) is triggered. _([onPressOut](https://reactnative.dev/docs/pressable#onpressout) will still fire when they remove their finger)_.
Diagram of the onPress events in sequence. Diagram from React Native Documentation
```jsx render The touch area never extends past the parent view bounds and the Z-index of sibling views always takes precedence if a touch hits two overlapping views. ``` ### HitRect & HitSlop Fingers are not the most precise instruments, and it is common for users to accidentally activate the wrong element or miss the activation area. To help, `Pressable` has an optional `HitRect` you can use to define how far a touch can register away from the wrapped element. Presses can start anywhere within a `HitRect`. `PressRect` allows presses to move beyond the element and its `HitRect` while maintaining activation and being eligible for a "press"—think of sliding your finger slowly away from a button you're pressing down on.
Diagram of the onPress events in sequence. Diagram from React Native Documentation
_You can set `HitRect` with `hitSlop` and set `PressRect` with `pressRetentionOffset`_. ```jsx render Pressable uses React Native's Pressability API. For more information around the state machine flow of Pressability and how it works, check out the implementation for Pressability . ``` ### Styling The `style` prop can be a function, an array of objects, or an object that defines the style of the `Pressable` component with design tokens. When using a function, the function will receive the `pressed` state as an argument. Additionally, the `children` prop can be a function that receives the `pressed` state as an argument or a React element. ```jsx live { return { width: 125, height: 125, borderWidth: pressed ? 10 : 4, borderRadius: '$xl', borderColor: pressed ? '$success1' : '$error3', backgroundColor: pressed ? '$success2' : '$error2', margin: '$md', padding: '$sm', alignItems: 'center', justifyContent: 'center', }; }} onPress={() => console.log('onPress Called!')} onPressIn={() => console.log('onPressIn Called!')} onPressOut={() => console.log('onPressOut Called!')} onLongPress={() => console.log('onLongPress Called!')} > {({ pressed }) => ( {pressed ? 'Release Me' : 'Press Me'} )} ``` ### Best Practices - **Press Feedback:** Always provide clear visual feedback (like color changes or animations) to indicate the element has been pressed. - **Hit Slop:** Use the `hitSlop` prop to extend the touchable area if needed, especially for smaller elements. - **Optimize Press Timing**: Use `onPressIn` and `onPressOut` wisely to handle animations or delays without affecting user experience. ## Accessibility Considerations - **Pressable Texts:** Ensure that the label of the pressable is descriptive for screen readers. - **Feedback for Press**: Make sure the visual feedback is discernible for all users, including those with visual impairments. - **Keyboard Navigation:** Ensure that pressable components are focusable and navigable with a keyboard.
```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the Pressable component with design tokens', }, { name: 'hitSlop', type: 'Inset | Insets', description: 'This defines how far a touch event can start away from the view.', }, { name: 'pressRetentionOffset', type: 'Inset | Insets', description: 'Additional distance outside of this view in which a touch is considered a press before `onPressOut` is triggered.', }, ]} /> ``` ``` --- id: refresh-control category: Core title: RefreshControl description: A standard control that can initiate the refreshing of a scroll view's contents sourceIsTS: true --- ```jsx import { RefreshControl } from '@uhg-abyss/mobile'; ``` ## Usage The `RefreshControl` component is used to implement pull-to-refresh functionality in scrollable views, such as `ScrollView`, `FlatList`, or `SectionList`. This component is a customized version of React Native's core RefreshControl, offering enhanced styling, animations, and better integration with our design system. ```jsx render Note: refreshing is a controlled prop, which is why it needs to be set to true in the onRefresh function otherwise the refresh indicator will stop immediately. ```
```jsx () => { const [refreshing, setRefreshing] = useState(false); const onRefresh = useCallback(() => { setRefreshing(true); setTimeout(() => { setRefreshing(false); }, 4500); }, []); return ( } > Pull down to see RefreshControl indicator ); }; ```
```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the RefreshControl component', }, { name: 'colors', type: 'Abyss.Color[]', description: 'The colors (at least one) that will be used to draw the refresh indicator', }, { name: 'progressBackgroundColor', type: 'Abyss.Color', description: 'The background color of the refresh indicator', }, { name: 'tintColor', type: 'Abyss.Color', description: 'The tint color of the refresh indicator', }, { name: 'titleColor', type: 'string', description: 'The color of the refresh indicator title', }, { name: 'progressViewOffset', type: 'number', description: 'The distance between the refresh indicator and the top of the view', }, ]} /> ``` --- id: safe-area-view category: Core title: SafeAreaView description: Render content within the safe area boundaries of a device. sourceIsTS: true --- ```jsx import { SafeAreaView } from '@uhg-abyss/mobile'; ``` ## Usage The purpose of `SafeAreaView` is to render content within the safe area boundaries of a device. It is currently only applicable to iOS devices with iOS version 11 or later. `SafeAreaView` renders nested content and automatically applies padding to reflect the portion of the view that is not covered by navigation bars, tab bars, toolbars, and other ancestor views. Moreover, and most importantly, Safe Area's paddings reflect the physical limitation of the screen, such as rounded corners or camera notches (i.e. the sensor housing area on iPhone 13). ```jsx This is a SafeAreaView that avoids the Status Bar This is a SafeAreaView that avoids the Home Bar ``` ```jsx render:phone-dark const Container = styled('View', { justifyContent: 'space-between', width: '100%', }); const PaddedView = styled('View', { paddingHorizontal: '$md', backgroundColor: '$primary1', width: '100%', variants: { placement: { top: { paddingTop: 40, paddingBottom: '$md', }, bottom: { paddingBottom: 28, paddingTop: '$md', }, }, }, }); const Label = styled('Text', { color: '$white', fontSize: '$md', textAlign: 'center', }); render(() => { return ( ); }); ``` ```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the SafeAreaView component with design tokens.', }, ]} /> ``` ```jsx render As padding is used to implement the behavior of the component, padding rules in styles applied to a SafeAreaView will be ignored and can cause different results depending on the platform. See{' '} #22211 {' '} for details. ``` --- id: scroll-view category: Core title: ScrollView description: A generic scrolling container that can host multiple components and views. sourceIsTS: true --- ## Usage The `ScrollView` component is a scrollable container that can hold a variety of elements, enabling vertical or horizontal scrolling. This customized version of React Native's [ScrollView](https://reactnative.dev/docs/scrollview) component integrates Abyss design tokens and performance optimizations for a smooth and responsive experience. ```jsx live Scroll Me Lorem ipsum odor amet, consectetuer adipiscing elit. Elit lacinia torquent et mauris habitasse netus efficitur aenean aptent. Finibus posuere maximus tortor, nisi bibendum ultricies. Tellus integer eu commodo sed pharetra mauris quam potenti mauris. Convallis nisl auctor risus mattis id. Himenaeos turpis egestas consequat tortor aliquam, dictumst integer volutpat eu. Quis leo ex parturient arcu sagittis. Et nostra platea vestibulum bibendum pharetra accumsan semper cursus. {'\n'} {'\n'} Dictum porttitor penatibus auctor nisl; sem porttitor curae quisque. Ipsum accumsan eu vestibulum ligula, vehicula integer. Nunc varius massa placerat conubia mus magna. Aliquet eleifend porttitor porta mattis consectetur habitasse at fringilla. Odio aptent aliquam sociosqu justo egestas adipiscing ac conubia. Bibendum ultrices commodo ante mus mollis netus. {'\n'} {'\n'} Bibendum taciti habitant ridiculus scelerisque aliquam varius lacus maximus. Efficitur facilisis parturient auctor accumsan nascetur ad phasellus lectus. Lacinia natoque conubia convallis habitant mauris eleifend. Turpis consectetur tempor egestas taciti; venenatis cursus? Cursus congue adipiscing purus neque vitae nibh? Risus accumsan ullamcorper velit eros tellus curae. Interdum nascetur morbi; pharetra id venenatis volutpat potenti. Convallis senectus praesent lectus nisi eget justo vitae. Venenatis ornare sociosqu euismod feugiat integer. Curae odio massa purus eu facilisis laoreet. {'\n'} {'\n'} Lectus curabitur scelerisque mus auctor nascetur iaculis ante risus. Tristique conubia nisl inceptos bibendum pellentesque. Eleifend imperdiet gravida pellentesque hendrerit eget dignissim magnis varius augue. Curabitur proin porttitor molestie gravida amet praesent et fringilla. Ante etiam at gravida efficitur cubilia rutrum torquent adipiscing. Parturient bibendum id convallis torquent venenatis. Lacinia primis leo nullam tincidunt consectetur quisque. Elementum tristique quis magnis ornare molestie venenatis. Sollicitudin netus class rutrum proin; curabitur facilisis convallis pretium sollicitudin. Feugiat volutpat eget arcu convallis ultricies id. ``` ### ScrollView vs FlatList `ScrollView` renders all its child components all at once, even if the component is not in view. This can negatively affect the performance of the app by requiring more processing power and memory usage when rendering a large number of items. This is where [FlatList](/mobile/core/flat-list) comes into play. `FlatList` renders items lazily, before they appear on screen. It also removes items that scroll off-screen to save memory and processing time. `FlatList` is also handy if you want to render separators between your items, multiple columns, infinite scrolling, or any number of other features it supports out of the box. ### Best Practices - **Minimize Overdraw:** Use a background color to avoid unnecessary redrawing of background elements. - **Limit Scrollable Content:** Avoid putting too many elements inside a `ScrollView`. If the content grows large, consider using a `FlatList` or `SectionList` for better performance. - **Optimize Images:** When scrolling with images, ensure they are properly sized and optimized to prevent memory issues. ## Considerations - **Bounded Height:** The `ScrollView` must have a bounded height, since they contain children with unbound-heights. - To set the height of a `ScrollView`, either define a height directly (discouraged) or make sure the parent Components have bounded height. - **Nested Responders:** `ScrollView` does not yet support preventing touch gestures on its children from becoming scroll gestures. - This doesn't mean that responders won't work inside of a `ScrollView`. It simply means `ScrollView` will prioritize its own responder if it detects a gesture that could be interpretedas a scroll. - _*For more information, check out React Native's [Gesture Responder System](https://reactnative.dev/docs/gesture-responder-system).*_ ### Accessibility Considerations - **Keyboard Focus:** Ensure that the content inside the `ScrollView` is reachable and visible when users navigate with a keyboard. - **Readable Labels:** Label any scrollable area for screen readers, so users know they can scroll through the content. - **Scroll Indicators:** Allow scroll indicators to be visible for accessibility users, so they are aware of the scrollable content. ### Performance Considerations - **Rendering Limits:** Avoid placing too many child components inside a `ScrollView` as it can cause performance bottlenecks. Instead, use lists (`FlatList`,`SectionList`) for large datasets. - **Lazy Loading:** Consider lazy loading for content-heavy views to avoid loading everything upfront. - **Batch Updates:** Ensure updates to the scroll view content are batched to reduce rendering overhead. ```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the ScrollView component with design tokens', }, { name: 'contentContainerStyle', type: 'Abyss.Style<"View">', description: 'Object or array of objects defining the style of the content container within the ScrollView component with design tokens. These styles will be applied to the scroll view content container which wraps all of the child views', }, { name: 'contentInset', type: 'Insets', description: 'The amount by which the scroll view content is inset from the edges of the scroll view', }, { name: 'hitSlop', type: 'Insets', description: 'This defines how far a touch event can start away from the ScrollView', }, { name: 'endFillColor', type: 'Abyss.Color', description: 'Sometimes a scrollview takes up more space than its content fills. When this is the case, this prop will fill the rest of the scrollview with a color to avoid setting a background and creating unnecessary overdraw. This is an advanced optimization that is not needed in the general case', }, ]} /> ``` --- id: section-list category: Core title: SectionList description: A performant interface for rendering sectioned lists. sourceIsTS: true --- ## Usage The `SectionList` component is a high-performance list component that efficiently renders sectioned lists. It is ideal for rendering lists with sections and supports the most handy features like: - Full tokenization support. - Fully cross-platform. - Configurable viewability callbacks. - List header support. - List footer support. - Item separator support. - Section header support. - Section separator support. - Heterogeneous data and item rendering support. - Pull to Refresh. - Scroll loading. _If you don't need section support and want a simpler interface, use a [FlatList](/mobile/core/flat-list)._ ```jsx live const data = [ { title: 'Main Dishes', data: ['Pizza', 'Burger', 'Risotto'], }, { title: 'Sides', data: ['French Fries', 'Onion Rings', 'Fried Shrimps'], }, { title: 'Drinks', data: ['Water', 'Coke', 'Beer'], }, { title: 'Desserts', data: ['Cheese Cake', 'Ice Cream', 'Brownies'], }, ]; const Item = styled('Text', { backgroundColor: '$interactive3', padding: '$md', marginVertical: '$xs', fontSize: '$xl', color: '$primary1', borderWidth: 2.5, borderColor: '$primary1', }); render(() => { return ( { return item + index; }} renderItem={({ item }) => { return {item}; }} renderSectionHeader={({ section: { title } }) => { return {title}; }} contentContainerStyle={{ padding: '$md' }} /> ); }); ``` ### Best Practices - **Use Memoization:** Use [React.memo()](https://react.dev/reference/react/memo) to avoid unnecessary re-renders of list items. - **Section Headers:** Keep section headers concise to avoid taking up too much space. - **Key Management:** Ensure that the `keyExtractor` for individual items and sections is unique to avoid rerender issues. ## Considerations _`SectionList` is a convenience wrapper around [VirtualizedList](/mobile/core/virtualized-list), and thus inherits its props (as well as those of [ScrollView](/mobile/core/scroll-view)) that aren't explicitly listed here, along with the following caveats:_ - **Internal State:** Internal state is not preserved when content scrolls out of the render window. Ensure all your data is captured in the item data or external stores like Flux, Redux, or Relay. - **Prop Updates:** This is a `PureComponent` meaning it will not re-render if props remain shallow-equal. Make sure that everything your `renderItem` function depends on is passed as a prop (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on changes. This includes the `data` prop and parent component state. - **Item Rendering:** In order to constrain memory and enable smooth scrolling, content is rendered asynchronously offscreen. This means it's possible to scroll faster than the fill rate and momentarily see blank content. This is a tradeoff that can be adjusted to suit the needs of each application. - **Key Management:** By default, the list looks for a `key` prop on each item and uses that for the React key. Alternatively, you can provide a custom `keyExtractor` prop. ### Accessibility Considerations - **Navigable Sections:** Ensure section headers and items are properly labeled for screen readers. - **Focus Management:** When rendering additional sections, ensure the user focus remains consistent without jumping or losing context. ### Performance Considerations - **Batch Updates:** Use batch updates to avoid triggering multiple re-renders when updating section data. - **SectionHeader Optimization:** Memoize section headers to prevent unnecessary re-renders during list scrolls. - **Windowing:** Use `initialNumToRender` and `maxToRenderPerBatch` props to control how many items are rendered initially and in each batch to avoid overloading the UI with too many items at once. - **Recycling Cells:** Consider using `CellRenderComponent` to recycle rendered items and improve rendering performance for large lists. ```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the SectionList component with design tokens', }, { name: 'hitSlop', type: 'Insets', description: 'This defines how far a touch event can start away from the SectionList', }, { name: 'contentContainerStyle', type: 'Abyss.Style<"View">', description: 'Object or array of objects defining the style of the content container within the SectionList component with design tokens. These styles will be applied to the scroll view content container which wraps all of the child views', }, { name: 'contentInset', type: 'Insets', description: 'The amount by which the scroll view content is inset from the edges of the scroll view', }, { name: 'endFillColor', type: 'Abyss.Color', description: 'The color of the background of the SectionList when there are not enough items to fill the content', }, { name: 'ListFooterComponentStyle', type: 'Abyss.Style<"View">', description: 'Styling for internal View for ListFooterComponent. Accepts an object or array of objects which defines the style of the ListFooterComponent within the FlatList component with design tokens', }, { name: 'ListHeaderComponentStyle', type: 'Abyss.Style<"View">', description: 'Styling for internal View for ListHeaderComponent. Accepts an object or array of objects which defines the style of the ListHeaderComponent within the FlatList component with design tokens', }, ]} /> ``` --- id: status-bar category: Core title: StatusBar description: Component to control the app's status bar. sourceIsTS: true --- ```jsx import { StatusBar } from '@uhg-abyss/mobile'; ``` ## Usage The `StatusBar` component controls the app's status bar. The status bar is the zone, typically at the top of the screen, that displays the current time, Wi-Fi and cellular network information, battery level and/or other status icons. This component is a customized version of React Native's `StatusBar` core component, offering additional configuration options and integration with our design system. It allows you to manage the status bar's `style`, `visibility`, and `backgroundColor` to match the application's theme with our design tokens. ## Usage with Navigator It is possible to have multiple StatusBar components mounted at the same time. The props will be merged in the order the StatusBar components were mounted. ## Imperative API For cases where using a component is not ideal, there is also an imperative API exposed as static functions on the component. However, it is not recommended to use the static API and the component for the same prop because any value set by the static API will get overridden by the one set by the component in the next render. ## Performance Considerations The StatusBar is a lightweight component, but to ensure optimal performance: - **Minimize Dynamic Changes:** Limit frequent updates to the status bar's properties, as unnecessary changes can impact performance, especially on lower-end devices. - **Use Animations Judiciously:** While animations can enhance the user experience, use them sparingly to avoid performance degradation. ## Accessibility Considerations When configuring the StatusBar, ensure that the chosen colors and styles provide sufficient contrast and readability. The status bar should be easily readable in various lighting conditions and should not obscure critical content or UI elements. ```jsx render ``` ```jsx render ``` --- id: touchable-highlight category: Core title: TouchableHighlight description: A wrapper for making views respond properly to touches. sourceIsTS: true --- ```jsx render If you're looking for a more extensive and future-proof way to handle touch-based input, check out the Pressable API. ``` ## Usage `TouchableHighlight` is a wrapper for handling pressEvents of a `View`. While pressing down, the opacity of the wrapped view is decreased, allowing the underlay color to show through. ```jsx function MyComponent(props: MyComponentProps) { return ( My Component ); } alert('Pressed!')} > ; ``` ```jsx live render(() => { return ( {}} > Gray Touchable Highlight ); }); ``` ## Considerations - **Visual Artifacts:** The underlay comes from wrapping the child in a `View` component. This can sometimes cause unwanted visual artifacts and affect layout if not used correctly. For example, if the `backgroundColor` of the wrapped `View` is not explicitly set to an opaque color. - **Children:** `TouchableHighlight` can only have a single child. If you wish to have several children, wrap them in a `View`. ### Accessibility Considerations - **Feedback for Users:** Ensure that the visual feedback (`underlayColor` color) is noticeable for users with visual impairments. - **Screen Reader Support:** Label the component appropriately for screen readers so users understand the interaction. - **Keyboard Focus:** Ensure the component can be focused and activated using a keyboard for accessibility. ```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the TouchableHighlight component with design tokens', }, { name: 'hitSlop', type: 'Inset | Insets', description: 'This defines how far a touch event can start away from the view', }, { name: 'pressRetentionOffset', type: 'Inset | Insets', description: 'Additional distance outside of this view in which a touch is considered a press before `onPressOut` is triggered', }, { name: 'underlayColor', type: 'Abyss.Color', description: 'The color of the underlay that will show through when the touch is active', }, { name: 'activeOpacity', type: 'Abyss.Opacity', description: 'Defines what the opacity of the wrapped view should be when touch is active', }, ]} /> ``` --- id: touchable-opacity category: Core title: TouchableOpacity description: A wrapper for making views respond properly to touches. sourceIsTS: true --- ```jsx render If you're looking for a more extensive and future-proof way to handle touch-based input, check out the{' '} Pressable {' '} API. ``` ## Usage The `TouchableOpacity` component is used to create pressable elements that fade out on press, providing smooth and subtle visual feedback. This component is lightweight and often used for buttons or other pressable elements that require an opacity change on interaction. This customized version of the React Native `TouchableOpacity` component is enhanced to supports Abyss design tokens and fit seamlessly into our design system. Opacity is controlled by wrapping the children in an `Animated.View`, which is added to the view hierarchy. Be aware that this can affect layout. ```jsx live const styles = StyleSheet.create({ button: { borderWidth: 2, padding: '$md', alignItems: 'center', borderRadius: 100, borderColor: '$info1', backgroundColor: '$conditional2', }, }); render(() => { return ( Opacity Button ); }); ``` ### Best Practices - **Consistent Opacity:** Use consistent values for activeOpacity across your app to maintain uniformity in user interactions. - **Clear Press Feedback:** Ensure that the change in opacity is noticeable enough to signal to users that the element is pressed. - **Layering Components:** Be mindful when layering TouchableOpacity over complex backgrounds, as the fade effect might not be as visible. ## Accessibility Considerations - **Visual Feedback:** Ensure the fade effect is sufficiently noticeable for all users, especially those with visual impairments. - **Keyboard Navigation:** Ensure the component can be navigated and activated via a keyboard. - **Accessible Labels:** Add descriptive labels to the TouchableOpacity component so screen readers can convey its functionality. ```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the TouchableOpacity component with design tokens', }, ]} /> ``` --- id: touchable-without-feedback category: Core title: TouchableWithoutFeedback description: Captures touch events without providing any visual feedback. sourceIsTS: true --- ```jsx render If you're looking for a more extensive and future-proof way to handle touch-based input, check out the Pressable API. ``` ## Usage The `TouchableWithoutFeedback` component is a wrapper that captures touch events without providing any visual feedback. It is ideal for handling touch events on components that don't need to display any visual feedback. **Do not use unless you have a very good reason.** All elements that respond to press should have a visual feedback when touched. ```tsx function MyComponent(props: MyComponentProps) { return ( My Component ); } alert('Pressed!')}> ; ``` ### Example ```jsx live () => { const Container = styled('View', { flex: 1, justifyContent: 'center', paddingHorizontal: '$md', }); const CountContainer = styled('View', { alignItems: 'center', padding: '$md', }); const Count = styled('Text', { color: '$interactive1', }); const ButtonView = styled('View', { alignItems: 'center', backgroundColor: '$gray2', padding: '$sm', }); const [count, setCount] = useState(0); const handlePress = () => { setCount(count + 1); }; return ( Count: {count} Increment ); }; ``` ### Best Practices - **Use Sparingly:** Only use `TouchableWithoutFeedback` when no visual feedback is required or desired. If feedback is expected, use `Pressable` or `TouchableOpacity` instead. - **Visual Feedback:** Ensure the child components provide sufficient visual feedback, even if `TouchableWithoutFeedback` does not. - **Invisible Buttons:** Consider accessibility implications if `TouchableWithoutFeedback` is used to create invisible or hidden touch areas. ## Considerations - **Number of Children:** `TouchableWithoutFeedback` only supports one child. If you wish to have several child components, wrap them in a View. - **Prop Spread:** `TouchableWithoutFeedback` Does not handle touch events directly, it clones its child and applies responder props to the clone. Therefore it is important that any intermediary components pass props through to the underlying React Native component. ### Accessibility Considerations - **Keyboard Navigation:** Ensure the component is accessible via keyboard and focusable if needed. - **Screen Reader Labels:** If touchable areas are hidden or provide no feedback, ensure the interactive area is described accurately for screen readers. ```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the TouchableWithoutFeedback component with design token support.', }, { name: 'hitSlop', type: 'Inset | Insets', description: 'This defines how far a touch event can start away from the view.', }, { name: 'pressRetentionOffset', type: 'Inset | Insets', description: 'Additional distance outside of this view in which a touch is considered a press before `onPressOut` is triggered.', }, ]} /> ``` ``` --- id: view category: Core title: View description: The most fundamental core component for building a UI, a container that supports layout. sourceIsTS: true --- ```jsx import { View } from '@uhg-abyss/mobile'; ``` ## Usage The `View` component is one of the fundamental building blocks of a user interface. It functions as a container that supports layout, styling, and interaction handling. The component serves as a versatile wrapper for organizing and displaying other components within a user interface. While it extends the core View component present in React Native, the `View` component in Abyss Mobile allows using tokens directly in the style prop. This feature enables developers to use the design tokens defined in the theme to style a component. This example creates a View that wraps two boxes with color and a text component in a row with padding. Notice the use of design tokens in the style prop. ```jsx live Hello World! ``` ```jsx render Views are designed to be used with{' '} StyleSheet for clarity and performance, although inline styles are also supported. ``` ## Best Practices - **Use for Layout**: Utilize the `View` component to create layouts and organize components within a user interface. For text context, consider using the [Text component](/mobile/ui/text). - **Avoid Excessive Nesting**: Limit the number of nested `View` components to maintain a clean and efficient layout. - **Use Tokens**: Leverage design tokens in the style prop to ensure consistency and maintainability in styling. - **Accessibility**: Ensure that the content within the `View` component is accessible to all users by providing appropriate labels and descriptions where necessary. ## Performance Considerations To maximize performance, be mindful of: - **Shallow component trees**: Minimize nesting `View` components to reduce the complexity of the component tree. - **Avoid unnecessary re-renders**: Use `React.memo` or similar optimization when rendering complex or frequently changing layouts. ```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the View component with design tokens.', }, ]} /> ``` --- id: virtualized-list category: Core title: VirtualizedList description: A high-performance list component that efficiently renders only the items currently visible on the screen, regardless of the size of the data set. sourceIsTS: true --- ## Usage The `VirtualizedList` component is a high-performance list component that efficiently renders only the items currently visible on the screen, regardless of the size of the data set. This is the base implementation of the [FlatList](/mobile/core/flat-list) and [SectionList](/mobile/core/section-list) components. In general, this should only be used if you need more flexibility than FlatList or SectionList can provides, e.g. for use with immutable data instead of plain arrays. Virtualization massively improves the performance and memory consumption of large lists by maintaining a finite render window showing only active items and replacing all items outside of the render window with appropriately sized blank space. The render window adapts with scrolling behavior, items are rendered incrementally with _low-pri_ (after any running interactions) if they are far from the visible area, or with _hi-pri_ if they are near the visible area. ```jsx live const ItemContainer = styled('View', { backgroundColor: '$conditional3', height: 150, justifyContent: 'center', marginVertical: '$sm', marginHorizontal: '$md', padding: 20, }); const ItemText = styled('Text', { fontSize: 32, }); const Item = ({ title }) => { return ( {title} ); }; const getItem = (_data, index) => { return { id: Math.random().toString(12).substring(0), title: `Item ${index + 1}`, }; }; render(() => { return ( { return ; }} keyExtractor={(item) => { return item.id; }} getItemCount={() => { return 50; }} getItem={getItem} ListFooterComponent={} ListHeaderComponent={} ListHeaderComponentStyle={{ borderColor: '$conditional3', borderWidth: 4, marginHorizontal: '$md', }} ListFooterComponentStyle={{ borderColor: '$conditional3', borderWidth: 4, marginHorizontal: '$md', }} /> ); }); ``` ### Best Practices - **Pagination:** Implement infinite scrolling by utilizing the `onEndReached` prop for large datasets. - **Use Memoization:** Use [React.memo()](https://react.dev/reference/react/memo) to avoid unnecessary re-renders of list items. - **Lazy Load Images:** If the list contains images, ensure they are lazy-loaded to avoid consuming memory unnecessarily. - **Key Management:** Ensure `keyExtractor` returns a unique and stable key to avoid performance degradation caused by reordering or re-rendering items unnecessarily. ## Considerations - **Internal State:** Internal state is not preserved when content scrolls out of the render window. Ensure all your data is captured in the item data or external stores like _Flux_, _Redux_, or _Relay_. - **Prop Updates:** This is a `PureComponent` meaning it will not re-render if props remain shallow-equal. Make sure that everything your `renderItem` function depends on is in passed as a prop (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on changes. This includes the `data` prop and parent component state. - **Item Rendering:** To constrain memory and enable smooth scrolling, content is rendered asynchronously offscreen. This means it's possible to scroll faster than the fill rate and momentarily see blank content. This is a tradeoff that can be adjusted to suit the needs of each application. - **Key Management:** By default, the list looks for a `key` prop on each item and uses that for the React key. Alternatively, you can provide a custom `keyExtractor` prop. ### Accessibility Considerations - **Focus Retention:** Retain focus and scroll position when adding or removing items from the list. - **Content Labeling:** Ensure each list item has accessible labels or descriptions for screen readers. ### Performance Considerations - **Windowing:** Adjust the `initialNumToRender` and `windowSize` props to strike a balance between performance and UX. - **Cell Recycling:** Use `CellRendererComponent` to efficiently recycle rendered items, especially for very large datasets. - **Avoid Expensive Re-renders:** Avoid triggering unnecessary re-renders by memoizing components or using `React.PureComponent`. ```jsx render ``` ```jsx render ', description: 'Object or array of objects defining the style of the VirtualizedList component with design tokens', }, { name: 'hitSlop', type: 'Insets', description: 'This defines how far a touch event can start away from the VirtualizedList', }, { name: 'contentContainerStyle', type: 'Abyss.Style<"View">', description: 'Object or array of objects defining the style of the content container within the VirtualizedList component with design tokens. These styles will be applied to the virtualized list content container which wraps all of the child views', }, { name: 'contentInset', type: 'Insets', description: 'The amount by which the virtualized list content is inset from the edges of the virtualized list', }, { name: 'endFillColor', type: 'Abyss.Color', description: 'The color of the background of the list when there are no items to display', }, { name: 'ListFooterComponentStyle', type: 'Abyss.Style<"View">', description: 'Styling for internal View for ListFooterComponent. Accepts an object or array of objects which defines the style of the ListFooterComponent within the FlatList component with design tokens', }, { name: 'ListHeaderComponentStyle', type: 'Abyss.Style<"View">', description: 'Styling for internal View for ListHeaderComponent. Accepts an object or array of objects which defines the style of the ListHeaderComponent within the FlatList component with design tokens', }, ]} /> ``` --- id: design-checklist title: Design Checklist --- ## Overview Welcome to Abyss! If you’re just starting out designing with Abyss, you’re in the right place. Here’s a checklist of everything you need to get up and running. Abyss design kit is available in Figma through our enterprise account (Optum/UHG) ## Create Figma Account ## Using the Designer Toolkit ## Review Updates --- id: design-kit title: Design Kit --- ## Overview ## Designer Toolkit ## Guidance ## Accessibility ## Contact Us --- id: code-connect title: Code Connect --- ## Figma Code Connect for Mobile Figma Code Connect is a Design-to-Code tool that aims to scaffold out the code required to implement a Figma Design. **Note:** Code Connect is currently in alpha and availability for components is changing. We invite you to discuss any enhancements or limitations in our Github Discussion Topic. **Note:** Components on the UHC or Global Figma with Code Connect enabled may only be for the V2 version of the component. See the list below for supported components. ## Instructions This Demo Video contains an overview of how to use Abyss with Code Connect. - Open the Figma file with the component you want to use and select the component. In dev mode, the Code Connect will be viewable in the side bar under "Recommended Code". - The button "Explore component behavior" will allow you to see the component in a preview mode. You can change available props and variants from this panel. _Due to Figma limitations, not all possible combinations will be available through here. Check Abyss documentation._ ```jsx render Code Connect Example with Badge ``` #### Slot limitations At this time, code connect does not support slots. If you need to use a slot, you will need to manually add it to the code after copying it from Code Connect. The reccomended code section does not show the actual slot element's code. ```jsx render Code Connect Example with Slots ``` ### Supported Components ```jsx render ``` --- id: abyss-admirals title: Abyss Admirals isHidden: true --- ## Who are Abyss Admirals?
An Abyss Admiral is a highly specialized role for a software engineer who is a dedicated member of a product delivery team. The most basic and essential function of an Admiral is to act as a bridge between the core Abyss ecosystem and the product team leveraging the framework.

Acting as representatives or ambassadors for their products, Admirals enable the adoption of a{' '} scalable, federated software development model by sharing the Abyss community's best practices with their teams. As subject matter experts for Abyss, Admirals are encouraged to guide and mentor their engineering teams, empowering them to take advantage of the benefits of working in a collaborative enterprise environment.
Abyss Admirals
## Benefits for Product Stakeholders It's very important for product stakeholders to understand that an Admiral's involvement in their new responsibilities will reduce their capacity for delivering sprint work as a standard individual contributor. However, by allocating enough time for the role, Admirals will enable engineering scrum teams to measurably improve both quality and delivery metrics. It's recommended to dedicate between **30% - 50%** of an Admiral's capacity for this role, but could be up to 100% depending on the size and scope of the project. Product stakeholders will be able to capitalize on the efficiencies gained by leveraging the collective knowledge and shared solutions that are accessible through the broader Abyss community. The benefits of staffing a dedicated Admiral on your product include: - **Accelerated Solution Development:** When delivery teams are asked to identify and create solutions to common problems, they’ll need to do so in between developing new features which can result in delays. An Admiral assists their product teams at critical moments by eliminating these bottlenecks and offering proven solutions, which in turn increases the speed of delivery.

- **Minimized Duplication of Work:** The Abyss team facilitates the creation of reusable digital assets such that, when the business makes a new request, an Admiral can utilize a similar solution that was built previously for another team rather than building a new one from scratch, greatly minimizing cost and time to value.

- **Consistent Product Quality:** It’s reasonable to assume that most teams will not be evenly balanced when it comes to experience and skill levels, resulting in products being built with different techniques and standards. Admirals can ensure that the quality of development is both consistent and in accordance with the established standards of other products built with Abyss.

- **Expansive Specialist Network:** When working with an Abyss Admiral, product stakeholders obtain access to a network of highly experienced and qualified specialists including software architects, lead engineers, UX designers, accessibility experts who are motivated to craft the best product experiences possible. ## Benefits for Engineering Managers It's very important for engineering managers to understand that an Admiral's involvement in their new responsibilities will reduce their capacity for delivering sprint work as a standard individual contributor. However, by allocating enough time for the role, Admirals will enable engineering scrum teams to measurably improve both quality and delivery metrics. It's recommended to dedicate between **30% - 50%** of an Admiral's capacity for this role, but could be up to 100% depending on the size and scope of the project. Engineering managers will be able to capitalize on the efficiencies gained by leveraging the collective knowledge and shared solutions that are accessible through the broader Abyss community. The benefits of staffing a dedicated Admiral on your delivery team include: - **Reduced Software Fragmentation:** When individual teams are developing within disconnected, siloed environments, they’ll often discover multiple different approaches to solve the same problem. Admirals can act as advisors to prevent this additional overhead from occuring by raising awareness of pre-existing solutions.

- **Promote Engineering Growth:** For an engineer who is eager to progress further along their career path, the Admirals program offers an elevated set of responsibilities for overseeing software projects. Since this role is both highly technical and relationship-oriented, coupled with a sense of personal accountability, Admirals can leverage this experience to explore their interest in management or technology leadership roles.

- **Accountability for Essential Tasks:** Engineering teams are often overburdened with upkeep and maintenance related chores because they are given a lower priority than feature work. By assigning an Admiral to each project, engineering managers can verify that code quality, versioning, and peer review processes are being observed.

- **Optimized Outcomes:** Admirals reduce the time and cost of development through specialization and economies of scale. By tapping into a centralized community of knowledge, skills, and experience, the Admirals program is able to streamline access to those scarce capabilities while also facilitating balanced, cohesive engineering teams. ## Admiral Assignments - **Upgrade Abyss Versions:** It's highly beneficial to keep your product up-to-date with the newest versions of Abyss. Inform your engineering team and product stakeholders of any new components, tools, or patterns your application can leverage. - **Review the [release notes](/mobile/releases/) after a release** to determine the level of effort for upgrading to the latest version. - **Run the command "npm run abyss"** to automatically upgrade all Abyss packages in your project. - **Support for new features and defects** will only be included in new versions.

- **Monitor Code Quality:** As an Admiral, the accountability of maintaining high standards for code quality starts with you. Become well-versed in JavaScript, React, ESLint, and SonarQube anti-patterns and shepherd your team away from these pitfalls, reducing the burden of unrestrained technical debt and extending the lifespan of your codebase. - **Remediate runtime errors & warnings** observed in the browser's developer console for your product. - **Inspect problems reported by [ESLint](https://eslint.org/docs/latest/rules)** and discuss rule modifications with other Admirals. - **Triage issues identified by [Sonar](https://sonar.optum.com)** to ensure your product meets code quality benchmarks.

- **Manage Pull Requests:** Within the GitHub repository for your product, you should encourage your team to open pull requests regularly. By consulting with other Admirals, you are in the most well-suited position to act as a code reviewer for your team. - **Open draft PR's early** in the sprint to give you and your team enough time to review and offer feedback on the approach. - **Offer comments and conduct reviews** for each PR before approving. - **Merge PR's in a timely manner** to improve time-to-build metrics for your product.

- **Leverage Assets:** Admirals should strive to identify all of the usuable assets that exist within Abyss, as well as the network of individuals involved. Becoming familiar with the abstract concepts of a framework will elevate the engineering maturity of your team. - **Research code developed for Abyss** to understand the patterns for consistent, repeatable software practices. - **Review and update documentation** which demonstrates guidance for best practices, guidelines, and considerations. - **Foster relationships with key experts** who possess very specific and unique skillsets who can influence the growth of your product.

- **Continous Learning:** To be successful, Admirals should provide thought leadership, direction, and appropriate recommendations for their teams and the Admiral community. The ability to both absorb and transfer knowledge is essential. - **Have a self-starter attitude** and a passion for growing your career by being surrounded by like-minded engineers. - **Seek opportunities for learning** by reading developer blogs, attending tech conferences, and networking with other Admirals. - **Familiarize yourself with industry trends** by researching and recommending techniques for application development.

- **Sustainable Software:** When left unchecked, the sustainability of an application can continously deteoriate. Admirals are able to counteract this by taking appropriate measures to establish a healthy development environment and extend the lifespan of a product. - **Maintain a log of tech debt** and track the ongoing scope of maintainance tasks incurred from past sprints. - **Conduct frequent pair programming** sessions with your team to guide current feature development. - **Discuss upcoming requirements** with architects to establish a clear path for future stories in your product pipeline.

- **Abyss Contributions:** With the Admiral contribution process, the development process for new assets can be accelerated by building the solution yourself as the need arises; rather than waiting for your idea to reach the top of the Abyss core backlog. - **Determine the priority** for framework enhancements based on your product delivery schedule. - **Discuss new ideas in [Office Hours](#abyss-office-hours)** with the core team and other Admirals. - **Follow the [Contribution Workflow](#contribution-workflow)** shown below to share your proposals with the framework. ## Admiral Developers Guide If an existing Abyss component doesn't meet your product's requirements, you can follow this guide for building and testing changes within your application's codebase. Start by cloning the package structure of abyss within your product, such as **'src/abyss/mobile/ui/Badge'** demonstrated below. If you are creating a new component, you can start with a similar one as a template, otherwise cloning the existing component is the recommended approach. ```txt └── packages └── mobile ├── node_modules ├── src | └── ui | └── Badge | ├── index.js | └── Badge.jsx └── package.json ``` Next, replace the relative imports with absolute paths to **@uhg-abyss/mobile**. You can use any combination of Abyss package imports, open source libraries, and custom JavaScript dependencies to build your component. ```jsx import React from 'react'; import PropTypes from 'prop-types'; import { styled } from '../../tools/styled'; import { useAbyssProps } from '../../hooks/useAbyssProps'; ``` Replace with: ```jsx import React from 'react'; import PropTypes from 'prop-types'; import { styled } from '@uhg-abyss/mobile/tools/styled'; import { useAbyssProps } from '@uhg-abyss/mobile/hooks/useAbyssProps'; ``` Finally, to test your component changes, modify your import path by changing **'@uhg-abyss/mobile/ui/Badge'** to **'@src/abyss/mobile/ui/Badge'** which will use your local Abyss component. Once you have fully verified your changes, you can submit a new Pull Request back to [Abyss](https://github.com/uhc-tech/abyss/pulls) and showcase your updates in the Abyss office hours. Once merged, your contributions will be available in the next release! ## Contribution Workflow As an Abyss Admiral the workflow for a contribution goes as follows: 1. Office Hours: Discuss proposal for new components, designs, architecture, and tools with other Admirals. 2. Abyss Contact us: If idea can be re-used, submit a new request with Abyss "Contact Us" form. 3. Develop Locally: Follow the steps in Admiral developers guide to create re-usable asset locally in your product. 4. Abyss GitHub: Before opening a new Pull Request, ensure that all requirements are met for UX, branding, and accessibility guidelines. 5. Abyss Office Hours: Demo proposed feature with Abyss core team and other Admirals. 6. Abyss Github: Pull Request undergoes modifications from feedback, acceptance, quality checks and merge. The contribution will end with the finalized abyss packages ![Contribution Workflow](/img/graphics/abyss_admirals_flowchart.png) ## Abyss Office Hours | Day | Time | Meeting | | --------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Tuesdays | 3:00 - 4:00 PM **CST** | [Join Teams Meeting](https://teams.microsoft.com/l/meetup-join/19%3ameeting_YjVlMDM3OTgtY2ExYi00OTU0LWIyYTYtODk0NGUwN2E2MmUz%40thread.v2/0?context=%7b%22Tid%22%3a%22db05faca-c82a-4b9d-b9c5-0f64b6755421%22%2c%22Oid%22%3a%226e73a16f-0cf1-4fd2-9501-31c2c9038e9b%22%7d) | | Thursdays | 9:00 - 10:00 AM **CST** | [Join Teams Meeting](https://teams.microsoft.com/l/meetup-join/19%3ameeting_YjVlMDM3OTgtY2ExYi00OTU0LWIyYTYtODk0NGUwN2E2MmUz%40thread.v2/0?context=%7b%22Tid%22%3a%22db05faca-c82a-4b9d-b9c5-0f64b6755421%22%2c%22Oid%22%3a%226e73a16f-0cf1-4fd2-9501-31c2c9038e9b%22%7d) | --- id: abyss-contributors title: Abyss Contributors --- ## Overview First of all, thank you for your interest in contributing to Abyss. All of your contributions are valuable to the project! There are several ways you can get involved in the Abyss community and become a contributor: - **Share Abyss:** Share the link to [Abyss](https://abyss.uhc.com) with members of your product team, and we'd be happy to discuss how we can help support your application. - **Improve documentation:** Help us improve the Abyss Docs by fixing incomplete or missing sections, examples, and explanations. - **Provide feedback:** The team at Abyss are constantly working to make the project better, please let us know what features you would like to see with the [Contact Us](/mobile/contact-us/) form. ## Abyss Code Repo ```jsx render () => { const packages = [ 'api', 'core', 'desktop', 'ext', 'infra', 'mobile', 'parcels', 'utility', 'web', ]; const products = ['assets', 'docs', 'ext', 'scaffold', 'storybook']; return ( {' '} The Abyss source code monorepo contains both core packages and products Packages: {packages.map((item) => { return ( {item} ); })} Products: {products.map((item) => { return ( {item} ); })} {` `} } > Visit ); }; ``` ### Setting Up Project Locally For the essential system tools to get Abyss running on your local development environment, visit our [workspace setup](/mobile/developers/workspace-setup) guide. To set up, clone the abyss repository: ```bash # Make the abyss-projects directory mkdir abyss-projects && cd abyss-projects # Clone the abyss repository git clone https://github.com/uhc-tech/abyss.git ```
Afterwards, install the dependencies for `abyss` on your machine: ```bash # Go into the abyss directory cd abyss # Install abyss dependencies npm i ```
Then you are ready to start `abyss-docs` on your machine: ```bash npm run docs ``` ### Commit Conventions Head to the Abyss source code for updates and additions to our Abyss NPM packages and documentation. With several contributors working in these repos daily, it's important to write your commit messages to be as descriptive as possible. Commit Convention: ``` [area] Optional title: Message ```
Examples: ``` [docs] Button: Edit accessibility section [@uhg-abyss/ui] useLoadingOverlay: Add remove handler [@uhg-abyss/core] Fix non-prod deployment scripts [@uhg-abyss/ui] Carousel: New feature added [docs] Doc scripts: Fix docs deployment script ``` ### Git Branch Names Naming the branch you're working on helps repository maintainers understand the changes being made when the PR is opened. Using consistent branch name prefixes also allows build tools to automatically categorize the branches using labels. Branch names should be all lowercase (with the exception of US and DE) and include hyphens between words. All branches are divided into four groups: - **story/#######** - Changes associated with a User Story, use the unique 7-digit number from Rally followed by a task description. - **defect/#######** - Changes associated with a Defect, use the unique 7-digit number from Rally followed by a task description. - **refactor/** - Changes to the repo that aren't documented in Rally are considered refactors, so use the task portion to add detail to your branch name. - **release/** - Used specifically by build tools, this branch name is exclusive to release notes and documentation leading up to a new release. Examples: ``` git checkout -b story/US2434515-developer-toolkit git checkout -b defect/DE308703-button-accessibility git checkout -b refactor/select-list-multi-docs git checkout -b story/US1533842-use-loading-overlay ```
Branch Name Rules: - Branch prefix must start with **story**, **defect**, **refactor**, or **release** - Branch name must be only **lowercase letters, numbers, and hypens** - **US###** and **DE###** are valid character exceptions ## Secure Groups Visit secure.uhc.com to request permissions groups: - **abyss_contributors**: For write access to abyss code repositories ## Developer Tools Abyss is built using a list of trusted resources. Below are links to what makes up the framework of Abyss Mobile. ```jsx render () => { const devLinks = [ { id: 1, name: 'React Native', href: 'https://reactnative.dev/', }, { id: 2, name: 'Emotion', href: 'https://emotion.sh/docs/introduction', }, { id: 3, name: 'React Navigation', href: 'https://reactnavigation.org/docs/getting-started', }, { id: 4, name: 'npm ', href: 'https://docs.npmjs.com/about-npm', }, ]; return ( {devLinks.map((link) => { return ( } > {link.name} ); })} ); }; ``` If you're ready to get started with Abyss on your own, checkout the Abyss StarterKit (coming soon) to get started. ## Design Tools Abyss has a dedicated team of designers creating a Design Kit on Figma. Below are some resources to help developers navigate these tools: ```jsx render () => { const designLinks = [ { id: 1, name: 'Abyss Design Kit', href: 'https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=1180-3541&t=1l9yUfKeafljt2k0-0', }, { id: 2, name: 'Figma for developers', href: 'https://www.figma.com/best-practices/tips-on-developer-handoff/an-overview-of-figma-for-developers/', }, { id: 3, name: 'UHC branding', href: 'https://brand.uhc.com/design-with-care', }, { id: 4, name: 'Optum branding', href: 'https://brand.optum.com/', }, ]; return ( {designLinks.map((link) => { return ( } > {link.name} ); })} ); }; ``` If you're a designer and want to dive deeper into the Abyss Design Kit, visit our Designer Getting Started (coming soon) page to learn more.
--- id: documentation-guide title: Documentation Guide --- ## Overview The documentation pages are organized under the **docs** directory shown below. When adding a new component, tool, or guide to Abyss Docs, create a new markdown.md file under the associated folder. ```txt abyss-docs-web └── docs └── mobile ├── brand ├── developers ├── hooks ├── tools └── ui ``` ## Markdown Structure Each markdown file should begin with the following metadata, as an example: ```md --- id: card category: Layout title: Card description: A single or multi-section container used to display content related to a single subject. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/branch/Sk3MrHYxjT39TKDzDU5LBc/Abyss-Mobile?node-id=12334%3A61355 --- ```
Every doc page is divided into three tabs: Overview, Integration and Accessibility. Within the body of the markdown file, use these tabs to group sections of information. ``` **Overview Content** **Integration Content** **Accesibility Content** ```
## Overview Tab ###### Import statement Add the import statement for the feature like such: ```jsx import { Alert } from '@uhg-abyss/mobile'; ``` ###### Component Sandbox Add Sandbox after the import statement for any components that make sense to have a sandbox. Inputs are controlled props that can be adjusted by the user using the Sandbox features. Each input contains `prop`, `type` and optionally: `options` and, `defaultValue`. To create a Sandbox, use the convention below: ```jsx sandbox { component: 'Alert', inputs: [ { prop: 'title', type: 'string', }, { prop: 'description', type: 'string' }, { prop: 'variant', type: 'select', options: [ { label: 'success', value: 'success' }, { label: 'warning', value: 'warning' }, { label: 'error', value: 'error' }, { label: 'info', value: 'info' }, ] }, ] } Go To Results ``` ###### Property examples Following the Sandbox, it's important to show the ability of each property separate of the others. We break each one down, giving it a title, description and jsx example showing variants of that specific property. For example, if you wanted to show the sizes for Button, you'd write: ``` ```
Since there are multiple visual variants of Button, which use the same sizing convention (`small` & `large`) we can combine the visuals under the one size example by organizing them utilizing the built-in Layout component from the Abyss library. Here's what the combined example looks like: ``` ```
To follow the complexity of each prop example, use the following rules to properly document the feature: - **When organizing the list of examples,** they should be ordered from simple to complex starting with size or width - **Start each example case** with "Use the `prop-name` property to..." followed by an explanation - **For props with a pre-set list of variants,** add a sentence listing out the variant options "Variants include `variant-1`, `variant-2`", and so on - **For props with a default value,** add "The default value is set to `value`" - **For the customization example section,** include the sentence “If further customization is needed, most styles of `component-name` can be overridden by passing style props to `abyss-component-name`. See the class table on each component for more details” - **Size and width examples** should include the list of Abyss style sizes - **Examples may include:** size, width, isDisabled, controlled, uncontrolled, loading, and customization. Take a look at other doc pages for examples of how to best format the component you're documenting ## Integration Tab Implementing a props table and classes table for the component, and any sub-components gives users an in-depth view of the component without having to visit the code. (The below example is modified for this template. Please refer to the Button component for a full list of props and classes). Follow these rules when creating a Props Table: - **Prop name** is lowercase - **Type** is one of the following: boolean, function, array, shape, number, string, number | string - **Description** first word is uppercase, followed by a brief description of the props use Follow these rules when creating a Classes Table: - **Class name** is lowercase and uses dashes to separate words - **Description** first word is uppercase, followed by a brief description of the class #### Example of Integration Tab ```jsx render ``` ```jsx render ``` ## Accessibility Tab This tab is important to be as thorough and in-detail as possible, adhering to the WAI-ARIA design guidelines. Check out the accessibility documentation on React Native for guidence during development. Follow this pattern when creating the Accessibility tab: - **Brief description** write a description about the component, and link to the WAI-ARIA website page referring to the component - **Sandbox** allows our A11Y partners to practice assistive technology on the component in a dedicated field - **Keyboard interactions table** referring to the WAI-ARIA keyboard interactions, create a table with all interactions usable for the specific component - **Additional guidance** note any additional guidance features of the component, including (but not limited to) Decorative Icons, Loading State, etc. #### Example of Accessibility Tab An alert is an element that displays a brief, important message in a way that attracts the user's attention without interrupting the user's task. Dynamically rendered alerts are automatically announced by most screen readers, and in some operating systems, they may trigger an alert sound. It is important to note that, at this time, screen readers do not inform users of alerts that are present on the page before page load completes. Adheres to the Alert WAI-ARIA design pattern. ```jsx live {}} /> {}} /> {}} /> {}} /> ```
```jsx render ``` ###### Decorative Icons In the alert below, since the word “Warning” appears next to the icon, the icon is considered decorative and must be ignored by assistive technology. The icon does not need to meet the 3:1 minimum contrast requirement against its adjacent color. ```jsx live ``` ###### Close Button Guidance Keyboard operation: if the “close” button is used on the alert, it must be keyboard accessible. A keyboard only user must be able to tab to the button, and activate it with the space bar and the enter key. ```jsx live {}} /> ```
Note: per the WAI ARIA specification, when the “alert” role is used, the user should not be required to close the alert. In this case, it is assumed that the close button is provided as a convenience and the user is not explicitly required to close the alert. --- id: mobile-contribution-standards title: Mobile Contribution Standards --- ## Overview Thanks for getting involved with Abyss! If you've made it here we'll assume you've reviewed the [Abyss Contributors](/mobile/developers/contributors/abyss-contributors) page. To make the contribution process go as smooth as possible, we've laid out our code standards and development steps for you below. As a reminder the component you are developing should _already_ be approved by product, design, and accessibility. ## Code Standards While components may differ, most Abyss Mobile UI components have the following... The outermost styled element will be named `ComponentNameRoot`. The rest of the names should describe what they contain.
Below is a simplified example from [Modal](/mobile/ui/modal/) ```
// code
```
The `useAbyssProps` hook must be imported in each component. This allows consuming teams to customize the style of the component and gives the ability to assign unique test-ids. ``` const abyssProps = useAbyssProps(props); ``` `abyssProps` are spread into each element that will allow for customization and testing. The class names start with the component name followed by a dash and then the element name. These names should be simple and self-explanatory. For example, `button-root` describes the root element and `button-label` describes the label element. ``` ```
Add accessibility props when applicable. ``` ```
Don't forget the display name. ``` Button.displayName = '@uhg-abyss/mobile/ui/Button'; ``` ### Types, Props, and Classes All the types, props, and classes used in the component must be defined in a `ComponentName.types.ts` file. This file should export a TypeScript interface for the component's props, classes, and any reused types.
Below is a simplified example of Button: ``` export type ButtonClasses = { 'button-root': Abyss.Class<'Animated.Pressable'>; 'button-label': Abyss.Class<'Animated.Text'>; }; ``` Unless a prop is marked as required in the TypeScript interface, be sure to add a default value where applicable (and within the component itself). ``` export interface ButtonProps extends Abyss.BaseProps { /** * The contents of the button component */ children?: | React.ReactNode | ((state: PressableStateCallbackType) => React.ReactNode); /** * Defines the button type (style) * @default 'filled' */ type?: 'filled' | 'outline' | 'text'; /** * Defines the button size * @default 'large' */ size?: 'large' | 'small'; /** * Disables the button * @default false */ isDisabled?: boolean; /** * Callback fired when the Button is pressed */ onPress?: Abyss.GestureResponderEventHandler; } ``` ``` export interface ButtonRef extends Abyss.PressableRef {} ```
Once the `ComponentName.types.ts` file is complete import the necessary elements into the `ComponentName.tsx` file. ``` import type { ButtonProps, ButtonRef } from './Button.types'; ``` The full props spread may look something like the example below.

`ButtonRoot` is the outermost `Pressable` component, so `size` and `type` are passed in for styling, as well as all the props pertaining to press interactions: `onPress`, `onPressIn`, `onPressOut`, and `disabled`. `{...abyssProps('button-root')}` is added, and for the Button component specifically, accessibility props are only within the `ButtonRoot` element. ``` ``` ## Development Workflow ###### 1. Developer Refinement Before starting development, you are **required** to attend our Mobile Developer refinement session. Here we will discuss the development plan for the component you are contributing. This includes outlining code already available to be used within your component, as well as what we expect to be reusable from your component. ###### 2. Development Please review our [Code Standards](/mobile/developers/contributors/mobile-contribution-standards/#code-standards) before you get started, and remember we're here to help! Feel free to reach out an Abyss Mobile developer and attend [office hours](/mobile/contact-us/?card=meetings) for anything that comes up during development. ###### 3. Documentation A component is not complete without proper documentation. Please see our [Documentation Guide](/mobile/developers/contributors/documentation-guide/) for more details and examples. Be sure to continue to update documentation with any changes from the Peer, QE, and A11y reviews. ###### 4. Peer Review After you've completed development, make a pull request and reach out to the Abyss Mobile developer that has been assigned to review your component. They will do an initial review, as well as check any code changes after QE and Accessibility testing. Any changes requested bring you back to [development](/mobile/developers/contributors/mobile-contribution-standards/#2-development). ###### 5. Quality Engineering Review Once the PR is complete, your component will be sent to our quality engineer for testing. Any changes requested bring you back to [development](/mobile/developers/contributors/mobile-contribution-standards/#2-development). ###### 6. Accessibility Review After your component has been approved by QE, it will be passed on to our accessibility engineer for testing. You are responsible for adding accessibility elements within your component. Check out our [Accessibility Testing](/mobile/developers/testing/accessibility-testing/) page and the React Native documentation for further guidance. Any changes requested bring you back to [development](/mobile/developers/contributors/mobile-contribution-standards/#2-development). If the changes made only pertain to accessibility, the component does not need to be reviewed again by QE. ## Definition of Done By contributing to Abyss, you are committing to the full development cycle. A component is complete when all changes have been made and accepted by the Peer, QE, and Accessibility reviews, and is thoroughly documented. --- id: ai-code-gen-how-to title: How to use AI to perform code generation "CodeGen" with Abyss sidebar_label: AI Code Generation searchSiteWide: true --- ## Design-to-Code ## Prompt-to-Code --- id: installation title: Installation --- There are two ways to go about installing Abyss - either by creating a new Abyss app or by adding Abyss to an existing application. ## Create Abyss App (Recommended) The recommended way to get started with Abyss is by using `create-abyss-app`. Go to our [tutorials](/mobile/developers/tutorials/create-abyss-app/) to get started! ### Advantages - **Zero Configuration Setup** - All tools are already configured that are essential for React development. Tools like Webpack (for bundling your code) and Babel (for using modern JavaScript features). - **Easy To Use** - You can start developing and see those results immediately. - **Optimized Build Output** - Provides optimized production builds out of the box, including minification, concatenation, and efficient loading (e.g., code splitting). - **Community and Support:** - Many team are already using the `create-abyss-app`, it offers significant support, regular updates, and a large number of resources for troubleshooting. ## Existing Application For teams that want to use Abyss web within an existing React framework (i.e. NextJs) or are unable to migrate their existing application to a create-abyss-app you can still use Abyss within your application. ### Peer dependencies Please note that react and react-native are peer dependencies, meaning you should ensure they are installed before installing Abyss. ```jsx "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-native": ">=0.63.0 <1", "react-native-safe-area-context": ">=3.0.0", "react-native-screens": ">=2.0.0", "react-native-svg": ">=12.0.0" "@react-navigation/bottom-tabs": ">=5.0.0", "@react-navigation/native": ">=5.0.0", "@react-navigation/native-stack": ">=5.0.0", }, ``` ### Install Abyss Mobile To add Abyss mobile to an existing application, first install the Abyss dependencies: ```jsx npm install @uhg-abyss/mobile ``` Then, wrap your application root component with [`ThemeProvider`](/mobile/ui/theme-provider). The `ThemeProvider` enables global theming for your application, with the option to customize or rely on the default styles of Abyss components. Utilizing React's context, it distributes your theme to all nested components. ```jsx import { ThemeProvider } from '@uhg-abyss/mobile/ui/ThemeProvider'; const theme = createTheme('uhc'); function Demo() { return ( ); } ``` ## Upgrading Abyss Abyss releases [New Versions](/mobile/releases/) on a biweekly basis. For further details, refer to our [Versioning Guide](/mobile/developers/migration/versioning-guide/). Benefits to staying current with the latest version of Abyss include: - **Adhering to Brand Guidelines** - Align with the latest branding guidelines, ensuring your application maintains a consistent look and feel with the overall brand identity. - **Enhanced Security** - Address vulnerabilities and security enhancements to protect your application against emerging threats. - **Improved Accessibility** - As accessibility standards evolve, Abyss updates provide enhancements and fixes that help ensure your application is accessible to all users, including those with disabilities. - **Access to New Components Features** - Gain access to new components and features that can enrich the user experience and offer new functionality for your application. - **Bug Fixes** - Addresses defects that improve the stability and performance of your application. - **Efficient Upgrades and Minimal Regression Testing** - Staying updated with the latest version simplifies the upgrade process and minimizes related regression testing efforts. ### How To Upgrade Abyss You can upgrade Abyss by running the following command in the root of your application: ```bash npm install @uhg-abyss/mobile@latest ``` ```bash yarn add @uhg-abyss/mobile@latest ``` --- id: v2-prepping-guide title: Prepping for V2 --- ```jsx render ``` ## Prepping for V2 of Abyss This is a prepping guide for ways teams can prepare for the upgrade from Abyss V1 to V2 before V2 is officially released. **What is the difference between a prepping guide and a migration guide?** This prepping guide is different from the migration guide that will be released with Abyss V2. Understanding these differences will help you plan your approach: | Prepping Guide (Current) | Migration Guide (Future) | | ----------------------------------------------- | ------------------------------------------------------------ | | Available **before** V2 release | Will be available **after** V2 release | | Focuses on **gradual preparation** | Provides comprehensive instructions for full migration to V2 | | Helps distribute workload across sprints | Will be a one-time migration effort | | Identifies components to start replacing now | | | Allows teams to adopt V2 patterns incrementally | | | **Reduces** migration complexity later | |
By following this prepping guide now, you'll significantly reduce the effort required when the full migration guide is released with Abyss V2. ## Dependencies ```jsx // from: - "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" // to: + "react": "^18.0.0 || ^19.0.0" ``` ## Components With the migration to Abyss V2 we are making several changes to our components. This includes: - **Prop changes**: Some props have been removed, modified, or renamed. - **Class changes**: Some classes have been removed and renamed. - **Component deprecations**: Some components will be deprecated and replaced with new ones. - **Component breaking changes**: Some components will have breaking changes that may affect how they are used in your application. ### Deprecations Here is a list of tools/hooks/components that will no longer be available in Abyss V2: **Note:** This list is not exhaustive and may change as we finalize the migration.
Teams should start replacing these components with their new counterparts or alternative solutions **now**, rather than waiting for the V2 release. By proactively doing this now, you can: - Reduce the refactoring effort required when V2 is officially released - Migrate in smaller, more manageable increments - Identify and resolve integration issues earlier - Ensure a smoother and quicker transition to V2 ### Component breaking changes We've documented all component changes to help with migration planning. For details, see the [Component Changes](/mobile/developers/migration/v2/components/) section. Many V2 components are already available with a "V2" prefix (e.g., `V2Button`). Start using these now to ease migration. **Note:** When V2 is officially released, the "V2" prefix will be removed and these components will replace their V1 counterparts. For example, `V2Button` will become `Button`. **Why should I use the V2 components now?** - Familiarize yourself with the new APIs and features - Identify any potential issues or changes in behavior early - Ensure your codebase is ready for the V2 release without needing a complete overhaul - Many of these components were completely rewritten to improve performance, accessibility, and usability - These components have many added features and improvements that are not available in the V1 components (e.g., tokens, design improvements, etc.) **Note:** These V2 components may change before the official V2 release. However, they are stable for production use and can be safely integrated into your application. --- id: v1-to-v2-guide title: V1 to V2 Guide isHidden: true --- ```jsx render ``` --- id: components title: Component Changes --- ```jsx render ``` ## Overview This guide focuses on breaking prop changes to be aware of when migrating from Abyss V1 to V2. These include: - Props that have been removed - Props whose behavior or typings have been updated - Props whose names have been changed but whose functionality remains the same. This guide does **not** cover: - New props added in V2 - Class changes - Token changes - Additional features and enhancements For complete documentation of all available props, including new features added, refer to each component's dedicated documentation page. ## Badge ## Banner ## Button ## Carousel ## CellGroup ### Cell ## DonutChart ## LoadingSpinner ## Popover ## Rating ## SelectInputMulti ## Skeleton ## Timeline --- id: overview title: Overview --- Abyss is a full-stack mobile application framework that enables you to build products faster and easier than ever. It features a comprehensive set of tools that weaves together the best parts of React Native and GraphQL. By taking common patterns and modularizing them into accessible and reusable packages, Abyss is designed to accelerate the development of production-ready React Native applications. The framework handles all heavy lifting behind the scenes, allowing you to focus on core business logic specific to your product. Automated code quality tools analyze, identify, and correct errors in the code, giving developers real-time feedback and training to standardize programming styles. With improvements in project maintainability, scalability, and source code quality, Abyss aims to deliver the best overall development experience. Developers looking to use Abyss must have, or obtain access via Secure, to Artifactory (repo1.uhc.com) ## Learning React Native Just starting your journey with React Native? Abyss is a framework built on top of the popular React Native library. To get started, visit the React Native documentation. If you are interested in learning high-level concepts, check out the getting started guide for React Native. ## Developer Tools Abyss is built using a list of trusted resources. Below are links to what makes up the framework of Abyss Mobile. ```jsx render () => { const devLinks = [ { id: 1, name: 'React Native', href: 'https://reactnative.dev/', }, { id: 2, name: 'Emotion', href: 'https://emotion.sh/docs/introduction', }, { id: 3, name: 'React Navigation', href: 'https://reactnavigation.org/docs/getting-started', }, { id: 4, name: 'npm ', href: 'https://docs.npmjs.com/about-npm', }, ]; return ( {devLinks.map((link) => { return ( } > {link.name} ); })} ); }; ``` ## Support If you're ready to get started with Abyss for your next project, check out our [Contact Us](/mobile/contact-us) page. Submit a new support request and let us know how we can help your team. If you found Abyss to be helpful, please give us a star on github! --- id: style-customization title: Style Customization description: Guide to override styles for Abyss components. design: https://www.figma.com/file/tk08Md4NBBVUPNHQYthmqp/Abyss-Design-System?node-id=0%3A1 --- ## Overview Every Abyss component supports style customization using class names. Customization should be kept at a minimum, as the Abyss components are set to create a standard across all UHG affiliated products. ## Prop Overrides To apply your styles to any component, go to the **Integration** tab under component documentation and find the classes table. The class name column will tell you how to target specific elements in any component. ### Button Example Here you have a default `Button` component. ```jsx live ```
Similarly, to customize `Button` component, you can target specific class names to change the styles. The `Button` component should maintain a 3:1 color contrast ratio. Please visit the [accessibility](/accessibility) documentation page to read more on designing an accessible component. ```jsx render ``` ```jsx // Add styles to Button ``` ## States Components that allow for additional customization depending on their state can be configured by passing the desired style to the Abyss class using the `&:state` selector. Available states will be displayed on the integration tab. ```jsx live () => { const today = new Date(); const day = today.getDate(); const [value, setValue] = useState(today); return ( { return date.getDay() === 6; }} styles={{ 'abyss-calendar-day-label': { '&:selected': { color: '$primary1' }, '&:disabled': { color: '#99A8C9' }, }, 'abyss-calendar-selection-circle': { backgroundColor: '#d9f6fa', }, }} /> ); }; ``` --- id: quality-engineering title: Quality Engineering description: QE Testing Overview --- ## Dedication To Quality ## Test Plan ## Automation Testing Automation testing of Abyss components is a top priority. Currently, our automation tests consist of the following: ### Web ### Mobile ### Unit Testing ## Manual Testing ## FAQ --- id: end-user-spec title: End User Specifications description: Abyss Spec --- ## Version Requirements ## Testing and Review ## How are these numbers calculated? ## Abyss Web ## Abyss Mobile

\* Last updated March 2025. To ensure this document is kept up to date and relevant, it should be revisited and revised with the latest metrics information on a set schedule, such as quarterly or bi-annually. --- id: component-testing title: Component Testing description: Guide on how to facilitate testing of Abyss Mobile components. --- ## testID To facilitate the usage of component testing libraries such as **[React Native Testing Library](https://callstack.github.io/react-native-testing-library/docs/getting-started)** you have the option of adding a `testID` attribute to a component's corresponding elements. By passing `testID` in as a prop with a value of the desired string id this attribute will be appended to all component elements that include a unique Abyss class name. Please see the Integration tab and the Classes sub-heading for each component to determine which elements will receive this test id. The resulting `testID` value will be a concatenated string that combines the value passed in with the prop and the element's unique class name. For example, the following code: ```jsx live Add your dependents to your account to view their claims and coverage. ``` will render the following structure: ```jsx Add your dependents to your account to view their claims and coverage. May 13, 2025 ``` ## Change testing strategy By default, the `testID` will have an abyss class name appended to it. If you do not want this, you can use the [`TestProvider`](/mobile/ui/TestProvider) component to change the testing strategy. The `TestProvider` component has two strategies: `"class"` and `"root"`. ### Root strategy The `"root"` strategy applies the `testID` only to the root element of the component. This means you won't be able to target nested elements within the Abyss component for testing. For example, let's say the `testID` of [ProgressBar](/mobile/ui/ProgressBar) is set to `"your-test-ID"`. ```jsx ``` You can use the following code to find and test the element: ```jsx import { render } from '@testing-library/react-native'; const screen = render( ); const element = screen.getByTestId('your-test-ID'); ``` ### Class strategy The `"class"` strategy (similar to default) allows testing of nested components by appending the Abyss class name to your `testID`. This creates unique identifiers for each element within the component. For example, let's say the `testID` of [ProgressBar](/mobile/ui/ProgressBar) is set to `"your-test-ID"`. ```jsx ``` You will have to look at the classes for the ProgressBar component to determine which elements will receive this test ID and have its class name appended to it. ```jsx render ``` The resulting test IDs will be _**`"your-test-ID-abyss-progress-bar-root"`**_ & _**`"your-test-ID-abyss-progress-bar-slide"`**_. You can then use the following code to find and test an element's subcomponents: ```jsx import { render } from '@testing-library/react-native'; const screen = render( ); const element = screen.getByTestId('your-test-ID-abyss-progress-bar-slide'); ``` --- id: accessibility-testing title: Accessibility Testing --- ## Overview Mobile accessibility, also known as A11Y, is the design and creation of mobile applications that can be used by everyone regardless of age, device, or disability. Accessibility support is necessary to allow assistive technology to interpret mobile pages. Abyss fully supports building accessible mobile applications and follows the Web Content Accessibility Guidelines (WCAG 2.1) and the UHG Accessibility Engineering Standards by the Accessibility Center of Excellence (ACOE). The list below are steps to take as a developer to ensure accessibility compliance. Please take a minute to read through the following testing resources and familiarize yourself with how to utilize them for best practices. ## Keyboard Navigation Use an external Bluetooth keyboard only to navigate the page without using your finger to tap or swipe. A visual indicator, known as the keyboard focus, should appear when navigating content. Look for difficulty reaching or activating interactive components, or if the focus becomes trapped on portions of the screen. Expected keyboard behavior for custom components is typically the following, but there are exceptions - Content navigation - **Tab** to enter a component - Use **arrow keys** to navigate within the component - **Tab** to exit the component - Interacting with CTA or form components - **Enter** or **Space** to mimic a ‘tap’ or ‘press’ - **Arrow keys** to navigate to an item within a list OR mimic a ‘swipe’ - **Tab** to move onto the next item ## Desktop Development Tools XCode's Accessibility Inspector tool is available for inspecting the code of the application for accessibility features and labels via a mobile emulator. ## Screen Reader Mobile Controls A Screen Reader is an accessibility tool used primarily by sight-deficient users to navigate computer/mobile content. They interact with applications by reading aloud the content presented in the code. On mobile devices, users utilize either custom tap/swipe controls, Bluetooth keyboards, or both, to interact with the application. These users are impacted the most from lacking A11Y implementation. Testing with a screen reader on physical mobile devices is important to understand if the code is working effectively for these tools. Mobile devices provide the following screen readers, each with similar, yet unique functionality - iOS VoiceOver: Learn about VoiceOver gestures - Android TalkBack: Learn about TalkBack gestures ## NPM Packages Most NPM packages rely on axe-core. Set an impact level and start with critical issues then work down. Remember to allow time to fix critical issues in the User Story. Otherwise, the product developers will get frustrated and learn to ignore the errors, which defeats the purpose and doesn't help anyone. ## Linting For linting rules, work with an Accessibility Engineer to determine what to include there. ## Summary Remember, the tools/processes mentioned above don't catch all A11Y issues, but they serve as a great start to empowering the team to do some of your own testing. You can learn more from the Mobile Accessibility page in the Accessibility Knowledge Center. For further information, reach out to an Accessibility Engineer! ## Accessibility Tools If you're looking for an in-depth overview of what accessibility standards Abyss is working towards, visit our Abyss [Accessibility page](/web/accessibility). ```jsx render () => { const accessibilityLinks = [ { id: 1, name: 'WCAG 2.1', href: 'https://www.w3.org/WAI/WCAG21/Understanding/', }, { id: 2, name: 'Color Contrast Analyser (CCA)', href: 'https://webaim.org/resources/contrastchecker/', }, { id: 3, name: 'W3 Validator', href: 'https://validator.w3.org/favelets.html', }, { id: 4, name: 'Digital A11y', href: 'https://www.digitala11y.com/accessibility-bookmarklets-testing/', }, { id: 5, name: 'React Native Accessibility', href: 'https://reactnative.dev/docs/accessibility', }, ]; return ( {accessibilityLinks.map((link) => { return ( } > {link.name} ); })} ); }; ``` --- id: create-abyss-app title: Create Abyss App --- --- **Note:**
We would appreciate any feedback on our tutorial guide. If you are stuck at any time, make sure to contact the Abyss Admiral assigned to your team. If they cannot help, send a help request on our [Contact Page](/mobile/contact-us/). --- Before starting, be sure to complete the [Workspace Setup](/mobile/developers/workspace-setup/) guide. ### Step 1: Create an App Now, let's get started. Navigate to your terminal in order to create a new project named **"my-new-app"**. Once there, run the following command: ```bash npx create-abyss-app my-new-app ``` ### Step 2: Navigate to Project Directory Next, navigate into the **my-new-app** project directory by running the command below: ```bash cd my-new-app ``` ### Step 3: Run Abyss Finally, run the following command in order to get localhost running: ```bash npm run mobile ``` Once you see the screen shown below, you are now up and running with Abyss! ```jsx render:phone () => { const Container = styled('ScrollView', { height: '90%', }); const theme = createTheme('uhc'); const links = [ { title: 'About Us', description: 'Learn more about the Abyss library', url: 'https://abyss.uhc.com/web/about', }, { title: 'Components', description: 'View the full list of components inside Abyss Mobile', url: 'https://abyss.uhc.com/mobile/ui/button', }, { title: 'Tokens', description: 'View our pallete of color tokens', url: 'https://abyss.uhc.com/mobile/brand/uhc/colors/', }, { title: 'Custom Styling', description: 'Learn how to customize Abyss components', url: 'https://abyss.uhc.com/mobile/developers/style-customization', }, { title: 'Navigation', description: 'Learn to handle moving between screens inside your application', url: 'https://abyss.uhc.com/mobile/tools/create-bottom-tab-navigator', }, { title: 'Support', description: 'Need help? Vsit our support page', url: 'https://abyss.uhc.com/mobile/contact-us', }, ]; return ( Welcome to Abyss Edit App.tsx to change this screen and then come back to see your edits. {links.map((link) => { return ( window.open(link.url)} /> ); })} ); }; ```
Great job, you have successfully created an abyss app! --- id: import-components title: Import Components --- --- **Note:**
We would appreciate any feedback on our tutorial guide. If you are stuck at any time, make sure to contact the Abyss Admiral assigned to your team. If they cannot help, send a help request on our [Contact Page](/mobile/contact-us/). --- ### Step 1: Open App.tsx In Visual Studio Code, open **my-new-app** project. From here, navigate into **products/mobile**, and open the **App.tsx** file. ```txt └── products └── mobile └── App.tsx ``` ### Step 2: Import React Anytime you’re using a react component, make sure to import the following dependency at the top: ```jsx import React from 'react'; ``` ### Step 3: Importing Component Depending on the project requirements, the **@uhg-abyss/mobile/ui**, **@uhg-abyss/mobile/hooks**, and **@uhg-abyss/mobile/tools** libraries have different components in order to assemble products quickly. There are three ways to import resources: Directly from one of the libraries above, from **@uhg-abyss/mobile**, or from the file for that resource **@uhg-abyss/mobile/ui/ComponentName** Let's start with the **Card** component. A Card acts as a container used to display content related to a single subject. You can access the documentation for the [Card](/mobile/ui/card/) through the Abyss Portal. The import statement for a card can be any of the following: ```jsx import { Card } from '@uhg-abyss/mobile'; ``` ```jsx import { Card } from '@uhg-abyss/mobile/ui'; ``` ```jsx import { Card } from '@uhg-abyss/mobile/ui/Card'; ``` In **App.tsx**, within the tsx of **App** functional component, insert the following code: ```jsx Hello tutorial We did it! ``` ### Step 4: Verifying Your Code Your code in **App.tsx** should now look similar to this: ```jsx import React from 'react'; import { Image, SafeAreaView, ScrollView, View } from 'react-native'; import { createTheme, ThemeProvider, Layout, Text, Card, Heading, } from '@uhg-abyss/mobile'; const theme = createTheme('uhc'); function App(): React.JSX.Element { return ( Welcome to Abyss Edit App.tsx to change this screen and then come back to see your edits. Hello tutorial We did it! ); } export default App; ``` ```jsx render const App = () => { const theme = createTheme('uhc'); return ( Welcome to Abyss Edit App.tsx to change this screen and then come back to see your edits. Hello tutorial We did it! ); }; render(() => { return ; }); ```
Great job, you have successfully imported components! --- id: screen-navigation title: Screen Navigation --- --- **Note:**
We would appreciate any feedback on our tutorial guide. If you are stuck at any time, make sure to contact the Abyss Admiral assigned to your team. If they cannot help, send a help request on our [Contact Page](/mobile/contact-us/). --- Before starting, be sure to complete the [Create Abyss App](/mobile/developers/tutorials/create-abyss-app/) tutorial. ## Step 1: Create New Files In Visual Studio Code, open **my-new-app** project. From here, navigate into **products/mobile/src/navigation**, and create a new file, named **"MyPlanNavigator.tsx"**. Then, navigate to **products/mobile/src/screens**, and create a new folder named **MyPlan**. Within this folder, we'll be creating 3 files: **"MyPlanScreen.tsx"**, **"CoverageScreen.tsx"**, and **"index.ts"**. Your folder structure should look like this: ```txt └── products └── mobile ├── src | ├── navigation | | ├── HomeNavigator.tsx | | ├── MenuNavigator.tsx | | └── MyPlanNavigator.tsx | ├── screens | | ├── Home | | ├── Menu | | ├── MyPlan | | | ├── CoverageScreen.tsx | | | ├── index.ts | | | └── MyPlanScreen.tsx └── App.tsx └── package.json ``` ## Step 2: Create MyPlan Screen Lets create a screen! We'll add a Plan Coverage card. In **"MyPlanScreen.tsx"**, insert the following code: ```jsx import React from 'react'; import { View } from 'react-native'; import { Card, CellGroup, Heading, IconBrand } from '@uhg-abyss/mobile'; export const MyPlanScreen = ({ navigation, route }) => { return ( Plan Coverage } title="Coverage & Benefits" onPress={() => navigation.navigate('Coverage', { benefits: ['Medical', 'Dental', 'Transportation'], }) } /> ); }; ``` Then in **index.ts** , insert the following export command: ```jsx // Exporting MyPlanScreen allows us to import and use MyPlanScreen in the Navigator export { MyPlanScreen } from './MyPlanScreen'; ``` In the code above, we have a Cell that navigates to another screen called **Coverage**. The Coverage screen takes an array of benefits as a parameter. Let's create that screen. ## Step 3: Create Coverage Screen ```jsx import React from 'react'; import { View } from 'react-native'; import { Card, CellGroup, Heading } from '@uhg-abyss/mobile'; export const CoverageScreen = ({ navigation, route }) => { // The benefits array from MyPlanScreen const { benefits } = route.params; return ( Coverage & Benefits {benefits.map((benfit) => { return ( {}} /> ); })} ); }; ``` ```jsx // Exporting MyPlanScreen allows us to import and use MyPlanScreen in the Navigator export { MyPlanScreen } from './MyPlanScreen'; ``` Then in **index.ts** , add the screen to the list of exports: ```jsx export { CoverageScreen } from './CoverageScreen'; export { MyPlanScreen } from './MyPlanScreen'; ``` ## Step 4: Create a Stack Navigator Next, we will create the stack navigator. In **MyPlanNavigator.tsx**, insert the following code: ```jsx import React from 'react'; import { Pressable } from 'react-native'; import { createStackNavigator, IconSymbol } from '@uhg-abyss/mobile'; import { MyPlanScreen, CoverageScreen } from '@/screens/MyPlan'; const MyPlanStack = createStackNavigator(); export const MyPlanNavigator = () => { return ( ( ), }} /> ); }; ``` ## Step 5: Add MyPlan Tab Next, we will add the MyPlan Stack Navigator, to the main tab bar. Navigate into **products/mobile/App.tsx**. There should already be two existing `Tab.Screen` components, representing the Home and Menu tabs. Let's place a new `Tab.Screen` in between the existing tabs so that the My Plan tab shows 2nd on the tab bar. First import the `MyPlanNavigator`. ```jsx import { MyPlanNavigator } from '@/navigation/MyPlanNavigator'; ``` Then add the MyPlan tab ```jsx ... ( ), }} name="HomeTab" component={HomeNavigator} /> ( ), }} name="MyPlanTab" component={MyPlanNavigator} /> ( ), }} name="MenuTab" component={MenuNavigator} /> ... ``` ## Step 6: Create Navigation Types Navigate to **products/mobile/src/navigation** and open **"navigation.ts"**. Let's create a new `type` to represent the two screens' parameters. The `MyPlan` screen has no parameters and the `Coverage` screen has a benefits array of strings. In **"navigation.ts"**, insert the following code. ```ts type MyPlanTabParamList = { MyPlan: undefined; Coverage: { benefits: string[] }; }; ``` Now, we can create a `type` for the navigation and route props of each screen. In **"navigation.ts"**, insert the the following code. ```ts export type MyPlanTabScreenProps = CompositeScreenProps< BottomTabScreenProps, RootStackScreenProps >; ``` Next, let's navigate back to **"MyPlanScreen.tsx"** and add the `MyPlanTabScreenProps` type in the function's parameter. ```jsx import { MyPlanTabScreenProps } from '@/types/navigation'; export const MyPlanScreen = ({ navigation, route, }: MyPlanTabScreenProps<'MyPlan'>) => { // rest of the code }; ``` We can do the same thing in **"Coverage.tsx"**. ```jsx import { MyPlanTabScreenProps } from '@/types/navigation'; export const CoverageScreen = ({ navigation, route, }: MyPlanTabScreenProps<'Coverage'>) => { // rest of the code }; ```
Great job, you have successfully completed page routing! --- id: custom-themes title: Custom Themes --- --- **Note:**
We would appreciate any feedback on our tutorial guide. If you are stuck at any time, make sure to contact the Abyss Admiral assigned to your team. If they cannot help, send a help request on our [Contact Page](/mobile/contact-us/). --- ### Step 1: Create a Theme screen In Visual Studio Code, open the **my-new-app** project. From here, navigate to **products/mobile/src/screens** and create a new folder named `ThemeScreen`. Within this new folder, create two new files named `index.ts` and `ThemeScreen.tsx`. ```txt products └── mobile └── src └── screens └── ThemeScreen ├── index.ts └── ThemeScreen.tsx ``` ### Step 2: Choosing A Theme In **ThemeScreen.tsx**, we are building our theme using the pre-defined `UHC` theme. You can choose between the different brands shown below: Abyss offers several pre-defined themes: `UHC`, `UHG`, and `Optum`. You can find these themes [here](https://github.com/uhc-tech/abyss/tree/main/packages/abyss-mobile/src/tools/theme/variants). While building **ThemeScreen.tsx**, you can choose any of the brands demonstrated below. --- [UHC Theme](https://github.com/uhc-tech/abyss/blob/main/packages/abyss-mobile/src/tools/theme/variants/uhc.js) ```jsx const theme = createTheme('uhc'); ``` [UHG Theme](https://github.com/uhc-tech/abyss/blob/main/packages/abyss-mobile/src/tools/theme/variants/uhg.js) ```jsx const theme = createTheme('uhg'); ``` [Optum Theme](https://github.com/uhc-tech/abyss/blob/main/packages/abyss-mobile/src/tools/theme/variants/optum.js) ```jsx const theme = createTheme('optum'); ``` ### Step 3: Customizing your Theme There are multiple ways to customize and style your application. In this guide, we will focus on color and font customization. To customize your brand's default themes to align with your product's branding, you can include a configuration object to add or override variables within the theme by adding values inside the **createTheme** function as shown below. You can learn more about the **createTheme** function [here](/mobile/tools/create-theme). ```jsx const theme = createTheme('uhc', { // Add custom colors theme: { colors: { customColor: 'purple', }, // Add custom fonts fonts: { customFont: 'UHCSerif', }, }, }); ``` ### Step 4: Viewing Theme To view some of your theme updates, import some Abyss components into your project and see how they look! ```jsx sandbox () => { // Add Custom Theme const theme = createTheme('uhc', { theme: { colors: { customColor: '#8943fe', }, fonts: { customFont: 'UHCSerif', }, }, }); return ( //Define Custom Theme in theme prop Standard Text Customized Text ); }; ```
Great job, you have successfully customized a theme! --- id: styled-components title: Styled Components --- --- **Note:**
We would appreciate any feedback on our tutorial guide. If you are stuck at any time, make sure to contact the Abyss Admiral assigned to your team. If they cannot help, send a help request on our [Contact Page](/mobile/contact-us/). --- ### Step 1: Create a Styled Screen In Visual Studio Code, open the **my-new-app** project. From here, navigate to **products/mobile/src/screens** and create a new folder named `StyledScreen`. Within this new folder, create two new files named `index.ts` and `StyledScreen.tsx`. ```txt products └── mobile └── src └── screens └── StyledScreen ├── index.ts └── StyledScreen.tsx ``` ### Step 2: Creating Styled Components You can use the **styled** tool to style existing components or create new styled components. To learn more, check out our [styled](/mobile/tools/styled/) function. In your **StyledScreen.tsx** file, add the following import statements: ```jsx import React from 'react'; import { IconBrand, Card, Text, styled } from '@uhg-abyss/mobile'; ``` We will create an information box to demonstrate how to work with styled-components. After your import statements, insert the following code: ```jsx const StyledCard = styled(Card, { padding: '$sm', flexDirection: 'row', alignItems: 'center', }); const StyledText = styled('Text', { fontWeight: '$semibold', }); ``` ### Step 3: Rendering Styled Components This component uses the **StyledCard**, and **StyledText** components we created previously. There are other features available in the [styled](/mobile/tools/styled/) function to customize and edit your components to best fit your product's custom designs. In the **StyledScreen.tsx** file, add the following code to your **StyledScreen** component: ```jsx export const StyledScreen = () => { return ( Average cost in your area: $980 ); }; ``` ### Step 4: Viewing Styled Components At the end of this tutorial, the code in your **StyledScreen.tsx** file should look like this: ```jsx import React from 'react'; import { styled, IconBrand, Card, Text } from '@uhg-abyss/mobile'; const StyledCard = styled(Card, { padding: '$sm', flexDirection: 'row', alignItems: 'center', }); const StyledText = styled('Text', { fontWeight: '$semibold', }); const StyledScreen = () => { return ( Average cost in your area: $980 ); }; ``` On your device, your StyledScreen should look like this: ```jsx live // Create styled Card const StyledCard = styled(Card, { padding: '$sm', flexDirection: 'row', alignItems: 'center', }); // Create styled Text const StyledText = styled('Text', { fontWeight: '$semibold', }); const StyledScreen = () => { return ( Average cost in your area: $980 ); }; render(() => { return ; }); ```
Great job, you have successfully styled components! --- id: versioning-guide title: Versioning Guide --- ## Overview Stability ensures that reusable components and libraries, tutorials, tools, and learned practices don't become obsolete unexpectedly. Stability is essential for the ecosystem around Abyss to thrive. This document contains the practices that are followed to provide you with a leading-edge UI library, balanced with stability, ensuring that future changes are always introduced in a predictable way. ## Semantic Versioning Abyss follows Semantic Versioning 2.0.0. Abyss version numbers have three parts: major.minor.patch. The version number is incremented based on the level of change included in the release. - **Major releases** contain significant new features, some but minimal developer assistance is expected during the update. When updating to a new major release, you may need to run update scripts, refactor code, run additional tests, and learn new APIs. - **Minor releases** contain important new features. Minor releases should be fully backward-compatible; no developer assistance is expected during update, but you can optionally modify your apps and libraries to begin using new APIs, features, and capabilities that were added in the release. - **Patch releases** are low risk, contain bug fixes and small new features. No developer assistance is expected during update. ## Release Frequency A regular schedule of releases helps you plan and coordinate your updates with the continuing evolution of Abyss. In general, you can expect the following release cycle: - A **major** release typically every year for major changes. - A **minor** releases every two weeks after each sprint. - A **patch** release at any time for urgent bugfixes. ## Deprecation Practices Sometimes **"breaking changes"**, such as the removal of support for select APIs and features, are necessary. To make these transitions as easy as possible: - The number of breaking changes is minimized, and migration tools provided when possible. - The deprecation policy described below is followed, so that you have time to update your apps to the latest APIs and best practices. ## Deprecation Policy - Deprecated features are announced in the changelog, and when possible, with warnings at runtime. - When a deprecation is announced, recommended update path is provided. - Existing use of a stable API during the deprecation period is supported, so your code will keep working during that period. - Peer dependency updates (React) that require changes to your apps are only made in a major release. ## Tested React Native versions --- id: workspace-setup title: Workspace Setup pagination_prev: null --- ## Overview Developing modern JavaScript applications requires efficient, powerful, and extensible tooling. Consistency across developer machines is a priority when collaborating across highly distributed teams. The following is a guide for installing the preferred environment for JS development. ![workspace setup](/img/graphics/workspace.svg) ## Secure Groups Visit secure.uhc.com to request permissions groups: - **github_users**: To access github.com - **Mac_Admin**: To install software for macOS users only ## VSCode Editor To write code for UI projects, it is **highly recommended** that you download and install Visual Studio Code. ![Visual Studio Code](https://code.visualstudio.com/assets/home/home-screenshot-mac-lg-2x.png) ## VSCode Extensions Recommended extensions will be suggested to you when you visit the VSCode Marketplace. - ESLint - code syntax validator ESLint is a JavaScript linting tool which is used for automatically detecting incorrect patterns found in ECMAScript/JavaScript code. It is used with the purpose of improving code quality, making code more consistent, and avoiding bugs. Rules can be configured to look for all kinds of discrepancies due to discouraged code patterns or formatting. Running a Linting tool over the source code helps to improve the quality and readability of the code. - Prettier - code formatter Prettier is very popular because it improves code readability and makes the coding style consistent for teams. Developers are more likely to adopt a standard rather than writing their own code style from scratch, so tools like Prettier will make your code look good without you ever having to dabble in the formatting. ## System Essentials ### Xcode - Xcode Command Line Tools (Mac Only) `xcode-select` contains necessary utilities for software development on macOS. Xcode's simulator will be used for viewing your app in an iOS environment. ```bash xcode-select --install ```
**_After install, exit and restart Terminal (CMD + Q)_** ``` $ xcode-select --version ``` ### Android Studio Install Android Studio. The Virtual Device Manager will be use for viewing your app in an Android environment. ### Additional Tools - oh-my-zsh >= 5.3.0 (optional) `zsh` is an optional upgrade to the native shell which provides a delightful terminal experience. ```bash sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" ```
**_After install, exit and restart Terminal (CMD + Q)_** ```bash omz version ```
--- - node >= 16.0.0 `nvm` is a great tool for installing and upgrading versions of Node on your system. ```bash curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash ```
**_After install, exit and restart Terminal (CMD + Q)_** ```bash nvm --version nvm install 16 && nvm use 16 && nvm alias default 16 ```
**_After install, exit and restart Terminal (CMD + Q)_** ``` $ npm --version $ npm config set registry https://repo1.uhc.com/artifactory/api/npm/npm-virtual ```
--- - git >= 2.0.0 `git` is a universal version control system for working collaboratively and efficiently. ```bash git config --global user.id "YOUR_MS_ID" git config --global user.email "YOUR_EMAIL@optum.com" ``` ## Git Branch Names Naming the branch you're working on helps repository maintainers understand the changes being made when the PR is opened. Using consistent branch name prefixes also allows build tools to automatically categorize the branches using labels. Branch names should be all lowercase (with the exception of US and DE) and include hyphens between words. All branches are divided into four groups: - **story/#######** - Changes associated with a User Story, use the unique 7-digit number from Rally followed by a task description. - **defect/#######** - Changes associated with a Defect, use the unique 7-digit number from Rally followed by a task description. - **refactor/** - Changes to the repo that aren't documented in Rally are considered refactors, so use the task portion to add detail to your branch name. - **release/** - Used specifically by build tools, this branch name is exclusive to release notes and documentation leading up to a new release. Examples: ``` git checkout -b story/US2434515-developer-toolkit git checkout -b defect/DE308703-button-accessibility git checkout -b refactor/select-list-multi-docs git checkout -b story/US1533842-use-loading-overlay ```
Branch Name Rules: - Branch prefix must start with **story**, **defect**, **refactor**, or **release** - Branch name must be only **lowercase letters, numbers, and hypens** - **US###** and **DE###** are valid character exceptions --- id: use-animation category: Utilities title: useAnimation description: Use to create standardized animation types for Abyss Mobile components design: https://www.figma.com/proto/wCMblLsq9TxAQvKzY3EfCt/branch/c6ve0gNrTGpdwBIuJehC3n/Abyss-Mobile?page-id=41951%3A175&type=design&node-id=41951-176&viewport=741%2C524%2C0.06&t=33q3lqs409fRYj6B-1&scaling=scale-down-width&starting-point-node-id=41951%3A176&show-proto-sidebar=1 --- ```jsx import { useAnimation } from '@uhg-abyss/mobile'; ``` ## Usage `useAnimation` hook standardizes the animation types for Abyss components and simplifies additional customization. The hook returns `animate`, `value`, `interpolations`, as well as other Animated functions used throughout Abyss. ```jsx export const useAnimation = (value: number, config: AnimationConfig) => { ... return { animate, value: animatedValue, interpolations, createAnimation, add, subtract, multiply, divide, delay, parallel, loop, sequence, }; }; ``` ## Animate The `animate` function requires a toValue be passed in, with the option for overrides and a callback function. ```jsx const animate = ( toValue: number, configOverride?: Partial, callback?: Animated.EndCallback ) => { return createAnimation(toValue, configOverride).start(callback); }; ``` ```jsx live () => { const { animate, value, interpolations } = useAnimation(1, { easing: 'gentle', interpolations: { background: { inputRange: [0.95, 1], outputRange: ['$primary1', '$info1'], tokenType: 'colors', }, }, }); const AnimatedButton = styled(Animated.createAnimatedComponent(Pressable), { justifyContent: 'center', alignItems: 'center', padding: '$md', borderRadius: 100, alignSelf: 'center', }); return ( { animate(0.95); }} onPressOut={() => { animate(1); }} style={{ transform: [{ scale: value }], background: interpolations.background, }} > Press Me ); }; ``` ### Overrides By default the animation type is set to `'timing'`. Additional types supported are `'spring'` and `'instant'` with the following animation config open to override: ```jsx interface AnimationConfig extends BaseAnimationConfig { type?: 'spring' | 'timing' | 'instant'; interpolations?: Record; delay?: number | undefined; // timing easing?: Easing; duration?: number | undefined; // spring overshootClamping?: boolean | undefined; restDisplacementThreshold?: number | undefined; restSpeedThreshold?: number | undefined; velocity?: number | { x: number, y: number } | undefined; bounciness?: number | undefined; speed?: number | undefined; tension?: number | undefined; friction?: number | undefined; stiffness?: number | undefined; mass?: number | undefined; damping?: number | undefined; } ``` ## Interpolations The `interpolations` prop can take in multiple interpolation objects to be used on the same animated value. ```jsx const { value, animate, interpolations } = useAnimation(1, { easing: 'gentle', interpolations:{ backgroundColor: { inputRange: [0.95, 1], outputRange: ['$info1', '$primary1'] tokenType: 'colors', }, opacity: { inputRange: [0.95, 1], outputRange: [0, 1] }, } }); ``` ```jsx live () => { const { animate, value, interpolations } = useAnimation(1, { easing: 'gentle', interpolations: { background: { inputRange: [0.95, 1], outputRange: ['#00BED5', '$primary1'], tokenType: 'colors', }, opacity: { inputRange: [0.95, 1], outputRange: [0.8, 1], }, }, }); const Label = styled(Animated.Text, { color: '$white', fontSize: '$xs', lineHeight: '$xs', fontWeight: '$bold', marginTop: '$xs', }); const AnimatedTabButton = styled( Animated.createAnimatedComponent(Pressable), { justifyContent: 'center', alignItems: 'center', padding: '$md', cornerRadius: 10, alignSelf: 'center', } ); return ( { animate(0.95); }} onPressOut={() => { animate(1); }} style={{ transform: [{ scale: value }], background: interpolations.background, }} > ); }; ``` ## Additional Functionality Other Animated methods currently used throughout Abyss components can also be returned. ```jsx const parallel = ( animations: Animated.CompositeAnimation[], parallelConfig?: Animated.ParallelConfig ) => { return Animated.parallel(animations, parallelConfig); }; const loop = ( animation: Animated.CompositeAnimation, loopConfig?: Animated.LoopAnimationConfig ) => { return Animated.loop(animation, loopConfig); }; const sequence = (animations: Animated.CompositeAnimation[]) => { return Animated.sequence(animations); }; const delay = (time: number) => { return Animated.delay(time); }; const add = (num: number) => { return Animated.add(animatedValue, num); }; const subtract = (num: number) => { return Animated.subtract(animatedValue, num); }; const divide = (num: number) => { return Animated.divide(animatedValue, num); }; const multiply = (num: number) => { return Animated.multiply(animatedValue, num); }; ``` ## Accessibility Integration The `useAnimation` hook has built-in integration with the [`useReduceMotion`](/mobile/hooks/use-reduce-motion) hook. This ensures that animated components automatically respect the user's "Reduce Motion" accessibility setting without requiring additional handling. When "Reduce Motion" is enabled, the `useAnimation` hook adjusts the animation type to `'instant'`, minimizing motion effects. This makes it easier to create accessible components that align with user preferences. --- id: use-device-orientation category: Utilities title: useDeviceOrientation description: Used to retrieve the orientation of the mobile device --- ```jsx import { useDeviceOrientation } from '@uhg-abyss/mobile'; ``` ## Usage `useDeviceOrientation` measures the width and height of the screen and returns `'landscape'` or `'portrait'`. ```jsx const orientation = useDeviceOrientation(); const iconSize = orientation === 'landscape' ? 20 : 24; return ; ``` --- id: use-form category: State Management title: useForm description: useForm is a hook for defining, validating and submitting forms. sourceIsTS: true --- ```jsx import { useForm } from '@uhg-abyss/mobile'; ``` ## Usage The `useForm` hook is used to define, validate, and submit forms in Abyss. Use `useForm` along with the [FormProvider](/mobile/ui/form-provider) component in order to better manage your forms and fully utilize the capabilities of form management within Abyss. Abyss components that can be used with useForm have a `model` prop that is used to bind the component to the form state. Validations can be defined on each component using the `validations` prop, which accepts an object with validation rules. If a component fails validation, it will display an error message based on the validation rules defined. When a form is successfully submitted, the function passed to the `onSubmit` prop will be called with the form data. ```jsx live () => { const form = useForm(); const onSubmit = (data) => { // Do something on submit alert(`FormData: ${JSON.stringify(data)}`); }; return ( ); }; ``` ## Default Values The defaultValues prop populates the entire form with default values. It supports both synchronous and asynchronous assignments of default values. ```jsx live () => { // Default Values Passed into useForm const form = useForm({ defaultValues: { firstName: 'John', lastName: 'Doe', }, }); const onSubmit = (data) => { alert(`FormData: ${JSON.stringify(data)}`); }; return ( ); }; ``` ## Form State This object contains information about the form state. If you want to subscribe to formState via useEffect, make sure that you place the entire formState in the optional array. ```jsx const form = useForm(); const { errors, // An object with field errors isDirty, // Set to true after the user modifies any of the inputs. isValid, // Set to true if the form doesn't have any errors. isValidating, // Set to true during validation. isSubmitting, // true if the form is currently being submitted; false if otherwise. isSubmitted, // Set to true after the form is submitted. isSubmitSuccessful, // Indicate the form was successfully submitted without any Promise rejection or Error being thrown within the handleSubmit callback. submitCount, // Number of times the form was submitted. touchedFields, // An object containing all the inputs the user has interacted with. dirtyFields, // An object with the user-modified fields. } = form.formState; ``` ## Watch This will watch specified inputs and return their values. It is useful for determining what to render. ```jsx live () => { const form = useForm(); const onSubmit = (data) => { alert(`FormData: ${JSON.stringify(data)}`); }; // Watch one field const WatchField = form.watch('firstName'); // Target specific fields by their names const WatchFields = form.watch(['firstName', 'lastName']); // Watch everything by passing no arguments const WatchAllFields = form.watch(); return ( Watch One Field: {JSON.stringify(WatchField)} Watch Multiple Fields: {JSON.stringify(WatchFields)} Watch All Fields: {JSON.stringify(WatchAllFields)} ); }; ``` ## Handle Submit This function will receive the form data if form validation is successful. ```jsx live () => { const form = useForm({ defaultValues: { firstName: 'John', lastName: 'Doe', }, }); const onSubmit = (data, e) => alert('onSubmit'); const onError = (errors, e) => alert('onError'); return ( ); }; ``` ## Validate Model This function will receive the model data if form validation is successful. ```jsx live () => { const form = useForm({ defaultValues: { firstName: 'John', }, }); const handleValidateFirst = () => { form.validate( 'firstName', (data) => { alert(`FormData: ${JSON.stringify(data)}`); }, (error) => { delete error.ref; alert(`Error: ${JSON.stringify(error)}`); } ); }; const handleValidateLast = () => { form.validate( 'lastName', (data) => { alert(`FormData: ${JSON.stringify(data)}`); }, (error) => { delete error.ref; alert(`Error: ${JSON.stringify(error)}`); } ); }; return ( ); }; ``` ## Set Error The function allows you to manually set one or more errors. ```jsx live () => { const form = useForm({ defaultValues: { firstName: 'John', lastName: 'Doe', }, }); // Set single error const setSingleError = () => { form.setError('firstName', { type: 'manual', message: 'There is an error with your name!', }); }; // Set multiple errors const setMultipleErrors = () => { [ { type: 'manual', name: 'firstName', message: 'Check first name', }, { type: 'manual', name: 'lastName', message: 'Check last name', }, ].forEach(({ name, type, message }) => { form.setError(name, { type, message }); }); }; // Set error for single field errors React.useEffect(() => { form.setError('firstName', { types: { required: 'This is required', minLength: 'This is minLength', }, }); }, []); return ( ); }; ``` ## Clear Errors This function can manually clear errors in the form. ```jsx live () => { const form = useForm({ defaultValues: { firstName: 'John', lastName: 'Doe', phone: '555-555-5555', }, }); const resetErrors = () => { [ { type: 'manual', name: 'firstName', message: 'Required', }, { type: 'manual', name: 'lastName', message: 'Required', }, { type: 'manual', name: 'phone', message: 'Required', }, ].forEach(({ name, type, message }) => { form.setError(name, { type, message }); }); }; // Clear single error const clearSingleErrors = () => { form.clearErrors('firstName'); }; // Clear multiple errors const clearMultipleErrors = () => { form.clearErrors(['firstName', 'lastName']); }; // Clear all errors const clearAllErrors = () => { form.clearErrors(); }; return ( ); }; ``` ## Get Values An optimized helper for reading form values. The difference between watch and getValues is that getValues will not trigger re-renders or subscribe to input changes. ```jsx live () => { const form = useForm({ defaultValues: { firstName: 'John', lastName: 'Doe', phone: '555-555-5555', }, }); // Read an individual field value by name const singleValue = form.getValues('firstName'); // Read multiple fields by name const multipleValues = form.getValues(['firstName', 'lastName']); // Reads all form values const allValues = form.getValues(); return (

Single Value: {JSON.stringify(singleValue)}

Multiple Values: {JSON.stringify(multipleValues)}

All Values: {JSON.stringify(allValues)}

); }; ``` ## Trigger Manually triggers form or input validation. This method is also useful when you have dependent validation (input validation depends on another input's value). ```jsx live () => { const form = useForm(); // Trigger one input to validate const triggerSingle = () => { form.trigger('firstName'); }; // Trigger multiple inputs to validate const triggerMultiple = () => { form.trigger(['firstName', 'lastName']); }; // Trigger entire form to validate const triggerAll = () => { form.trigger(); }; const clearErrors = () => { form.clearErrors(); }; return ( ); }; ``` ## Cross-Field Validation Example ```jsx live () => { const form = useForm(); const onSubmit = (data) => { console.log('data', data); }; return ( { const checkValue = form.getValues('middleName-check'); if (!checkValue && !v) { return 'Required'; } }, }} showValidations={false} /> { form.trigger('middleName'); }} /> ); }; ``` ## Additional Documentation `@uhg-abyss/mobile/hooks/useForm` is a built on top of the `useForm` hook from React Form Hook. **Note:** You should be using Abyss's `useForm` hook when using Abyss components and **not** react hook forms. --- id: use-reduce-motion category: Utilities title: useReduceMotion description: Used within animated components to respect a user's accessibility preferences. --- ```jsx import { useReduceMotion } from '@uhg-abyss/mobile'; ``` ## Usage The `useReduceMotion` hook is a custom hook designed to determine whether the user has enabled the "Reduce Motion" accessibility setting on their device. This setting is often used by individuals who prefer to minimize animations and motion effects for accessibility or comfort reasons. The hook returns an object `{reducedMotionEnabled}` with the boolean value: - `true`: "Reduce Motion" is enabled. - `false`: "Reduce Motion" is disabled. When reduce motion is enabled, this hook can be used to adjust animation behavior. ```jsx live const AnimatedContainer = styled('Animated.View', { padding: '$lg', backgroundColor: '$primary1', width: 100, height: 100, margin: '$lg', }); const SequentialAnimation = () => { const { reducedMotionEnabled } = useReduceMotion(); const animatedValues = useRef([ new Animated.Value(1), new Animated.Value(1), new Animated.Value(1), ]).current; useEffect(() => { const createAnimation = (index) => Animated.sequence([ Animated.timing(animatedValues[index], { toValue: 1.4, duration: 500, useNativeDriver: false, }), Animated.timing(animatedValues[index], { toValue: 1, duration: 500, useNativeDriver: false, }), ]); const loopAnimation = Animated.loop( Animated.stagger(250, [ createAnimation(0), createAnimation(1), createAnimation(2), ]) ); loopAnimation.start(); return () => loopAnimation.stop(); }, []); return ( {animatedValues.map((animatedValue, index) => ( ))} ); }; render(() => { return ; }); ``` ## Enabling Reduce Motion by Device ### iOS - Open the Settings app - Select Accessibility - Choose Motion - Toggle the switch next to Reduce Motion to on ### Android - Open the Settings app - Select Accessibility - Depending on your Android version, look for Remove Animations (or similar) ### Windows - Open Settings - Select Accessibility - Go to Visual Effects - Toggle Animation Effects to off ### Mac OS - Open System Settings - Select Accessibility - Choose Display - Toggle Reduce Motion to on ## Additional Notes - The hook listens for changes to the "Reduce Motion" setting and updates its value dynamically. - The [`useAnimation`](/mobile/hooks/use-animation) hook integrates `useReduceMotion` directly, ensuring that animated components automatically respect the user's motion preferences without requiring additional handling. - Use this hook to create a more inclusive and accessible experience for users who prefer minimal motion effects. Please refer to accessibility guidelines for animations when creating new components. - WCAG standards: - iOS - Android --- id: use-set-focus category: Utilities title: useSetFocus description: Used to set accessibility focus on a specified element. --- ```jsx import { useSetFocus } from '@uhg-abyss/mobile'; ``` ## Usage The hook returns a function that consumes a ref object. Simply call said function to set the screen reader focus. ```jsx const setFocus = useSetFocus(); const myRef = useRef(null); return ( <> Link to be focused ); ``` ## Example In the example below, Android's Talkback can be seen focusing the bottom text when the `Focus Text` button is pressed. --- id: use-style-sheet category: Styling title: useStyleSheet description: Used to parse styles from a StyleSheet --- ```jsx import { useStyleSheet } from '@uhg-abyss/mobile'; ``` The `useStyleSheet` hook helps to parse the additional functionality from the Abyss [StyleSheet](/mobile/ui/style-sheet). ## Usage ```tsx useStyleSheet(styles: object): object ``` Take a look at StyleSheet below: ```jsx const styles = StyleSheet.create({ container: { padding: '$xs * 4px', margin: '$fontScale', }, label: { color: '$gray4', fontWeight: '$bold', fontSize: '$lg', marginVertical: '$md * 2', fontFamily: '$heading', }, box: { backgroundColor: '$interactive1', borderColor: '$error1', borderRadius: '$md * $sm', borderWidth: 4, width: '6rem', height: '48px * 3', marginBottom: '32px - 0.75rem', '@media (min-width: 767px)': { width: '12rem', }, }, }); ``` There's a lot of code that is unfamiliar to the normal StyleSheet. Above, there are _**media queries**_, _**tokens**_, _**operations**_, _**rem values**_, and _**pixel values**_, which normally would not be able to be parsed by React Native core component. This is where the `useStyleSheet` hook comes in. By using the hook, we can parse these value into value that the core component can understand. ```jsx live const themedStyles = StyleSheet.create({ container: { padding: '$xs * 4px', margin: '$fontScale', }, label: { color: '$gray4', fontWeight: '$bold', fontSize: '$lg', marginVertical: '$md * 2', fontFamily: '$heading', }, box: { backgroundColor: '$interactive1', borderColor: '$error1', borderRadius: '$md * $sm', borderWidth: 4, width: '6rem', height: '48px * 3', marginBottom: '32px - 0.75rem', '@media (min-width: 767px)': { width: '12rem', }, }, }); render(() => { const styles = useStyleSheet(themedStyles); return ( Parsed Styles {JSON.stringify(styles, null, 4)} Original Styles {JSON.stringify(themedStyles, null, 4)} ); }); ``` --- id: use-token category: Utilities title: useToken description: Used to get values mapped to tokens. --- ```jsx import { useToken } from '@uhg-abyss/mobile'; ``` This hook returns a function that is used to get the style value associated with a token defined in the theme. Use this whenever you want to pass in a prop with the value of a token string instead of the associated token value. ## Properties ```typescript type TokenKey = 'colors' | 'space' | 'sizes' | 'fontSizes' | 'lineHeights' | 'fontWeights' | 'fonts' | 'radii'; interface TokenConfig { retain?: boolean; }; useToken(key: TokenKey, config?: TokenConfig) ``` ## Usage The `key` argument corresponds to the upper level category inside the theme. Start with defining a function and passing it the string of the token key. You can then use that function to pass in your token element and get the associated value. A hex value can also be passed in as a `token` and it will be returned as is|unless set not to. ```jsx const theme = { theme: { colors: {...}, space: {...}, fontSizes: {...}, fonts: {...}, }, }; const getColorToken = useToken('colors'); const color = getColorToken('$primary1'); ``` ## Example ```jsx live () => { const getColorToken = useToken('colors'); const color = getColorToken('$interactive3'); const getSpaceToken = useToken('space'); const space = getSpaceToken('$md'); return ( Abyss Design System ); }; ``` ### Defaults By default, the `useToken` hook uses the passed in value if the token is not found/is invalid. This allows it to take hex and string values and return them as is. Using the `config` parameter, you can pass in `{retain: false}` to require tokenized values. ```jsx live () => { const getSpaceToken = useToken('space'); const space = getSpaceToken('$md'); const getColorToken = useToken('colors'); const color = getColorToken('#D9E9FA'); const color2 = getColorToken('gray'); const getColorToken2 = useToken('colors', { retain: false }); const color3 = getColorToken2('#D9E9FA'); return ( Abyss Design System Abyss Design System Abyss Design System ); }; ``` --- id: use-translate category: Utilities title: useTranslate description: Used to get the translated string from the i18n object. --- ```jsx import { useTranslate } from '@uhg-abyss/mobile/hooks/useTranslate'; ``` The `useTranslate` hook is used to get the translated string from the Abyss [i18n](/mobile/utilities/i18n) object. ## Usage ```typescript interface I18nTranslate { t: (key: string, replacements?: object) => string; i18n: object; } useTranslate(key: string, replacements?: object): I18nTranslate ``` The `key` argument corresponds to the key in the i18n object. The `replacements` argument is an object that contains the values to replace in the translated string. Let's use an example to illustrate how to use the `useTranslate` hook. In the [TextField](/mobile/ui/text-field) component, we have a text block that displays the remaining characters available to be typed in the text field. We can get that value with the key `TextField.charactersRemaining`. ```jsx live-expanded () => { const { t } = useTranslate(); return {t('TextField.charactersRemaining')}; }; ``` If we want to replace the value of the remaining characters, we can pass in the `replacements` object with the key `count`. Replacement values should always appear in double curly braces, (e.g. `{{count}}`). ```jsx live-expanded () => { const { t } = useTranslate(); return {t('TextField.charactersRemaining', { count: 10 })}; }; ``` We can also use the `useTranslate` hook to get the translated string from the i18n object. ```jsx live-expanded () => { const { i18n } = useTranslate(); return {i18n.TextField.charactersRemaining}; }; ``` --- id: about slug: /mobile/about title: About Abyss --- ## What is Abyss? ## How Abyss works ## We support adoption ## Guiding Principles ## We Maintain Assets ## The Abyss Team --- id: contact-us slug: /mobile/contact-us title: Contact Us hide_table_of_contents: true --- ## Support ## Requests --- id: disclaimer slug: /mobile/disclaimer title: Mobile Components Disclaimer --- As a note, these components were developed for use in a mobile application environment. For your convenience, we created documentation and interactive examples on our Abyss site. We have tried our best to accurately show the functionality and style of each component on these pages, but you may notice some differences and limitations as the optimal platform for viewing and interacting with these components is a mobile device. A few examples are described below: - [Date Input](/mobile/ui/date-input) requires native code, so this component is only supported on iOS and Android. There are no interactive examples in the documentation; only recordings of each feature are available. To fully interact with the date and time pickers you will need to import the component within a mobile development environment. - [Modal](/mobile/ui/modal), and any component that uses Modal, takes up the entire browser window when opened. While it is intended to take up the entire phone screen as well, the content on the modal will fill much more space when used on a mobile device than it does in the browser window. --- id: overview slug: /mobile/overview title: Overview hide_table_of_contents: true --- ## --- id: product-roadmap slug: /mobile/product-roadmap title: Product Roadmap --- ## For 2024, please see details below that outline the Goals/Themes, Solutions Capabilities, and PI Plans for the Abyss Design System. Please note, this page will be updated at the beginning of each PI. ## Abyss Business Objectives for 2024 ## Solution Capabilities To achieve greater adoption and consistency, below are the overarching Solution Capabilities for Abyss that outline some more specific initiatives the team is working toward. For additional details, check out our Aha Board on Solution Capabilities. ## Abyss Mobile Development As we continue work to build out Abyss Mobile, below is a breakdown of the Solution Capabilities and high-level PI plan for the planned work. To see more specific details on the status of Abyss Mobile, please review our UHC Design System Status and Migration Plan PowerPoint. ### PI Plan - Further breaking down our Solution Capabilities, below is our outline for current, upcoming PI Plans and the key features being addressed in each of the 5 sprints. - For additional details on items that will fall under each of these Solution Capabilities, check out our Aha Feature board ** Note: The items below are subject to change as priorities shift throughout each sprint. ** ## Abyss Mobile Design ### PI Plan - Further breaking down our Solution Capabilities, below is our outline for current, upcoming PI Plans and the key features being addressed in each of the 5 sprints. ** Note: The items below are subject to change as priorities shift throughout each sprint. ** --- id: releases slug: /mobile/releases title: Releases --- --- id: accessibility title: Accessibility --- ## Overview ## Interactive components ```jsx sandbox { component: 'Button', inputs: [ { prop: 'variant', type: 'select', options: [ { label: 'solid', value: 'solid' }, { label: 'outline', value: 'outline' }, { label: 'ghost', value: 'ghost' }, ], }, { prop: 'size', type: 'string', }, { prop: 'children', type: 'string', }, { prop: 'isDisabled', type: 'boolean' }, ], } ``` ## Color Contrast ```jsx render ``` ## Icons ### Meaningful or Control Icons If the icon is being used in a setting where it is the only element providing meaning, then that same meaning should be conveyed to screen reader users. The below implementation provides examples of situations in which the property `isScreenReadable` should be set to true and the `title` property is required and should describe the purpose of the image. Example 1: An alert icon is used to convey a sense of urgency; there is adjacent text (“There is a data outage”) but the text doesn't include any words that convey urgency. So, in this case, the icon should have a text alternative such as “Alert” or “Warning”. ```jsx live
There is a data outage
```
Example 2: An “X” material icon is used as a close button on a modal dialog. There is no adjacent text, so the icon should have a text alternative of “close” or “close window”. ```jsx live ``` ### Decorative Icons If the icon is being used in a setting in which it is just a decorative element (which is the default case for icons), then the icon should be ignored by screen readers. The below implementation provides example of which situations would be classified as decorative. Since the default of `isScreenReadable` is set to false no specific changes need to be made for decorative icons. Example 1: An alert icon is used next to an urgent message and the word “Alert” is included in the adjacent text. In this case, the icon becomes decorative in nature and should be ignored by screen readers. ```jsx live
Alert: There is a data outage
```
Example 2: An “X” material icon is used as a close button on a modal dialog; the word “Close” appears to the right of the button. In this case, the icon should be considered decorative and ignored by screen readers. ```jsx live
Close
``` ## Dynamic Type Abyss Mobile standards for dynamic types are as follows:
- We scale typography and icons in increments of 11.8%.
- Figma shows scaling examples at xxxLarge and AX5.
- We do not scale images, brand icons, or illustrations. - See [IconBrand](/mobile/brand/uhc/icon-brand/) and [IllustrationBrand](/mobile/brand/uhc/illustration-brand/) for more details.
- We also do not scale border-weight or border-radius.
- Some components, such as framing components and certain graphic elements, freeze at xxxLarge (3XL). ### Scale ```jsx render
Scale Percent
XL 110%
XXL 120%
XXXL 130%
AX1 179%
AX2 214%
AX3 264%
AX4 314%
AX5 357%
``` ## Additional Resources --- id: product-inclusion title: Product Inclusion --- ## Mission statement ## Product inclusion principles ## Product inclusion checklist ## Product inclusion audit tool ## Contact us --- id: product-resources title: Product Resources --- ## Overview ## How does Abyss work? ## Versioning ## Branding ## Accessibility ## Support --- id: create-theme category: Util title: createTheme description: Tool to create and modify themes. --- ```jsx import { createTheme } from '@uhg-abyss/mobile'; ``` The tool `createTheme` allows for the creation of preset themes and allows you to override those themes to fit your design needs. `createTheme` is used in conjunction with [ThemeProvider](/mobile/ui/theme-provider). ## Properties ```typescript createTheme( theme: string, override?: object, ): object; ``` ## Usage `createTheme` takes two arguments. The first argument is the choice of a default theme. There are currently 4 themes available: `"uhc"`, `"uhg"`, `"optum"`, and `"abyss"`. If no theme is chosen, it will fall back to the default `"abyss"` theme. The second argument is any overrides you wish to apply to the chosen base theme. ```jsx import { ThemeProvider, createTheme } from '@uhg-abyss/mobile'; import { AppRegistry } from 'react-native'; import { name as appName } from './app.json'; const themeOverride = { theme: { colors: {...}, space: {...}, fontSizes: {...}, fonts: {...}, fontWeights: {...}, lineHeights: {...}, letterSpacings: {...}, sizes: {...}, borderWidths: {...}, borderStyles: {...}, radii: {...}, shadows: {...}, zIndices: {...}, transitions: {...}, }, deprecatedOptumIcons: boolean, // see below for details }; const theme = createTheme('uhc', themeOverride); const App = () => { return ...; }; AppRegistry.registerComponent(appName, () => App); ``` ## Example Here is an example of a theme created with a custom override. A token named `customColor` will be added to the color tokens. ```jsx const theme = createTheme('uhc', { theme: { colors: { customColor: '#ff612b', // orange }, }, }); ``` ```jsx live () => { const theme = createTheme('uhc', { theme: { colors: { customColor: '#ff612b', }, }, }); return ( Sample Text ); }; ``` ## Important: Optum Icons Deprecation The Optum icons used in `IconBrand` have been updated in [Abyss v1.54.0](https://github.com/uhc-tech/abyss/releases/tag/v1.54.0). The new icons correspond to the icons available on the Optum Brand website. The old icons will remain available for the time being and can be enabled by setting the `deprecatedOptumIcons` property to `true` in the `themeOverride` object. ```jsx const themeOverride = { deprecatedOptumIcons: true, }; ``` --- id: create-bottom-tab-navigator category: Navigation title: createBottomTabNavigator description: A simple tab bar on the bottom of the screen that lets you switch between different routes. --- ```jsx import { createBottomTabNavigator } from '@uhg-abyss/mobile'; ``` The most common style of navigation in mobile apps is tab-based navigation. A simple tab bar on the bottom of the screen lets you switch between different routes. Routes are lazily initialized -- their screen components are not mounted until they are first focused. Calling the `createBottomTabNavigator` function creates a Tab object with `Screen` & `Navigator` properties. All tab screen must be wrapped around a Tab.Navigator object component. All screens must have a `name` and `component` passed in as props. ## Example ```jsx const Tab = createBottomTabNavigator(); const Screen = ({ route }) => { return ( This is the {route.name} page ); }; export default function App() { return ( ( ), }} /> ( ), }} /> ( ), }} /> ( ), }} /> ); } ``` ```jsx render:phone-dark () => { const [page, setPage] = useState('home'); const Container = styled('View', { flexGrow: 1, }); const Header = styled('View', { backgroundColor: '$primary1', width: '100%', paddingTop: 30, }); const HeaderContent = styled('View', { margin: '$sm', flexDirection: 'row', justifyContent: 'center', }); const Title = styled('Text', { color: '$white', fontWeight: '$semibold', textTransform: 'capitalize', }); const ContentArea = styled('View', { flexGrow: 1, alignItems: 'center', justifyContent: 'center', }); const TabBar = styled('View', { backgroundColor: '$primary1', height: 60, display: 'flex', color: '$white', paddingBottom: 16, paddingTop: 8, flexDirection: 'row', borderTopLeftRadius: 12, borderTopRightRadius: 12, }); const TabBarItem = styled('Pressable', { flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', }); const ContentText = styled('Text', {}); const TabLabel = styled('Text', { fontSize: '$2xs', color: '$white', variants: { active: { true: { fontWeight: '$bold' }, }, }, }); return (
{page.split('_').join(' ')}
{page === 'home' && This is the Home page} {page === 'my_plan' && ( This is the My Plan page )} {page === 'find_care' && ( This is the Find Care page )} {page === 'menu' && This is the Menu page} setPage('home')} > {({ pressed }) => { const active = page === 'home'; return ( <> Home ); }} setPage('my_plan')} > {({ pressed }) => { const active = page === 'my_plan'; return ( <> My Plan ); }} setPage('find_care')} > {({ pressed }) => { const active = page === 'find_care'; return ( <> Find Care ); }} setPage('menu')} > {({ pressed }) => { const active = page === 'menu'; return ( <> Menu ); }}
); }; ```
### Props The `Tab.Navigator` component accepts following props: #### `id` Optional unique ID for the navigator. This can be used with `navigation.getParent` to refer to this navigator in a child navigator. #### `initialRouteName` The name of the route to render on first load of the navigator. #### `screenOptions` Default options to use for the screens in the navigator. #### `backBehavior` This controls what happens when `goBack` is called in the navigator. This includes pressing the device's back button or back gesture on Android. It supports the following values: - `firstRoute` - return to the first screen defined in the navigator (default) - `initialRoute` - return to initial screen passed in `initialRouteName` prop, if not passed, defaults to the first screen - `order` - return to screen defined before the focused screen - `history` - return to last visited screen in the navigator; if the same screen is visited multiple times, the older entries are dropped from the history - `none` - do not handle back button #### `detachInactiveScreens` Boolean used to indicate whether inactive screens should be detached from the view hierarchy to save memory. This enables integration with [react-native-screens](https://github.com/software-mansion/react-native-screens). Defaults to `true`. #### `sceneContainerStyle` Style object for the component wrapping the screen content. #### `tabBar` Function that returns a React element to display as the tab bar. Note that you **cannot** use the `useNavigation` hook inside the `tabBar` since `useNavigation` is only available inside screens. You get a `navigation` prop for your `tabBar` which you can use instead: ### Options The following options can be used to configure the screens in the navigator. These can be specified under `screenOptions` prop of `Tab.navigator` or `options` prop of `Tab.Screen`. #### `title` Generic title that can be used as a fallback for `headerTitle` and `tabBarLabel`. #### `tabBarLabel` Title string of a tab displayed in the tab bar or a function that given `{ active: boolean, color: string }` returns a React.Node, to display in tab bar. When undefined, scene `title` is used. To hide, see `tabBarShowLabel`. #### `tabBarShowLabel` Whether the tab label should be visible. Defaults to `true`. #### `tabBarLabelPosition` Whether the label is shown below the icon or beside the icon. - `below-icon`: the label is shown below the icon (typical for iPhones) - `beside-icon` the label is shown next to the icon (typical for iPad) By default, the position is chosen automatically based on device width. #### `tabBarLabelStyle` Style object for the tab label. #### `tabBarIcon` React.ReactNode or a function that given `{ active: boolean }` returns a React.ReactNode, to display in the tab bar. #### `showTabBarBadge` Whether or not the badge should be visible on the tab icon. #### `showTabBarBadgeOnActive` Whether or not the badge should be visible on the tab icon when it is active. #### `tabBarBadgeLabel` Text to show in a badge on the tab icon. Accepts a `string` or a `number`. #### `tabBarBadgeOffset` Offset for the badge on the tab icon. #### `tabBarAccessibilityLabel` Accessibility label for the tab button. This is read by the screen reader when the user taps the tab. It's recommended to set this if you don't have a label for the tab. #### `tabBarTestID` ID to locate this tab button in tests. #### `tabBarActiveTintColor` Color for the icon and label in the active tab. #### `tabBarInactiveTintColor` Color for the icon and label in the inactive tabs. #### `tabBarActiveBackgroundColor` Background color for the active tab. #### `tabBarInactiveBackgroundColor` Background color for the inactive tabs. #### `tabBarActiveLabelWeight` Font weight for the label in the active tab. #### `tabBarInactiveLabelWeight` Font weight for the label in the inactive tabs. #### `tabBarItemStyle` Style object for the tab item container. #### `tabBarStyle` Style object for the tab bar. You can configure styles such as background color here. To show your screen under the tab bar, you can set the `position` style to absolute: ```js ``` ## Dynamic Type Text and icons scale to 3XL. Any additional text or icon passed in should set `maxFontSizeMultiplier={1.3}`. --- id: create-stack-navigator category: Navigation title: createStackNavigator description: Provides a way for your apps to transition between screens where each new screen is placed on top of a stack. --- ```jsx import { createStackNavigator } from '@uhg-abyss/mobile'; ``` This navigator uses the native APIs `UINavigationController` on iOS and `Fragment` on Android so that navigation built with `createStackNavigator` will behave exactly the same and have the same performance characteristics as apps built natively on top of those APIs. `Screen` components are used to configure various aspects of screens inside a navigator. A `Screen` is returned from a createStackNavigator function: ```jsx const Stack = createStackNavigator(); // Stack contains Screen & Navigator properties ``` After creating the navigator, it can be used as children of the Navigator component: ```jsx function MyStack() { return ( ); } ``` You need to provide at least a name and a component to render for each screen. ### Options The following options can be used to configure the screens in the navigator. These can be specified under `screenOptions` prop of `Tab.Navigator` or `options` prop of `Tab.Screen`. #### `title` Generic title that can be used as a fallback for `headerTitle` and `tabBarLabel`. #### `lazy` Whether the screen should render the first time it's accessed. Defaults to `true`. Set it to `false` if you want to render the screen on initial render. #### `unmountOnBlur` Whether this screen should be unmounted when navigating away from it. Unmounting a screen resets any local state in the screen as well as state of nested navigators in the screen. Defaults to `false`. Normally, we don't recommend enabling this prop as users don't expect their navigation history to be lost when switching tabs. If you enable this prop, please consider if this will actually provide a better experience for the user. #### `freezeOnBlur` Boolean indicating whether to prevent inactive screens from re-rendering. Defaults to `false`. Defaults to `true` when `enableFreeze()` from `react-native-screens` package is run at the top of the application. Requires `react-native-screens` version >=3.16.0. ### Header related options You can find the list of header related options [here](elements.md#header). These [options](screen-options.md) can be specified under `screenOptions` prop of `Tab.navigator` or `options` prop of `Tab.Screen`. You don't have to be using `@react-navigation/elements` directly to use these options, they are just documented in that page. In addition to those, the following options are also supported in bottom tabs: #### `header` Custom header to use instead of the default header. This accepts a function that returns a React Element to display as a header. The function receives an object containing the following properties as the argument: - `navigation` - The navigation object for the current screen. - `route` - The route object for the current screen. - `options` - The options for the current screen. Example: ```jsx import { getHeaderTitle } from '@react-navigation/elements'; header: ({ navigation, route, options }) => { const title = getHeaderTitle(options, route.name); return ; }; ``` To set a custom header for all the screens in the navigator, you can specify this option in the `screenOptions` prop of the navigator. #### `headerTitle` String or React Element that returns a React Element to be used as the title of the header. Defaults to scene `title` #### `headerTitleAlign` How to align the header title. Possible values: - `left` - `center` Defaults to `center` on iOS and `left` on Android. #### `truncateTitle` Allows the title to be truncated. Defaults to false. #### `headerEyebrow` String, React Element, or function that returns a React Element to be used as the eyebrow of the header. The eyebrow is placed above the title. #### `headerSubtitle` String, React Element, or function that returns a React Element to be used as the subtitle of the header. The subtitle is placed below the title. #### `headerLeft` React Element or Function which returns a React Element to display on the left side of the header. You can use it to implement your custom left button, for example: ```js ( { // Do something }} /> ), }} /> ``` #### `headerRight` Function which returns a React Element to display on the right side of the header. #### `headerContent` Content of the header placed at the bottom of the header. #### `headerStyle` Style object for the header. You can specify a custom background color here, for example. #### `headerTitleStyle` Styles for the title component. #### `headerLeftContainerStyle` Customize the style for the container of the `headerLeft` component, for example to add padding. #### `headerRightContainerStyle` Customize the style for the container of the `headerRight` component, for example to add padding. #### `headerTitleContainerStyle` Customize the style for the container of the `headerTitle` element. #### `headerBackgroundContainerStyle` Customize the style for the container of the `headerBackground` element. #### `headerTintColor` Tint color for the header #### `headerTransparent` Defaults to `false`. If `true`, the header will not have a background unless you explicitly provide it with `headerBackground`. The header will also float over the screen so that it overlaps the content underneath. This is useful if you want to render a semi-transparent header or a blurred background. Note that if you don't want your content to appear under the header, you need to manually add a top margin to your content. React Navigation won't do it automatically. #### `headerBackground` Function which returns a React Element to render as the background of the header. This is useful for using backgrounds such as an image or a gradient. For example, you can use this with `headerTransparent` to render a blur view to create a translucent header. #### `headerShown` Whether to show or hide the header for the screen. The header is shown by default. Setting this to `false` hides the header. ## Group `Group` components are used to group several screens inside a navigator. A `Group` is returned from a createStackNavigator function: ```jsx const Stack = createStackNavigator(); // Stack contains Screen & `Navigator` properties ``` After creating the navigator, it can be used as children of the Navigator component: ```jsx ``` It's also possible to nest `Group` components inside other `Group` components. ## Props ### screenOptions Options to configure how the screens inside the group get presented in the navigator. It accepts either an object or a function returning an object: ```jsx {/* screens */} ``` When you pass a function, it'll receive the route and navigation: ```jsx ({ title: route.params.title, })} > {/* screens */} ``` These options are merged with the options specified in the individual screens, and the screen's options will take precedence over the group's options. See Options for screens for more details and examples. ## navigationKey Optional key for a group of screens screen. If the key changes, all existing screens in this group will be removed (if used in a stack navigator) or reset (if used in a tab or drawer navigator): ```jsx {/* screens */} ``` This is similar to the navigationKey prop on Screen, but applies to a group of screens. --- id: elevation category: Styling title: elevation description: Tool to create depth on screen for users --- ```jsx import { elevation } from '@uhg-abyss/mobile'; ``` ## Properties ```typescript elevation(level: number) ``` ## Usage The `elevation` function take in a single parameter that chooses the magnitude of the shadow effect applied to the surface of the component. The parameter accepts integers 0 - 4 inclusive. The function will return an object that can be placed in a component's style prop, which will add the shadow effects directly to the component. ```jsx live () => { return ( Level 0 Level 1 Level 2 Level 3 Level 4 ); }; ``` --- id: flatten-tokens category: Theme title: flattenTokens description: Tool to combine tokens from multiple sources into a single object. --- ```jsx import { flattenTokens } from '@uhg-abyss/mobile'; ``` ## Properties ```typescript flattenTokens(...themes: TokenTheme[]) ``` ## Usage The `flattenTokens` function is designed to flatten and merge tokens from multiple JSON structures, to support a layered system where tokens from different themes can override core, semantic, and component level tokens. The Abyss theme accepts an object with the following keys to define the theme: `sizing`, `spacing`, `color`, `borderRadius`, `borderWidth`, `boxShadow`, `fontFamilies`, `fontWeights`, `fontSizes`, `lineHeights`, `letterSpacing`, `border`, and `opacity`. This function will return a single object in this format that can be used to create a theme object via [createTheme](/mobile/tools/create-theme) for later use in the [ThemeProvider](/mobile/ui/theme-provider). ## Overriding Abyss Token Theme In this example, we use `flattenTokens` and `createTheme` to create a new theme object, which is then passed into `ThemeProvider` to override the Abyss theme and customize the `Toast` component. ```jsx live const coreTheme = { core: { color: { brand: { 60: { value: '#60A0F0', type: 'color', description: 'Overrides info toast with core token', }, }, red: { 60: { value: '#FF5757', type: 'color', description: 'Overrides error toast with core token', }, }, }, }, }; const semanticTheme = { semantic: { color: { surface: { container: { status: { warning: { saturated: { value: '#FFAC63', type: 'color', description: 'Overrides warning toast with semantic token', }, }, }, }, }, }, }, }; const componentTheme = { toast: { color: { background: { success: { value: '#61D48F', type: 'color', description: 'Overrides success toast with component token', }, error: { value: '{core.color.red.60}', type: 'color', description: 'Overrides error toast with component token', }, }, icon: { leading: { value: '{core.color.neutral.80}', type: 'color', description: 'Toast color icon leading', }, close: { value: '{core.color.neutral.80}', type: 'color', description: 'Toast color icon close', }, }, text: { heading: { value: '{semantic.color.text.interactive.rest.tertiary}', type: 'color', description: 'Toast color heading', }, link: { value: '{semantic.color.text.interactive.rest.tertiary}', type: 'color', description: 'Toast color link', }, notification: { value: '{semantic.color.text.interactive.rest.tertiary}', type: 'color', description: 'Toast color notification', }, }, }, 'font-size': { heading: { value: '{core.font-size.lg}', type: 'fontSizes', description: 'Overrides toast heading font size with component token', }, }, 'line-height': { value: '{core.line-height.lg}', type: 'lineHeights', description: 'Overrides toast line height with component token', }, }, }; const Toasts = ({ overridden }) => { return ( {overridden ? 'Info Toast color overridden by Core Theme' : 'You can only compare up to 4 providers'} {overridden ? 'Success Toast color overridden by Component Theme' : 'Your estimate has been saved!'} {overridden ? 'Warning Toast color overridden by Semantic Theme' : 'Be careful what you wish for!'} {overridden ? 'Error Toast color is overridden by Component & Core Theme' : 'Your estimate did not save!'} ); }; const flattenedTokens = flattenTokens(coreTheme, semanticTheme, componentTheme); const theme = createTheme('uhc', { theme: flattenedTokens }); const styles = StyleSheet.create({ heading: { marginLeft: '$md', marginBottom: '$sm', marginTop: '$lg', }, }); render(() => { return ( Original Theme Custom Theme ); }); ``` --- id: styled category: Styling title: styled description: Tool to style elements. --- ```jsx import { styled } from '@uhg-abyss/mobile'; ``` ## Properties ```typescript styled( nativeElement: string | React.FC, config?: object ) ``` ## Object Syntax Only Write using the JavaScript object style syntax. The reasons for this are: performance, bundle size, and developer experience. ```jsx const Button = styled('Pressable', { backgroundColor: '$interactive3', borderColor: '$primary1', borderWidth: 2, borderRadius: 24, padding: 12, }); ``` ```jsx live () => { const Button = styled('Pressable', { backgroundColor: '$interactive3', borderColor: '$primary1', borderWidth: 2, borderRadius: 24, padding: 16, }); return ); }; ``` ## Tokens and Themes You can define tokens in the config file and seamlessly consume and access directly in the Style Object. See [Abyss Theme Tokens](/mobile/ui/theme-provider/#abyss-theme-tokens) to see the default tokens. ```jsx const Button = styled('Pressable', { ... backgroundColor: '$primary1', paddingVertical: '$sm', paddingHorizontal: '$md', }); ``` ```jsx live () => { const Button = styled('Pressable', { alignItems: 'center', borderRadius: 24, paddingVertical: '$sm', paddingHorizontal: '$md', backgroundColor: '$primary1', }); return ( ); }; ``` ### Light and Dark Theme ```jsx const Button = styled('Pressable', { ... light: { backgroundColor: '$white', borderWidth: 2, borderColor: '$primary1', }, dark: { backgroundColor: '$primary1' }, }); ``` ### Landscape and Portrait Orientation Use `landscape` and `portrait` to define styles specific to the device orientation. ```jsx const OrientationView = styled('View', { ... landscape: { flexDirection: 'row', paddingVertical: '$xs', }, portrait: { paddingVertical: '$sm', }, }); ``` ## Animations You can use Animated within a styled component to add animations. Animation values must be passed to the component's `style` prop, not placed within the styled configs. ```jsx import { Animated } from 'react-native'; const AnimatedButton = styled(Animated.View, { alignItems: 'center', borderRadius: 24, paddingVertical: '$sm', paddingHorizontal: '$md', backgroundColor: '$primary1', }); const animatedValue = useRef(new Animated.Value(1)).current; const fade = (direction) => { Animated.timing(animatedValue, { toValue: direction === 'in' ? 0.95 : 1, duration: direction === 'in' ? 100 : 300, useNativeDriver: true, easing: Easing.bezier(0.25, 0.1, 0.25, 0.1), }).start(); }; const handlePressIn = (e) => { fade('in'); }; const handlePressOut = (e) => { fade('out'); }; ``` ```jsx live () => { const ButtonWrapper = styled('Pressable', {}); const AnimatedButton = styled(Animated.View, { alignItems: 'center', borderRadius: 24, paddingVertical: '$sm', paddingHorizontal: '$md', backgroundColor: '$primary1', }); const animatedValue = useRef(new Animated.Value(1)).current; const fade = (direction) => { Animated.timing(animatedValue, { toValue: direction === 'in' ? 0.95 : 1, duration: direction === 'in' ? 100 : 300, useNativeDriver: Platform.select({ native: true, default: false, }), easing: Easing.bezier(0.25, 0.1, 0.25, 0.1), }).start(); }; const handlePressIn = (e) => { fade('in'); }; const handlePressOut = (e) => { fade('out'); }; return ( Button ); }; ``` ## Dynamic/Static When static variants won't work and you need a variable style, you can use the `props` config in the `styled` tool. Place all of your styles along with variants in the static config. The `props` config takes in a function with the props passed into the component as the function's parameters. You can then use those props to handle dynamic styles like sizing and colors. The function should return the style properties to add to the component. The `props` function overrides static styles and styles defined in `variants`. For the best results and performance, it is recommended to use variants when possible. ```jsx live () => { const Button = styled('Pressable', { borderRadius: 24, alignItems: 'center', justifyContent: 'center', variants: { variant: { solid: { backgroundColor: '#d9f6fa' }, outline: { backgroundColor: '$white', borderWidth: 2, borderColor: '$primary1', }, }, isDisabled: { true: { backgroundColor: '$gray2', borderWidth: 0 }, }, }, props: ({ size }) => { return { padding: size + 4 }; }, }); return ( ); }; ``` --- id: tokenize category: Theme title: tokenize description: Tool to tokenize props in a component. --- ```jsx import { tokenize } from '@uhg-abyss/mobile'; ``` ## Properties ```typescript tokenize( component: React.Component, tokenObj: object ): React.Component; ``` There are times when a component accepts a prop that could potentially map to an Abyss token. For example, the React Native SVG Circle component has a `fill` prop that accepts a color. We can use the `tokenize` function to allow the `fill` prop to now accept an Abyss color token. ```jsx const TokenizedCirle = tokenize(Circle, { fill: 'colors' }); ``` The default values that can be used for a prop are `'colors'`, `'space'`, `'fontSizes'`, `'lineHeights'`, `'fontWeights'`, `'fonts'` & `'radii'`. The tokens that exist for each value are defined in the [ThemeProvider](/mobile/ui/theme-provider/#abyss-theme-tokens). ## Basic Example The TouchableHighlight component has a prop, `underlayColor` that accepts a color. Since we have tokens for colors, we can use the `tokenize` function to map the prop to accept tokens. ```jsx const TokenizedTouchableHighlight = tokenize(TouchableHighlight, { underlayColor: 'colors', }); ``` Now we can use a color token as a value for the `underlayColor` prop. Press the button below to see the underlay color. ```jsx live const TokenizedTouchableHighlight = tokenize(TouchableHighlight, { underlayColor: 'colors', }); render(() => { return ( {}} accessibilityRole="button" > Press Me ); }); ``` ### Advanced Example If the prop that should accept tokens is an object, the token object can be mapped to an object. For example, the `style` prop on a View accepts an object with many props that can be mapped to tokens. ```jsx const TokenizedView = tokenize(View, { style: { backgroundColor: 'colors', borderColor: 'colors', marginLeft: 'space', borderRadius: 'radii', }, }); ``` Now, those four props can now accept tokens. ```jsx live const TokenizedView = tokenize(View, { style: { backgroundColor: 'colors', borderColor: 'colors', marginLeft: 'space', borderRadius: 'radii', }, }); render(() => { return ( ); }); ``` --- id: with-theme category: Util title: withTheme description: Tool to wrap a component with a higher order component. --- ```jsx import { withTheme } from '@uhg-abyss/mobile'; ``` This function takes a component and returns a higher order component with the current theme passed into the child. ## Usage `withTheme` has one argument: the component to be wrapped. This sample code will show the difference. The first component will not have the theme passed to it, while the second will. ```jsx () => { const UnwrappedComponent = (props) => { return {JSON.stringify(props, null, 4)}; }; const WrappedComponent = withTheme(UnwrappedComponent); return ( Here are the props of a component not wrapped with the withTheme function Here are the props of a component that *are* wrapped with the withTheme function ); }; ``` ```jsx live () => { const UnwrappedComponent = (props) => { return {JSON.stringify(props, null, 4)}; }; const WrappedComponent = withTheme(UnwrappedComponent); return ( Here are the props of a component not wrapped with the withTheme function Here are the props of a component that *are* wrapped with the withTheme function ); }; ``` --- id: accordion category: Content title: Accordion description: A vertically stacked list of headers that reveal or hide associated sections of content. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/branch/1Uml7LO8NTEWoBUAl4DTaG/Abyss-Mobile?node-id=10126-35246&t=2iqvkqjkI4KBEphM-0 --- ```jsx import { Accordion } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Accordion', inputs: [ { prop: 'type', type: 'select', options: [ { label: 'single', value: 'single' }, { label: 'multiple', value: 'multiple' }, ], }, { prop: 'defaultValue', type: 'select', options: [ { label: 'none', value: '' }, { label: 'sandbox-1', value: 'sandbox-1' }, { label: 'sandbox-2', value: 'sandbox-2' }, { label: 'sandbox-3', value: 'sandbox-3' }, ], }, { prop: 'isCollapsible', type: 'boolean', }, { prop: 'isDisabled', type: 'boolean', }, ] } SURPRISE - Sandbox Accordion 1 SURPRISE - Sandbox Accordion 2 SURPRISE - Sandbox Accordion 3 ``` ## Type Multiple Use the `type` property to set the Accordion to either have `single` or `multiple` open items. The default is set to `single`. ```jsx live SURPRISE - Sandbox Accordion 1 SURPRISE - Sandbox Accordion 2 SURPRISE - Sandbox Accordion 3 ``` ## Default Value - Single Use the `defaultValue` property to set an initial Accordion to be open based on its `value` property. When type is set to `single` pass in a string. ```jsx live SURPRISE - Sandbox Accordion 1 SURPRISE - Sandbox Accordion 2 SURPRISE - Sandbox Accordion 3 ``` ## Default Value - Multiple When the `type` of the Accordion is set to `multiple` pass in a string array. ```jsx live SURPRISE - Sandbox Accordion 1 SURPRISE - Sandbox Accordion 2 SURPRISE - Sandbox Accordion 3 ``` ## Collapsible The `isCollapsible` property allows closing content when clicking the trigger for an open item. When `true` you are allowed to collapse all items. When `false` one item will always remain open. The default is set to `true`. Collapsible does not apply when the type is "multiple". ```jsx live Accordion Content 1 Accordion Content 2 Accordion Content 1 Accordion Content Item 2 ``` ## Disabled Use the `isDisabled` property to disable the entire `Accordion` or individual levels. The default is set to `false`. ```jsx live Not disabled Disabled Not disabled Disabled Disabled ``` ## Border Variants Accordion borders can be manipulated using the below props: `hideBorderTop` | hides the border at the top of the accordion. `hideBorderBottom` | hides the border at the bottom of the accordion. `hideBorderAll` | hides all borders in the accordion. ```jsx live Hides the border at the top of the accordion Hides the border at the top of the accordion Hides the border at the bottom of the accordion Hides the border at the bottom of the accordion Hides all borders in the accordion Hides all borders in the accordion ``` ## onValueChange The `onValueChange` property is an event handler that is called when the expanded state of any item changes. ```jsx live () => { return ( console.log('Multi Value', val)} style={{ marginBottom: 20 }} > Accordion Content 1 Accordion Content 2 Accordion Content 3 console.log('Single Value', val)} > Accordion Content 1 Accordion Content 2 Accordion Content 3 ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: accumulator-v2 category: Data Viz title: V2Accumulator description: A graphical representation of a data set based on one variable. design: https://www.figma.com/design/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=60560-2884&t=nVava1vxhAz8Wrvz-0 subDirectory: Accumulator/v2 sourceIsTS: true --- ```jsx import { V2Accumulator } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'V2Accumulator', inputs: [ { prop: 'percentage', type: 'number', defaultValue: 50 }, { prop: 'animationDelay', type: 'number', }, { prop: 'color', type: 'string', defaultValue: '$conditional7', }, ] } ``` ## Percentage The `percentage` prop determines how far the accumulator will move. The prop accepts numbers between `0` and `100` The default is `0`. ```jsx live ``` ## Animation Delay The `animationDelay` prop is used to start the animation after a given delay (milliseconds). The default is `0`. ```jsx live () => { const [animated, toggle] = useToggle(false); const percentage = animated ? 40 : 0; return ( ); }; ``` ## Color The `color` prop is used to set the color for the accumulator track. The default is set to `$conditional7`. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: accumulator category: Data Viz title: Accumulator description: A graphical representation of a data set based on one variable. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=20095%3A102386 subDirectory: Accumulator/v1 --- ```jsx import { Accumulator } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Accumulator', inputs: [ { prop: 'percentage', type: 'number', defaultValue: 50 }, { prop: 'animationDelay', type: 'number', }, ] } ``` ## Percentage The `percentage` prop determines how far the accumulator will move. The prop accepts numbers between `0` and `100` The default is `0`. ```jsx live ``` ## Animation Delay The `animationDelay` prop is used to start the animation after a given delay (milliseconds). The default is `0`. ```jsx live () => { const [animated, toggle] = useToggle(false); const percentage = animated ? 40 : 0; return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: activity-tracker category: Data title: ActivityTracker description: Displays a visual representation of the user's activity. design: https://www.figma.com/design/wCMblLsq9TxAQvKzY3EfCt/UHC-Abyss-Mobile?node-id=56619-6859&t=7SRu2GmHzPNPQzaT-0 --- ```jsx import { ActivityTracker } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'ActivityTracker', inputs: [ { prop: 'title', type: 'string', }, { prop: 'showActiveStep', type: 'boolean', }, { prop: 'variant', type: 'select', options: [ { label: 'oneweek', value: 'oneweek' }, { label: 'twoweeks', value: 'twoweeks' }, ], }, ] } ``` ## Variants Use the `variant` prop to determine the variant of the activity tracker. The `twoweeks` variant displays a 14-day option that does not contain an `showActiveStep`. The `oneweek` variant is the default. ```jsx live ``` ## Completed Steps Use the `completedSteps` prop to determine the steps that have been completed. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: alert category: Feedback title: Alert description: Communicate a state that affects the entire system, not just a feature or page. It persists over a session and appears without the user initiating the action. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=10091%3A35499&t=KluMjh3mwaQrqcQh-0 --- ```jsx import { Alert } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Alert', inputs: [ { prop: 'title', type: 'string', }, { prop: 'description', type: 'string' }, { prop: 'variant', type: 'select', options: [ { label: 'success', value: 'success' }, { label: 'warning', value: 'warning' }, { label: 'error', value: 'error' }, { label: 'info', value: 'info' }, ] }, { prop: 'isCloseable', type: 'boolean' }, ] } Go To Results ``` ## Title and Description The `title` and `description` props are used to set the title and description of the alert. Both props are required. ```jsx live ``` ## isCloseable The `isCloseable` prop determines if a close button is displayed in the alert. It defaults to `true` unless the `inline` prop is enabled. ```jsx live ``` ## Variants Use the `variant` property to set the color of the `Alert`. The options are `success`, `warning`, `error`, and `info`. The default is `success`. ```jsx live ``` ## Inline Variant The inline variant functions the same as the default, but is rounder and takes up less width. It uses the `inline` prop, changing the border radius to create a more rounded look. The inline variant should be wrapped in a component that provides padding on the left and right (see example below). ```jsx live () => { const InlineWrapper = styled('View', { paddingHorizontal: 48, }); return ( ); }; ``` ## Children Add Children to the Alert component by simply placing elements between the Alert tags. Children should be used for adding either a link or button. Links and buttons can be added with the `Alert.Link` and `Alert.Button` components, respectively. ```jsx live setIsVisible(false)} > } > Go To Results } > Use current location ``` ## Change Icon Use the `icon` property to pass in a specific `Icon` component. Since `title` and `description` are required props, icons are used in a setting in which it is just a decorative element (which is the default case for icons) and should be ignored by screen readers. The implementation below provides an example. Since the default of `isScreenReadable` is set to false, no specific changes need to be made for decorative icons. Find further guidance on material icons in the [Material Icons Tab](/mobile/ui/icon-material). ```jsx live } title="Search Title" description="The icon is decorative because of text from the title and this description." variant="info" onClose={() => {}} /> ``` ## onClose Use the `onClose` property to handle the action when close button is triggered. The `onClose` property is always required. ```jsx live () => { const [isVisible, toggleVisibility] = useToggle(true); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: app-bar category: Navigation title: AppBar description: Displays information and actions relating to the current screen. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=10091%3A35499&t=KluMjh3mwaQrqcQh-0 --- ```jsx sandbox { component: 'AppBar', inputs: [ { prop: 'title', type: 'string', defaultValue: 'Title' }, { prop: 'truncateTitle', type: 'boolean', }, { prop: 'titleAlignment', type: 'select', options: [ { label: 'center', value: 'center' }, { label: 'left', value: 'left' }, ] }, ], } ``` ## Accessories App Bar contains options to add several partner components, depending on the page the App Bar is designed for. These accessories can be placed in the children of the AppBar. - SearchBar - A search input that allows users to search for content within your app. - ProgressBar - A complementary visual representation of the current step a user is on while completing a form of inputs. - Tabs - A navigation tool for secondary pages and actions. When a page contains Tabs, the user is able to horizontally scroll through the list of options. - Segmented Controls - A list of options, and there cannot be more than one segment selected, so under the hood this behaves like a radio group. - Avatar - Used on the homepage App Bar, and within the nested actions for Selector Section to show the current user on the app. ```jsx live () => { const [segmentTab, setSegmentTab] = useState('tab-1'); const [value, setValue] = useState(''); return ( { return logger.log('Value submitted'); }} colorScheme="dark" placeholder="Search your benefits" /> For Michael California ); }; ``` ## Nested Content Use the `AppBar.NestedContent` component to add nested content to the AppBar with the `nestedContent` prop. The `AppBar.NestedContent` component adds padding and around the nested content and gaps between the nested content. ```jsx live Nested Content Nested Content } right={ } > ``` ## Banner Use the `Banner` prop to pass an accessory such as GlobalAppProcess to be displayed at the top of the AppBar. - GlobalAppProcess : Used to communicate system status or background processes. ```jsx live } /> ``` ## Title Alignment Use the `titleAlignment` props to configure the alignment of the title. The options can be either `center` or `left` The default is `center`. ```jsx live ``` ## Left and Right The `left` and `right` props are used to placed content on the left and right sides of the title. ```jsx live Cancel } right={1 of 5} /> ``` ## Variants The App Bar’s functionality should change based on the page that it is on. - Homepage - On the homepage, App Bar announces the user’s name, and shows the Avatar of the specific user. - Level 1 - Level One pages are reserved for the pages that are the highest level of navigation. There is no previous content that navigated them to this page, and to return to the homepage, the user will click on the Homepage tab on Tab Bar. - Level 2 - Level Two pages are nested pages within a Level One page. The Back button in the upper left corner of the title section will bring the user back to the Level One page. Like the Level One page, these pages also host a number of nested components used to help users navigate the page. ```jsx live Homepage } titleAlignment="left" title="Home" subtitle="Jessica Anderson" right={ } /> Level 1 } /> Level 2 } titleAlignment="center" title="Home" right={ } /> ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` **It is the responsibility of consuming teams to make sure all components within AppBar are accessible.**

Dynamic Type

AppBar scales to 3XL. Additional Icons and Text passed to any prop of type node should have `maxFontSizeMultiplier={1.3}` set.
```jsx render ``` --- id: arrow category: Navigation title: Arrow description: Navigates between a set number of items. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=27809-47023&mode=design&t=txkb6WojbsllpkON-0 --- ```jsx import { Arrow } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Arrow', inputs: [ { prop: 'direction', type: 'select', options: [ { label: 'back', value: 'back' }, { label: 'forward', value: 'forward' }, ] }, ] } ``` ## Direction Use the `direction` prop to determine which way the arrow will face. The default is set to `'back'`. ```jsx live ``` ## onPress Use the `onPress` prop to determine the action when the arrow is pressed. ```jsx live () => { const [direction, setDirection] = useState('Press an arrow'); const handlePress = (d) => { setDirection(d + ' pressed'); }; return ( handlePress('Back')} /> {direction} handlePress('Forward')} /> ); }; ``` ```jsx render ``` ```jsx render ``` --- id: avatar category: Data Display title: Avatar description: The Avatar component is used to represent a user, and displays the profile picture or the user initials as a fallback. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=10158%3A35251&t=0e2Zlqt54CaApzhj-0 --- ```jsx import { Avatar } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Avatar', inputs: [ { prop: 'initials', type: 'string', }, { prop: 'size', type: 'select', options: [ { label: 'large', value: 'large' }, { label: 'Small', value: 'small' }, ] }, { prop: 'colorTheme', type: 'select', options: [ { label: 'blue', value: 1 }, { label: 'green', value: 2 }, { label: 'orange', value: 3 }, { label: 'light blue', value: 4 }, ] }, { prop: 'showBorder', type: 'boolean' }, ] } ``` ## Images Use the `imageUrl` prop to set the `Avatar` image. This can be used to create a co-branded avatar. ```jsx live ``` ###### Co-branded Avatars ```jsx live () => { const AvatarBorder = styled('View', { borderRadius: 100, borderWidth: 4, borderColor: '$interactive3', marginLeft: -15, }); return ( Abyss Design System Home ); }; ``` ## Color Theme Use the `colorTheme` prop to set the color of the `Avatar`. The options are `1` (blue), `2` (green), `3` (orange), and `4` (light blue). The default is `1` (blue). ```jsx live ``` ## Size Use the `size` prop to set the size of the `Avatar`. The 2 options are `small`, `large`. The default is `small`. ```jsx live ``` ## Notification Use the Indicator component with the Avatar to display a notification. See [Indicator](/mobile/ui/indicator) for more details and examples. ```jsx live ``` ## Show border Use the `showBorder` prop to add a border to the Avatar. ```jsx live ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type The avatar text will scale up to 3XL. ```jsx render ``` --- id: badge-v2 category: Data Display title: V2Badge description: Used to highlight an item's status for quick recognition. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=9534%3A32348 subDirectory: Badge/v2 sourceIsTS: true --- ```jsx import { V2Badge } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'V2Badge', inputs: [ { prop: 'children', type: 'string', }, { prop: 'variant', type: 'select', options: [ { label: 'success', value: 'success' }, { label: 'warning', value: 'warning' }, { label: 'error', value: 'error' }, { label: 'info', value: 'info' }, { label: 'neutral', value: 'neutral' }, ], defaultValue: 'success', }, { prop: 'outline', type: 'boolean', }, ] } Badge Sandbox ``` ## Variants Use the `variant` property to set the color of the `Badge`. The options are `success`, `warning`, `error`, `info`, and `neutral`. The default is `success`. ```jsx live Success Badge Warning Badge Error Badge Info Badge Neutral Badge Success Badge Warning Badge Error Badge Info Badge Neutral Badge ``` ## Outline Use the `outline` property to turn on the outline of the `Badge`. The default is `false`. ```jsx live Badge Outlined Badge ``` ## Icons Use the `icon` property to set the icon of the `Badge`. ```jsx live } variant="success" > Complete } variant="error" > Incomplete ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: badge category: Data Display title: Badge description: Used to highlight an item's status for quick recognition. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=9534%3A32348 pagination_prev: null --- ```jsx import { Badge } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Badge', inputs: [ { prop: 'children', type: 'string', }, { prop: 'variant', type: 'select', options: [ { label: 'success', value: 'success' }, { label: 'warning', value: 'warning' }, { label: 'error', value: 'error' }, { label: 'info', value: 'info' }, { label: 'neutral', value: 'neutral' }, ] }, { prop: 'rounded', type: 'boolean', }, { prop: 'outline', type: 'boolean', }, ] } Badge Sandbox ``` ## Variants Use the `variant` property to set the color of the `Badge`. The options are `success`, `warning`, `error`, `info`, and `neutral`. The default is `success`. ```jsx live Success Badge Warning Badge Error Badge Info Badge Neutral Badge Success Badge Warning Badge Error Badge Info Badge Neutral Badge ``` ## Rounded Use the `rounded` property to change the style of the `Badge` from rounded or squared. The default is `false`. ```jsx live Squared Badge Rounded Badge ``` ## Outline Use the `outline` property to turn on the outline of the `Badge`. The default is `false`. ```jsx live Badge Outlined Badge ``` ## Icons Use the `icon` property to set the icon of the `Badge`. ```jsx live } variant="success" > Complete } variant="error" > Incomplete ``` ```jsx render ``` ```jsx render ``` --- id: banner-v2 category: Layout title: V2Banner description: Used to provide high-level content in a heading, paragraph, and image format within a pressable for navigation. design: https://www.figma.com/design/5djnh49w0SBYAG5tFJifIG?node-id=1492-351&m=dev subDirectory: Banner/v2 sourceIsTS: true --- ```jsx import { V2Banner } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'V2Banner', inputs: [ { prop: 'heading', type: 'string', defaultValue: '2 line heading max' }, { prop: 'paragraph', type: 'string', defaultValue: 'Description paragraph should not expand on more than four lines and truncates if that is the case.' }, { prop: 'variant', type: 'select', options: [ { label: 'horizontal', value: 'horizontal' }, { label: 'vertical-lg', value: 'vertical-lg' }, { label: 'vertical-sm', value: 'vertical-sm' }, { label: 'branded', value: 'branded' }, ], defaultValue: 'horizontal' }, { prop: 'background', type: 'select', options: [ { label: 'White', value: 'white' }, { label: 'Peach', value: 'peach' }, { label: 'Mint', value: 'mint' }, { label: 'Aqua', value: 'aqua' }, { label: 'Sky Blue', value: 'sky-blue' }, ], defaultValue: 'white' }, { prop: 'isClosable', type: 'boolean', }, ] } ``` ## Variants Use the `variant` prop to set the type of `Banner` to render. ```jsx live } /> ``` ## Background Use the `background` prop to set the background color of the banner. The default value is `white`. ```jsx live ``` ## Image Use the `image` prop to set an image in the image container of the banner. ```jsx live } imageBackgroundColor="$pastel1" /> } paragraph="Get 24/7 access to providers by phone, video, or tablet." cta={ } /> ``` ## Image Background Color Use the `imageBackgroundColor` prop to set an image in the image container of the banner. The default is `$gray1`. ```jsx live } variant="horizontal" /> ``` ## CTA Use the `cta` prop to set a call to action button in the banner. If a cta is set the number of lines of the heading paragraph are limited depending on the size of the CTA. To view the truncation rules, see the [Banner truncation conditions](https://www.figma.com/design/5djnh49w0SBYAG5tFJifIG?node-id=1620-6985&m=dev). ```jsx live } imageBackgroundColor="$pastel1" cta={ } /> } variant="horizontal" cta={ Link CTA } /> ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type Text and icons scale to Abyss standards. Any images, illustrations, or IconBrand passed to Banner should not scale. Size AX5 and larger cause reordering and resizing of all variants. ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: banner category: Layout title: Banner description: Used to provide high-level content in a title, description, and image format within a pressable for navigation. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=18723-91680&mode=design&t=yH2CaS5OmhOCzV1a-0 subDirectory: Banner/v1 --- ```jsx import { Banner } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Banner', inputs: [ { prop: 'eyebrow', type: 'string', }, { prop: 'title', type: 'string', defaultValue: 'Your Organizer' }, { prop: 'description', type: 'string', defaultValue: 'A "one-stop-shop" for the important things a caregiver could need to give you for providing the best care.' }, { prop: 'variant', type: 'select', options: [ { label: 'horizontal-small', value: 'horizontal-small' }, { label: 'horizontal-large', value: 'horizontal-large' }, { label: 'vertical', value: 'vertical' }, { label: 'branded', value: 'branded' }, ] }, { prop: 'color', type: 'select', options: [ {label: '$white', value: '$white'}, {label: '$pastel1', value: '$pastel1'}, {label: '$pastel2', value: '$pastel2'}, {label: '$pastel3', value: '$pastel3'}, {label: '$pastel4', value: '$pastel4'}, ], }, { prop: 'textSize', type: 'select', options: [ {label: 'small', value: 'small'}, {label: 'large', value: 'large'}, ], }, { prop: 'grow', type: 'boolean' }, { prop: 'fixed', type: 'boolean' }, { prop: 'isDisabled', type: 'boolean' }, { prop: 'showExternalIcon', type: 'boolean' }, { prop: 'centerText', type: 'boolean' }, { prop: 'isCloseable', type: 'boolean' }, ] } ``` ## Variants Use the `variant` prop to set the type of `Banner` to render. The options are `horizontal-small`, `horizontal-large`, `vertical`, and `branded`. Please note that some props will only work with certain variants. Be sure to follow design guidelines when deciding which variant to use for your content. For variants to be used within Carousel use the [Carousel Banner](/mobile/ui/banner/#carousel-banner) component, while Featured Content Cards should use Banner. ```jsx live } /> ``` ### isCloseable and isVisible Use the `isCloseable` prop to add a close button to the banner. The default value is `false`. This flag works on horizontal small, horizontal large, and vertical variants. When the button is pressed, the `onClose` callback is fired. `isVisible` is a boolean prop that determines whether the banner is visible or not. The default value is `true`. ```jsx live () => { const [isVisible, setIsVisible] = React.useState(true); const [isVisible2, setIsVisible2] = React.useState(true); const [isVisible3, setIsVisible3] = React.useState(true); return ( setIsVisible(false)} /> setIsVisible2(false)} /> setIsVisible3(false)} /> ); }; ``` ## Carousel Banner Use the `Carousel.Banner` component for use within a carousel. The Carousel Banner receives the same props as Banner with the exception of the `textSize` and `grow` props. It does not allow for multiple variants within the same carousel. See [Carousel](/mobile/ui/carousel) for details on using the carousel component. ```jsx live () => { return ( , ]} /> , ]} /> , ]} /> } />, ]} /> ); }; ``` ## Text Content There are three props that add to the text content area of the Banner: `eyebrow`, `title`, and `description`. - eyebrow - Sets the eyebrow content above the`title` content. This can be a string or a node. - title - Sets the title content - description - Sets the description content ```jsx live Badge} title={'Title Text'} textSize={'small'} /> ``` ## Text Size Use the `textSize` prop to set the size of the title and description text. By default it is set to `"large"`. ```jsx live ``` ## Colors Use the `color` prop to set the background color of the content container. The options are the theme colors `$white`, `$pastel1`, `$pastel2`, `$pastel3`, and `$pastel4`. The default is `$white`. ```jsx live ``` ## Image Use the `image` prop to set an image in the image container of the banner. ```jsx live
} variant="horizontal-small" /> } variant="branded" href={'https://www.optum.com/'} linkText="External Link" /> ``` ## Image Background Color Use the `imageBackgroundColor` prop to set an image in the image container of the banner. The default is `$gray1`. ```jsx live
} variant="horizontal-small" /> ``` ## Content Use the `content` prop to add content to the container. When content is added and `fixed` is set to true, be sure to evaluate whether a title, eyebrow, or description is necessary as the fixed container may clip the additional content. ```jsx live $0 cost } /> $0 cost } /> ``` ## Footer Use the `footer` prop to add content to the bottom of the content container. ```jsx live $0 cost } /> ``` ## Link Use the `linkText` prop to set the text of the link and the `href` prop to set the URL. ```jsx live } /> } /> ``` ## CTA Type The `ctaType` prop allows optional switching of the CTA element to either a Button or Link. The default CTA is a Link for non-branded variants, and a Button for the branded variant. There are some Design guidelines regarding CTA use and truncation that are recommended, but not enforced by code. They can be found [here.](https://www.figma.com/design/5djnh49w0SBYAG5tFJifIG/v1.58.0-App-Abyss-Global%E2%80%A8Component-Library?node-id=1492-351&t=lXSGUgv4HybmB0o2-0) ```jsx live Defaults } /> With Prop } /> ``` ## Link Icon Use the `linkIcon` prop to customize the icon displayed after the link text. ```jsx live } linkIcon={ } /> ``` ## Grow Use the `grow` prop to stretch the horizontal banner variants to full width of its container. The default value is `false`. ```jsx live ``` ## Fixed Use the `fixed` prop to make the height of the banners static. When fixed is true, the `centerText` prop can be set to align the title and description content in the center. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type Text and icons scale to Abyss standards. Any images, illustrations, or IconBrand passed to Banner should not scale. Size AX5 and larger cause reordering and resizing of all variants. --- id: bottom-sheet category: Overlay title: BottomSheet description: A surface containing supplementary content that are anchored to the bottom of the screen. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=15431-86066&t=xQWp12dwBu1GhQOV-0 --- ```jsx import { BottomSheet } from '@uhg-abyss/mobile'; ``` ## Example ```jsx live () => { const [radioValue, setRadioValue] = useState('one'); const [tempRadioValue, setTempRadioValue] = useState(radioValue); const [isVisible, setIsVisible] = useState(false); const [isVisible2, setIsVisible2] = useState(false); const updateRadioValue = (save) => { if (save) { setRadioValue(tempRadioValue); } else { setTempRadioValue(radioValue); } setIsVisible(false); }; return ( updateRadioValue()} title="A really long title" footer={ } > } > You can customize your estimate by deleting this step. Would you like to delete this step from the estimate? ); }; ``` ### Scrolling When the content of the BottomSheet becomes too long to display, scrolling is automatically enabled. In code, the height of the content is compared to the max height of the BottomSheet, which is 90% of the screen. To manually disable scrolling, setting `scrollEnabled` to `false` will switch the component from a ScrollView to a View. Default is `true,` meaning the height of the content will determine scrollability. ```jsx live () => { const [radioValue, setRadioValue] = useState('one'); const [tempRadioValue, setTempRadioValue] = useState(radioValue); const [isVisible, setIsVisible] = useState(false); const updateRadioValue = (save) => { if (save) { setRadioValue(tempRadioValue); } else { setTempRadioValue(radioValue); } setIsVisible(false); }; return ( updateRadioValue()} title="A really long title" footer={ } > ); }; ``` ### Closing on Background Press By default, the BottomSheet will close when the user taps on the background overlay. This can be disabled by setting the `disableOverlayPress` prop to `true`. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); return ( setIsVisible(false)} title="Bottom Sheet with Background Click Disabled" disableOverlayPress footer={ } > Tapping outside this Bottom Sheet will not close it. Use the button below to close. ); }; ``` ## Advanced Layout Layouts like BottomSheet and Modal can be used in combination with each other to create flows. ```jsx live () => { const team = [ { firstName: 'Michael', lastName: 'White', linkText: 'California', subText: 'MM/DD/YYYY', value: '1', }, { firstName: 'Thomas', lastName: 'Musengwa', linkText: 'Arkansas', subText: 'MM/DD/YYYY', value: '2', }, { firstName: 'Bailey', lastName: 'Surowiec', linkText: 'Illinois', subText: 'MM/DD/YYYY', value: '3', }, { firstName: 'Pablo', lastName: 'Zepeda', linkText: 'California', subText: 'MM/DD/YYYY', value: '4', }, ]; const locations = [ { name: 'Alabama', value: 'AL', }, { name: 'Alaska', value: 'AK', }, { name: 'Arizona', value: 'AZ', }, { name: 'Arkansas', value: 'AR', }, { name: 'California', value: 'CA', }, { name: 'Colorado', value: 'CO', }, { name: 'Connecticut', value: 'CT', }, { name: 'Delaware', value: 'DE', }, { name: 'Florida', value: 'FL', }, { name: 'Georgia', value: 'GA', }, { name: 'Hawaii', value: 'HI', }, { name: 'Idaho', value: 'ID', }, { name: 'Illinois', value: 'IL', }, { name: 'Indiana', value: 'IN', }, { name: 'Iowa', value: 'IA', }, { name: 'Kansas', value: 'KS', }, { name: 'Kentucky', value: 'KY', }, { name: 'Louisiana', value: 'LA', }, { name: 'Maine', value: 'ME', }, { name: 'Maryland', value: 'MD', }, { name: 'Massachusetts', value: 'MA', }, { name: 'Michigan', value: 'MI', }, { name: 'Minnesota', value: 'MN', }, { name: 'Mississippi', value: 'MS', }, { name: 'Missouri', value: 'MO', }, { name: 'Montana', value: 'MT', }, { name: 'Nebraska', value: 'NE', }, { name: 'Nevada', value: 'NV', }, { name: 'New Hampshire', value: 'NH', }, { name: 'New Jersey', value: 'NJ', }, { name: 'New Mexico', value: 'NM', }, { name: 'New York', value: 'NY', }, { name: 'North Carolina', value: 'NC', }, { name: 'North Dakota', value: 'ND', }, { name: 'Ohio', value: 'OH', }, { name: 'Oklahoma', value: 'OK', }, { name: 'Oregon', value: 'OR', }, { name: 'Pennsylvania', value: 'PA', }, { name: 'Rhode Island', value: 'RI', }, { name: 'South Carolina', value: 'SC', }, { name: 'South Dakota', value: 'SD', }, { name: 'Tennessee', value: 'TN', }, { name: 'Texas', value: 'TX', }, { name: 'Utah', value: 'UT', }, { name: 'Vermont', value: 'VT', }, { name: 'Virginia', value: 'VA', }, { name: 'Washington', value: 'WA', }, { name: 'West Virginia', value: 'WV', }, { name: 'Wisconsin', value: 'WI', }, { name: 'Wyoming', value: 'WY', }, ]; const [data, setData] = useState(team); const [value, setValue] = useState(data[0].value); const [member, setMember] = useState(data[0]); const [isVisible, setIsVisible] = useState(false); const [showModal, setShowModal] = useState(false); const getCurrentMember = (data, val) => { return data.find(({ value }) => value === val); }; const getLocation = (locations, val) => { return locations.find(({ value }) => value === val); }; const showToastMessage = () => { Toast.show({ text: 'Member changed', variant: 'success', }); }; const handlePress = () => { setIsVisible(true); }; const updateTeam = (newLocation) => { const newArr = data.map((member) => { if (member.value === value) { member.linkText = newLocation; } return member; }); setData(newArr); }; const handlePressLink = (value) => { const currentMember = getCurrentMember(data, value); setValue(currentMember.value); setShowModal(true); }; const handleButtonPress = () => { const currentMember = getCurrentMember(data, value); setMember(currentMember); setIsVisible(false); showToastMessage(); }; const handleCellPress = (val) => { const newLocation = getLocation(locations, val); updateTeam(newLocation.name); const currentMember = getCurrentMember(data, value); Toast.show({ text: currentMember.firstName + "'s location updated to " + newLocation.name + '!', variant: 'success', }); }; const TextView = styled('View', { justifyContent: 'center', paddingLeft: 9, }); const Content = styled('View', { alignItems: 'center', justifyContent: 'space-between', flexDirection: 'row', backgroundColor: '$primary1', padding: '$md', }); return ( {'For ' + member.firstName} {member.linkText} setIsVisible(false)} title={'Select a member'} footer={} > {data.map( ({ linkText, value, firstName, lastName, subText }, i) => ( } > {subText && ( {subText} )} {linkText && ( handlePressLink(value)} after={ } > {linkText} )} ) )} setShowModal(false)} actionLeft={ } onActionLeftPress={() => { setShowModal(false); }} > {locations.map(({ value, name }) => ( handleCellPress(value)} iconRight={ } > ))} ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ## Focus Guidance Abyss does not control the focus of components on the screen when the Bottomsheet is toggled off. To meet accessibility guidelines, the focus must be set to the previous node when closed. The [useSetFocus](/mobile/hooks/use-set-focus) hook can be used for this. For example, if a button is pressed to open a Bottomsheet, focus must return to that button once it is closed, so that a screen reader or keyboard user may continue using the app where they left off. ```jsx render ``` --- id: box category: Layout title: Box description: Used as a blanket filler to surround just about any component(s) with color or create a box of predefined size. --- ```jsx import { Box } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Box', inputs: [ { prop: 'children', type: 'string', }, { prop: 'padding', type: 'string', }, { prop: 'height', type: 'string', }, { prop: 'width', type: 'string', }, { prop: 'color', type: 'string' }, ], } ``` ## Basic Usage `Box` is a container component that can be used to organize or structure a screen. Use `height` and `width` to control the size. ## Children `Box` takes children of type `node`. ```jsx live () => { return ( Abyss is cool! ); }; ``` ## Color The `color` prop sets the background color. The default is set to $gray2. ```jsx live () => { return ( ); }; ``` ## Padding The `padding` prop sets the padding in all directions. The default is set to $md. ```jsx live () => { return ( Default Padding Large Padding ); }; ``` ## Search Message Example A simple component that displays a message with a link to search for a different term. [Design](https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=64584-133368&mode=design&t=oXWMZJo7aLUqUiFG-0) ```jsx live () => { return ( Results for providers that can treat knee replacement in the following categories: }> Search for knee repladment instead ); }; ``` ```jsx render ``` ```jsx render ``` --- id: bullets category: Controls title: Bullets description: Used to represent the active slide or card in a stack of slides, like a Carousel. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=12804-63325&t=NM5yYbHIHkde3zlc-0 --- ```jsx import { Bullets } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Bullets', inputs: [ { prop: 'bullets', type: 'number', }, ], } () => { const [active, setActive] = useState(1); return ( ) } ``` ## Usage Bullets are the simplified visual pagination for the Carousel component. The active slide or card is shown with a long rectangle, while inactive slides are light blue circles. There will only ever be one active bullet at a time. When the user swipes the carousel or presses on an arrow, the animation of the bullet changes. ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [active, setActive] = useState(3); return ; }; ``` ## Bullets Use the `bullets` prop to set the number of bullets. Bullets should not be used for less than three and no more than six cards, therefore this number should fall between three (3) and six (6). ```jsx live () => { const [active, setActive] = useState(1); const [active2, setActive2] = useState(2); return ( ); }; ``` ## Active Use the `active` prop to set the position of the active bullet. This should be between 1 and the number of bullets. See the example below where the active bullet can be changed by pressing on a specific bullet, or by pressing on the numbered buttons, which directly set the active bullet. ```jsx live () => { const [active, setActive] = useState(1); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: Button-V2 category: Navigation title: V2Button description: Used to trigger an action or event. design: https://www.figma.com/design/wCMblLsq9TxAQvKzY3EfCt/v1.66.0-App-Abyss-UHC-Component-Library?node-id=9529-32359&p=f&m=dev --- ```jsx import { V2Button } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'V2Button', inputs: [ { prop: 'children', type: 'string', }, { prop: 'variant', type: 'select', defaultValue: 'brand', options: [ { label: 'brand', value: 'brand' }, { label: 'neutral', value: 'neutral' }, { label: 'destructive', value: 'destructive' }, { label: 'inverse', value: 'inverse' }, ], }, { prop: 'type', type: 'select', defaultValue: 'filled', options: [ { label: 'filled', value: 'filled' }, { label: 'outline', value: 'outline' }, { label: 'text', value: 'text' }, ], }, { prop: 'size', type: 'select', defaultValue: 'large', options: [ { label: 'large', value: 'large' }, { label: 'small', value: 'small' }, ], }, { prop: 'isDisabled', type: 'boolean', }, ], } Click Here! ``` ## Button Types and Variants V2Button provides distinct visual styles through separate `type` and `variant` props: - Use the `type` prop to specify the button style category: `filled`, `outline`, or `text`. - Use the `variant` prop to indicate purpose: `brand` (primary actions), `neutral` (secondary actions), `destructive` (dangerous actions), or `inverse` (on dark backgrounds). By default, `brand` variant and `filled` type are enabled. ```jsx live Filled Brand Filled Neutral Filled Destructive Outline Brand Outline Neutral Outline Destructive Text Brand Text Neutral Text Destructive Filled Inverse Outline Inverse Text Inverse ``` ## Size Use the `size` prop to change the size of the button. The size prop can take in either `large` or `small`. The default value is `large`. To better visualize the difference, the Layout is preventing the large from taking up the full screen. ```jsx live Large Button Small Button ``` ## Icon Position The `iconPosition` prop controls where icons appear in your button. There are three options: - `trailing` (default): Places the icon after the button text - `leading`: Places the icon before the button text - `iconOnly`: Creates a small circular button with only the icon and no text ```jsx live Continue Send Message Back Add to Cart ``` ### Icon-Only Buttons `iconPosition="iconOnly"` creates button with no text. ```jsx live ``` ## Icons V2Button provides icon support through the `icon` prop. You can display icons in different ways: **Using an Icon Object:** Provide an icon object with properties: - `name`: The icon name (from [IconSymbol](mobile/ui/icon-symbol)) - `variant`: icon style ('filled' or 'outlined') **Using a String Name:** Provide the icon name as a valid IconSymbol name string. **Using a React Node:** For advanced use cases, you can provide a custom Icon component. **Using a Function:** Use a function that returns a React node, which receives the button's pressed state and allows `V2Button` to set the color according to Abyss color mappings, or override them. ```jsx live Object String } > ReactNode { return ( ); }} iconPosition="leading" variant="destructive" type="outline" > Function ``` ## Loading When `isLoading` is set to `true`, a spinner indicates that an action is in progress, and `onPress` events are disabled. ```jsx live Button Button Button Button ``` ## Disabled Use the `isDisabled` prop to render the button in a non-interactive state. A disabled button cannot be in a loading state. ```jsx live Disabled Button Cannot Delete Unavailable ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: button category: Navigation title: Button description: Used to trigger an action or event. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=8866%3A42214 --- ```jsx import { Button } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Button', inputs: [ { prop: 'children', type: 'string', }, { prop: 'variant', type: 'select', defaultValue: 'primary', options: [ { label: 'primary', value: 'primary' }, { label: 'secondary', value: 'secondary' }, { label: 'tertiary', value: 'tertiary' }, { label: 'destructive', value: 'destructive' }, { label: 'alt', value: 'alt' }, ], }, { prop: 'size', type: 'select', defaultValue: 'large', options: [ { label: 'large', value: 'large' }, { label: 'small', value: 'small' }, ], }, { prop: 'isDisabled', type: 'boolean', }, { prop: 'rounded', type: 'boolean' }, ], } ``` ## Size Use the `size` prop to change the size of the button. The size prop can take in either `large` or `small`. The default value `large`. The large button fill the entire width of its container. The small button fits the content contained in the button. ```jsx live ``` ## Variant Use the `variant` prop to change the visual style of the Button. You can set the value to `primary`, `secondary`, `tertiary`, `destructive` or `alt`. Primary buttons should only appear once per screen (not including the application header, modal dialog, or side panel). The default value is `primary`. ```jsx live ``` ### Tertiary Variant The tertiary variant now has different padding than other styles. The `large` size has a vertical padding of 6px and the `small` size has a vertical padding of 5px. Users who wish to keep the previous gap between buttons will need to update the margin to 16px. ## Rounded Use the `rounded` prop to make a rounded button. You can insert an icon as a child of the button. ```jsx live ``` ## Disabled Use the `isDisabled` prop to disable a button. ```jsx live ``` ## Inserting Elements Insert elements into the Button component using the `before` and `after` props. ```jsx live ``` ## Preferred Icons Here is a list of preferred icons accepted for use on buttons. If there’s one not listed, please use sparingly. ```jsx live ``` ## Grouping Buttons Pairing similar action buttons together, like “Previous” and “Next”, should use one Primary button and one secondary button, showing the expected action as the primary CTA. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: calendar category: Controls title: Calendar description: A control element that displays a full calendar month at one time. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=39874-170177&mode=design&t=Ridf1HoSvYi7LIPY-0 --- ```jsx sandbox { component: 'Calendar', } () => { const [value, setValue] = useState(new Date()); return ( ) } ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [value, setValue] = useState(); return ; }; ``` ## Minimum and Maximum Date Use the `minimumDate` and `maximumDate` props to set the min and max dates in the Calendar. The default values will be a 1 year range from the prop `value.` ```jsx live () => { const [value, setValue] = useState(); const currentYear = new Date().getFullYear(); const currentMonth = new Date().getMonth(); return ( ); }; ``` ## Excluded Dates To exclude dates use the `excludeDate` prop. Set a function that receives date as an argument and returns true if date should be disabled. For example, to disable weekends, check if the day is 0 or 6. ```jsx live () => { const [value, setValue] = useState(); return ( { return date.getDay() === 0 || date.getDay() === 6; }} /> ); }; ``` ## Goals Use the `goals` prop to add rewards to certain days on the calendar. The goals prop must be an array of 'Goal' objects. A goal object has three properties - `date`: The date the reward will be. Must be a date instance. - `state`: The current state of the reward. Valid values are `'progress'` and `'complete'`. - `percentage`: If the state of the goal is `'progress'`, you can add the completed percentage. Defaults to `50`. ```jsx live () => { const [value, setValue] = useState(); const currentYear = new Date().getFullYear(); const currentMonth = new Date().getMonth(); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type Calendar scales to 3XL. Star icon does not grow. The date picker component in Abyss library is currently not accessible to users relying on screen readers and therefore it is hidden from screen reader users. Please use the date input method as an alternative to the date picker. ```jsx render ``` --- id: card category: Content title: Card description: A single or multi-section container used to display content related to a single subject. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/branch/Sk3MrHYxjT39TKDzDU5LBc/Abyss-Mobile?node-id=12334%3A61355 --- ```jsx import { Card } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Card', inputs: [ { prop: 'children', type: 'string', }, ] } Card Sandbox ``` ## Basic usage The Card component is a versatile wrapper used to display content related to a single subject. One example of `Card` can be seen in [HomeWidget](/mobile/ui/home-widget). ```jsx live Heading text example Text example ``` ## Pressable and Animations The Card component extends the props of [Pressable](https://reactnative.dev/docs/pressable#props) and can be used as a button. Use the `isDisabled` prop to disable the pressable responder. In the case where you place something with its own touch responder within the card, like a ScrollView, FlatList, SectionList, etc., you will want to disable the pressable responder. [Animations](https://reactnative.dev/docs/animated#props) can be added via the `styles` prop targeting `abyss-card-root`. A Card that is programmed as a button with a Call To Action (CTA) must use accessibility props like those in `Button`. This ensures assistive technology can reach, understand, and activate the card. When building a CTA Card, text content should be short. A Screen Reader will present button content in one full string that cannot be navigated easily by the device. Large CTA Card text will be difficult for screen reader users to understand. ```jsx live () => { const scale = useRef(new Animated.Value(1)).current; const shrink = () => { Animated.timing(scale, { toValue: 0.95, duration: 300, easing: Easing.linear, useNativeDriver: true, }).start(); }; const grow = () => { Animated.timing(scale, { toValue: 1, duration: 300, easing: Easing.linear, useNativeDriver: true, }).start(); }; return ( console.log('pressed')} onPressIn={shrink} onPressOut={grow} accessible={true} role="button" styles={{ 'abyss-card-root': { paddingTop: 20, height: 200, alignItems: 'center', transform: [{ scale }], }, }} > Pressable Card Press card to log a console message ); }; ``` ## Card.Section The `Card.Section` subcomponent provides a card 16px of padding. A use case example is provided in [CheckBoxGroup](/mobile/ui/checkbox-group/#group-with-card). ```jsx live Card Section ``` ## Multi Select Card A Card can be used as a container for selections by combining it with the [CheckboxGroup](/mobile/ui/checkbox-group) or [Checkbox](/mobile/ui/checkbox) components. ```jsx live () => { const form = useForm(); return ( Select All Claims (4) $278.89 Dr. Sharon Tang $93.22 Date of Service 5/11/23 Claim Information Walgreens #927956 $127.93 Date of Service 5/5/23 Claim Information Dr. Edward M Jenner $25.00 Date of Service 10/30/22 Claim Information Odin Medical Group $32.74 Date of Service 12/9/22 Claim Information ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: carousel-v2 category: Content title: V2Carousel description: A circular conveyor of information, cycling between cards. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=12446-61354 --- ** Disclaimer: ** On a mobile device the carousel has the full functionality of scrolling, pagination, and snapping to a slide. While on the web, the carousel is restricted to scrolling only by dragging the bottom scroll bar or using the pagination buttons. ```jsx import { V2Carousel } from '@uhg-abyss/mobile'; ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [currentSlide, setCurrentSlide] = useState(1); const colors = ['$pastel1', '$pastel2', '$pastel3', '$pastel4']; return ( { return ( Slide {index + 1} ); }} /> ); }; ``` ## Disable Scrolling Use the `disableScrolling` prop to prevent the carousel from scrolling. ```jsx live () => { const [currentSlide, setCurrentSlide] = useState(1); const [disableScrolling, toggleScrolling] = useToggle(true); const colors = ['$pastel1', '$pastel2', '$pastel3', '$pastel4']; return ( { return ( Slide {index + 1} ); }} /> ); }; ``` ## Disable Pagination Use the `disablePagination` prop to remove the pagination bullets below the V2carousel. ```jsx live () => { const [currentSlide, setCurrentSlide] = useState(1); const [disablePagination, togglePagination] = useToggle(true); const colors = ['$pastel1', '$pastel2', '$pastel3', '$pastel4']; return ( { return ( Slide {index + 1} ); }} /> ); }; ``` ## Slide Gap Use the `slideGap` prop to add a gap between each slide. The default value is `$carousel.space.slide-gap || 8`. ```jsx live () => { const [currentSlide, setCurrentSlide] = useState(1); const [currentSlide2, setCurrentSlide2] = useState(1); const [disableScrolling, toggleScrolling] = useToggle(); const colors = ['$pastel1', '$pastel2', '$pastel3', '$pastel4']; return ( { return ( Slide {index + 1} ); }} /> { return ( Slide {index + 1} ); }} /> ); }; ``` ## Snap Percentage Use the `snapPercentage` prop to set minimum percent in either direction a carousel should be shifted to snap to another V2carousel. The default value is `30`. In the examples below, there is a green line denoting how much the carousel would need to scroll before snapping to the next slide. ```jsx live () => { const [currentSlides, setCurrentSlides] = useState([1, 1, 1]); const snapPercents = [30, 50, 75]; const carousels = snapPercents.map((percent, i) => { const changeSlide = (goToSlide) => { setCurrentSlides((currentSlides) => { const copySlides = currentSlides.slice(0); copySlides[i] = goToSlide; return copySlides; }); }; const slideContent = ['Slide 1', 'Slide 2']; return ( { return ( {slideContent[index]} ); }} /> Snap Percentage: {percent} ); }); return {carousels}; }; ``` ## Data Carousel is made to be used in conjunction with slides, therefore the `data` prop is required. ``` const slides = [ { color: 'white', eyebrow: 'New Service', heading: 'Virtual Care', paragraph: 'Get medical advice from the comfort of your home. Discover our new virtual care services.', }, { color: 'peach', eyebrow: 'Mental health', paragraph: 'Learn more about available mental health benefits and resources available to you', heading: 'Explore Coverage & Support', }, { color: 'mint', eyebrow: 'Update', heading: 'COVID-19 Vaccine Information', paragraph: 'Stay informed about the COVID-19 vaccine. Learn about eligibility, safety, and how to get your shot.', }, { color: 'aqua', eyebrow: 'Event', heading: 'United Healthcare Community Health Fair', paragraph: 'Join us for a day of free health screenings and wellness activities. Bring your family and friends!', }, { color: 'sky-blue', eyebrow: 'In-App Care', heading: 'Real-time, online visits', paragraph: 'Connect with a designated provider using your smartphone.', }, ]; ``` ## Render Slide Use the `renderSlide` prop to render the data passed into V2Carousel. This function takes in the slide object and index number. ``` renderSlide={{({ slide, index }) => { return ( { console.log(`card ${index + 1} pressed`); }} />); }}} ``` ```jsx live () => { const [currentSlide, setCurrentSlide] = useState(1); const slides = [ { color: 'white', eyebrow: 'New Service', heading: 'Virtual Care', paragraph: 'Get medical advice from the comfort of your home. Discover our new virtual care services.', }, { color: 'peach', eyebrow: 'Mental health', paragraph: 'Learn more about available mental health benefits and resources available to you', heading: 'Explore Coverage & Support', }, { color: 'mint', eyebrow: 'Update', heading: 'COVID-19 Vaccine Information', paragraph: 'Stay informed about the COVID-19 vaccine. Learn about eligibility, safety, and how to get your shot.', }, { color: 'aqua', eyebrow: 'Event', heading: 'United Healthcare Community Health Fair', paragraph: 'Join us for a day of free health screenings and wellness activities. Bring your family and friends!', }, { color: 'sky-blue', eyebrow: 'In-App Care', heading: 'Real-time, online visits', paragraph: 'Connect with a designated provider using your smartphone.', }, ]; return ( { return ( { console.log(`Card ${index + 1} pressed`); }} /> ); }} /> ); }; ``` ### Carousel Card Use the `V2Carousel.Card` component to display content on a card with pre-defined styled specific for use within a V2carousel. Please follow design guidelines when implementing. ```jsx live () => { const [currentSlide, setCurrentSlide] = useState(1); const [variant, setVariant] = useState('horizontal'); const optumBrand = ( ); const slides = [ { color: '$white', eyebrow: 'New Service', heading: 'Virtual Care', paragraph: 'Get medical advice from the comfort of your home. Discover our new virtual care services.', }, { color: '$pastel1', eyebrow: 'Mental health', paragraph: 'Learn more about available mental health benefits and resources available to you', heading: 'Explore Coverage & Support', }, { color: '$pastel2', eyebrow: 'Update', heading: 'COVID-19 Vaccine Information', paragraph: 'Stay informed about the COVID-19 vaccine. Learn about eligibility, safety, and how to get your shot.', }, { color: '$pastel3', eyebrow: 'Event', heading: 'United Healthcare Community Health Fair', paragraph: 'Join us for a day of free health screenings and wellness activities. Bring your family and friends!', }, { color: '$pastel4', eyebrow: 'Virtual Care', heading: 'Real-time, online visits', paragraph: 'Connect with a designated provider using your smartphone.', }, ]; return ( <> { return ( { console.log(`Card ${index + 1} pressed`); }} /> ); }} /> ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: carousel category: Content title: Carousel description: A circular conveyor of information, cycling between cards. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=12446-61354 --- ```jsx import { Carousel } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Carousel', inputs: [ { prop: 'title', type: 'string' }, { prop: 'disableScrolling', type: 'boolean' }, { prop: 'disablePagination', type: 'boolean' }, { prop: 'actionText', type: 'string' }, ] } () => { const [currentSlide, setCurrentSlide] = useState(1); const length = 5 const banners = Array.from({length}).map((_,i) => { return Slide {i +1} }) return ( ) } ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [currentSlide, setCurrentSlide] = useState(1); const length = 5; const banners = Array.from({ length }).map((_, i) => { return ( Slide {i + 1} ); }); return ( ); }; ``` ## Disable Scrolling Use the `disableScrolling` prop to prevent the carousel from scrolling. ```jsx live () => { const [currentSlide, setCurrentSlide] = useState(1); const [disableScrolling, toggleScrolling] = useToggle(true); const length = 5; const banners = Array.from({ length }).map((_, i) => { return ( Slide {i + 1} ); }); return ( ); }; ``` ## Disable Pagination Use the `disablePagination` prop to remove the pagination bullets below the carousel. ```jsx live () => { const [currentSlide, setCurrentSlide] = useState(1); const [disablePagination, togglePagination] = useToggle(true); const length = 5; const banners = Array.from({ length }).map((_, i) => { return ( Slide {i + 1} ); }); return ( ); }; ``` ## Slide Gap Use the `slideGap` prop to add a gap between each slide. The default value is `$sm || 8`. ```jsx live () => { const [currentSlide, setCurrentSlide] = useState(1); const [currentSlide2, setCurrentSlide2] = useState(1); const [disableScrolling, toggleScrolling] = useToggle(); const length = 5; const banners = Array.from({ length }).map((_, i) => { return ( Slide {i + 1} ); }); return ( ); }; ``` ## Snap Percentage Use the `snapPercentage` prop to set minimum percent in either direction a carousel should be shifted to snap to another carousel. The default value is `30`. In the examples below, there is a green line denoting how much the carousel would need to scroll before snapping to the next slide. ```jsx live () => { const [currentSlides, setCurrentSlides] = useState([1, 1, 1]); const snapPercents = [30, 50, 75]; const carousels = snapPercents.map((percent, i) => { const changeSlide = (goToSlide) => { setCurrentSlides((currentSlides) => { const copySlides = currentSlides.slice(0); copySlides[i] = goToSlide; return copySlides; }); }; const slides = [ Slide 1 , Slide 2 , ]; return ( Snap Percentage: {percent} ); }); return {carousels}; }; ``` ## Slides Carousel is made to be used in conjunction with slides, therefore the `slides` prop is required. If you are not using the [Carousel Banner](/mobile/ui/banner#carousel-banner) component for slides, please be sure to make them accessible by passing in the current slide index along with the total slide count to the accessibility label. ### Carousel Banner Use the `Carousel.Banner` component to display content on a banner with pre-defined styled specific for use within a carousel. The Carousel Banner receives the same props as [Banner](/mobile/ui/banner) with the exception of the `textSize` and `grow` props. It does not allow for multiple variants within the same carousel. Please follow design guidelines when implementing. ```jsx live () => { const [currentSlide, setCurrentSlide] = useState(1); const [variant, setVariant] = useState('horizontal-small'); const optumBrand = ( ); return ( <> { return ( ); } )} currentSlide={currentSlide} onSlideChange={setCurrentSlide} snapPercentage={30} /> ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` Note that the `textSize` and `grow` props will not apply to the `Carousel.Banner` component. ```jsx render ``` --- id: cell-v2 category: Data Display title: V2Cell description: Typically used as a navigation element to display a page of categorized content. design: https://www.figma.com/design/5djnh49w0SBYAG5tFJifIG/v1.66.0-App-Abyss-Global%E2%80%A8Component-Library?node-id=1242-1385&p=f&t=sMgU6UgXCqKGqjZe-0 sourceIsTS: true --- ```jsx import { V2Cell } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'V2Cell', inputs: [ { prop: 'eyebrow', type: 'string', defaultValue: 'eyebrow' }, { prop: 'heading', type: 'string', defaultValue: 'Cell Heading' }, { prop: 'subheading', type: 'string', defaultValue: 'Subheading' }, { prop: 'paragraph', type: 'string', defaultValue: 'Paragraph content here' }, { prop: 'value', type: 'string', }, { prop: 'indicator', type: 'string', }, ] } ``` ## Leading Content The `leadingContent` prop defines the cell's content on the left-hand side. This may contain the [IconBrand](/mobile/ui/icon-brand) component, a utility icon ([IconSymbol](/mobile/ui/icon-symbol)), or an [Avatar](/mobile/ui/avatar). ```jsx live <> } /> console.log('Cell Pressed')} heading="Primary Care" paragraph="Your first contact for to get care for your health." leadingContent={} /> console.log('Cell Pressed')} subheading="Search your benefits" leadingContent={ } /> ``` ## Main Content The `eyebrow` prop defines the cell's eyebrow. This prop can either be text or a custom component such as `Badge`. The `heading` prop defines the cell's heading. The `subheading` prop defines the cell's subheading. The `paragraph` prop defines the cell's paragraph The `trailingIcon` prop placed an icon at the end of the paragraph. This will not show unless there are also defined `paragraph` and `onPress` props. When this icon is present, only the icon is pressable, not the entire cell. A [link](/mobile/ui/link) can be displayed below the paragraph using the `link` prop. A link may also be passed to the `value` prop to be displayed on the right side of the cell. ```jsx live <> console.log('Help Icon Pressed')} trailingIcon={ } /> Badge} onPress={() => console.log('Cell Pressed')} /> Go to Abyss } /> ``` ## Trailing Content The `value` prop defines the value component on the right side of the cell. This is generally used to display a numerical value but can also display the [Link](/mobile/ui/link) component. If the `value` prop exists, the cell cannot contain an `onPress` function. The `indicator` prop defines the indicator component on the right side of the cell. Unlike `value`, this prop can co-exist with an `onPress` function. Either a string or a custom Component can be passed here. The `navIcon` prop defines the icon that appears on the far right side of the cell. This icon will only exist when an `onPress` function exists. ```jsx live <> Link Value } /> console.log('Cell Pressed')} heading="Cell heading" paragraph="Cell with a string indicator and onPress" indicator="Indicator" /> console.log('Cell Pressed')} subheading="Badge Indicator" indicator={ } > Badge } /> console.log('Cell Pressed')} navIcon={} /> ``` ## isDisabled `isDisabled` is a prop available for type `radio`, `checkbox`, and `toggle`. When `isDisabled` is present in [V2CellGroup](/mobile/ui/cell-group-v2), the entire group is disabled and cannot be modified. However, if `isDisabled` is present in `V2Cell`, only that cell is disabled and cannot be modified. ```jsx live () => { const [checkboxValue, setCheckboxValue] = useState([]); const [radioValue, setRadioValue] = useState('one'); const [disableGroup, setDisableGroup] = useState(false); const [disableCells, setDisableCells] = useState(false); return ( <> ); }; ``` ```jsx render ``` ```jsx render ``` **It is the responsibility of consuming teams to make sure all components within Cell are accessible.**
When possible, please test on physical devices for accessibility accuracy.
```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: cell-group-v2 category: Data Display title: V2CellGroup description: Cells present data in one or more vertically stacked rows. design: https://www.figma.com/design/5djnh49w0SBYAG5tFJifIG/v1.66.0-App-Abyss-Global%E2%80%A8Component-Library?node-id=1242-1385&p=f&t=sMgU6UgXCqKGqjZe-0 sourceIsTS: true --- ```jsx import { V2CellGroup } from '@uhg-abyss/mobile'; ``` ## Type Inheritance When creating a `V2CellGroup` of type `radio`, `checkbox`, or `toggle`, it is important to use the `type` prop. Once a type prop is assigned to a CellGroup, every child will inherit the group's type and have access to all necessary providers preventing errors. Below, you can see a few examples of type inheritance within `V2CellGroup`. Notice how `V2Cell` does not have the `type` prop, inheriting it from the CellGroup. ```jsx live () => { const [isToggleChecked, setIsToggleChecked] = useState(false); const [radioValue, setRadioValue] = useState('one'); const [value, setValue] = useState([]); return ( <> ); }; ``` ## Toggle Switch CellGroup Cells of type `toggle` contain the content of the cell with a ToggleSwitch on the right side. Toggle Cells will return a boolean of the selected value onChange. ```jsx live () => { const [isToggleChecked, setIsToggleChecked] = useState(false); const [isToggleChecked2, setIsToggleChecked2] = useState(true); return ( console.log('info button pressed')} /> ); }; ``` ## Radio Group Cells of type `radio` are required to be wrapped in a `V2CellGroup` with type `radio`. Reference the [Type Inheritance](/mobile/ui/cell-group-v2/#type-inheritance) section above for more information. Radio Cells contain the content of the cell with a radio button on the right side, and return the selected value onChange. Radio `V2CellGroup` requires the `onChange` and `value` props. ```jsx live () => { const [radioValue, setRadioValue] = useState('one'); return ( <> Radio Group Example ); }; ``` ## Checkbox Group Cells of type `checkbox` are required to be wrapped in a `V2CellGroup` with type `checkbox`, reference the [Type Inheritance](/mobile/ui/cell-group-v2/#type-inheritance) section above for more information. Checkbox Cells contain the content of the cell, with a checkbox on the right side. Checkbox Cells works the same as a standard Checkbox Group, returning the selected cell values. `selectAll` is a prop that can exist inside a Cell in a Checkbox CellGroup. This prop creates a select all checkbox that will select/deselect the entire checkbox list. Checkbox `V2CellGroup`'s require the `onChange` and `value` props. ```jsx live () => { const [checkboxValue, setCheckboxValue] = useState([]); return ( <> Checkbox Group Example ); }; ``` ## isDisabled `isDisabled` is a prop available for type `radio`, `checkbox`, and `toggle`. When `isDisabled` is present in `V2CellGroup`, the entire group is disabled and cannot be modified. However, if `isDisabled` is present in `V2Cell`, only that cell is disabled and cannot be modified. ```jsx live () => { const [checkboxValue, setCheckboxValue] = useState([]); const [radioValue, setRadioValue] = useState('one'); const [disableGroup, setDisableGroup] = useState(false); const [disableCells, setDisableCells] = useState(false); return ( <> ); }; ``` ```jsx render ``` **It is the responsibility of consuming teams to make sure all components within CellGroup are accessible.** ```jsx render ``` ```jsx render ``` --- id: cell-group category: Data Display title: CellGroup description: Present data in one or more vertically stacked rows. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=9599%3A34319&t=OIAOmowjKZ4THvqh-0 --- ```jsx import { CellGroup } from '@uhg-abyss/mobile'; ``` ## Type Inheritance When creating a `CellGroup` of type `radio`, `checkbox`, `toggle` or `dragAndDrop`, it is important to use the `type` prop. Once a type prop is assigned to a `CellGroup`, every child `CellGroup.Cell` will inherit the `CellGroups`'s type and have access to all necessary providers preventing errors. Below, you can see a few examples of type inheritance within `CellGroup`. Notice how `CellGroup.Cell` does not have the `type` prop, causing `CellGroup.Cell` to inherit its type from `CellGroup`. ```jsx live () => { const [isToggleChecked, setIsToggleChecked] = useState(false); const [isToggleChecked2, setIsToggleChecked2] = useState(true); const [radioValue, setRadioValue] = useState('one'); const [value, setValue] = useState([]); return ( <> ); }; ``` ## Cell Customization The `icon` prop defines the cell's icon on the left-hand side. Either IconBrand or IconSymbol can be used here. When using IconBrand, you can customize the icon's background color using the `iconBackgroundColor` prop. The `eyebrow` prop defines the cell's eyebrow. This prop can either be text or a custom component such as `Badge`. The `title` prop defines the cell's title. The title's fontWeight can be modified via the `titleWeight`, `titleColor` and the `highlight` prop. The `highlight` prop will highlight the cell title to make it stand out. If your cell does not contain any `onPress` functionality, you can pass in the `onInfoButtonPress` prop, which will cause an info button to populate to the right side of the title. The `subtitle` prop defines the cell's subtitle. The `description` prop defines the cell's description The `footer` prop defines the component that lives at the bottom of the cell's content section. This is generally used to add either a Link or a Badge. The `value` prop defines the value component on the right side of the cell. If the `value` prop exists, the cell cannot contain an `onPress` function. Either a string or a custom component can be passed here. This is generally used to display a numerical value or a link. The `indicator` prop defines the indicator component on the right side of the cell. Unlike `value`, this prop can co-exist with an `onPress` function. Either a string or a custom Component can be passed here. The `pressIcon` prop defines the icon that appears on the far right side of the cell. This icon will only exist when an `onPress` function exists. ```jsx live () => { const [isToggleChecked, setIsToggleChecked] = useState(false); const [isToggleChecked2, setIsToggleChecked2] = useState(true); const [radioValue, setRadioValue] = useState('one'); const [value, setValue] = useState([]); return ( console.log('Cell Pressed')} pressIcon={} /> console.log('Cell Pressed')} eyebrow="CELL eyebrow" title="Cell Title" description="Cell description" iconBackgroundColor="$pastel1" titleWeight="$bold" icon={ } > This is a badge console.log('Cell Pressed')} eyebrow={Badge} title="Cell Title" subtitle="Cell subtitle" description="Cell description" titleWeight="$bold" icon={ } /> } footer={ Link } /> console.log('Cell Pressed')} title="Cell Title" description="A segmented control is a linear set of two or more segments" titleWeight="$bold" icon={} /> console.log('Cell Pressed')} title="Cell Title" description="Cell description" indicator="Indicator" /> console.log('Cell Pressed')} title="Cell Title" description="A segmented control is a linear set of two or more segments" indicator={ } > Badge } /> Link } /> console.log('info button pressed')} title="Cell Title" /> ); }; ``` ## Cell Header For cells requiring a category title, use the `CellGroup.CellHeader` component within the `CellGroup`.

`CellHeader` has the following props: - `title` - the title of the cell category. - `value` - place content, such as a link, on the right side of the header cell. - `onInfoButtonPress` - info button populates on the right side of the title. ```jsx live () => { const cellPressed = () => { console.log('Cell Pressed'); }; return ( } /> } /> { console.log('info button pressed'); }} /> } /> { return console.log('See all pressed'); }} > See All } /> } /> ); }; ``` ## Border Variants Cell borders can be manipulated using the below props: `hideBorderTop` | hides the border of the top cell. `hideBorderBottom` | hides the border of the bottom cell. `hideBorderAll` | hides the border of every cell. `fullBorder` | makes border for every cell extend the full width of the parent. ```jsx live () => { return ( console.log('Cell Pressed')} title="Hide Border Top" /> console.log('Cell Pressed')} title="Hide Border Top" /> console.log('Cell Pressed')} title="Hide Border Bottom" /> console.log('Cell Pressed')} title="Hide Border Bottom" /> console.log('Cell Pressed')} title="Hide Border All" /> console.log('Cell Pressed')} title="Hide Border All" /> console.log('Cell Pressed')} title="Border Full" /> console.log('Cell Pressed')} title="Border Full" /> ); }; ``` ## Toggle Switch Cell Cells of type `toggle` contain the content of the cell with a ToggleSwitch on the right side. Toggle Cells will return a boolean of the selected value onChange. ```jsx live () => { const [isToggleChecked, setIsToggleChecked] = useState(false); const [isToggleChecked2, setIsToggleChecked2] = useState(true); return ( console.log('info button pressed')} /> ); }; ``` ## Radio Group Cells of type `radio` are required to be wrapped in a `CellGroup` with type `radio`. Reference the `Type Inheritance` section above for more information. Radio Cells contain the content of the cell with a radio button on the right side, and return the selected value onChange. When using the type `radio` use the [Cell Header](/mobile/ui/cell-group/#cell-header) component for the group label. Radio `CellGroup` requires the `onChange` and `value` props. ```jsx live () => { const [radioValue, setRadioValue] = useState('one'); return ( ); }; ``` ## Checkbox Group Cells of type `checkbox` are required to be wrapped in a `CellGroup` with type `checkbox`, reference the `Type Inheritance` section above for more information. Checkbox Cells contain the content of the cell, with a checkbox on the right side. Checkbox Cells works the same as a standard Checkbox Group, returning the selected cell values. `selectAll` is a prop that can exist inside a Cell in a Checkbox CellGroup. This prop creates a select all checkbox that will select/deselect the entire checkbox list. Checkbox `CellGroup`'s require the `onChange` and `value` props. When using the type `checkbox` use the [Cell Header](/mobile/ui/cell-group/#cell-header) component for the group label. ```jsx live () => { const [checkboxValue, setCheckboxValue] = useState([]); return ( ); }; ``` ## isDisabled `isDisabled` is a prop available for type `radio` and `checkbox`. When `isDisabled` is present in `CellGroup`, the entire group is disabled and cannot be modified. However, if `isDisabled` is present in `CellGroup.Cell`, only that cell is disabled and cannot be modified. ```jsx live () => { const [checkboxValue, setCheckboxValue] = useState([]); const [radioValue, setRadioValue] = useState('one'); const [disableGroup, setDisableGroup] = useState(false); const [disableCells, setDisableCells] = useState(false); return ( ); }; ``` ## Custom Cells Cells can be customized to fit design needs by passing it children and leaving the values empty for any combination of the `title`, `subtitle`, `description`, `eyebrow`, `icon`, and `linkText` properties. ```jsx live () => { return ( } > {'12/19/2022'} } > Get Started } > } > See the Designs ); }; ``` ## CellCreator & DragAndDrop This type of cell cannot be used inside a ScrollView. `CellCreator` will create a new `DragAndDrop` Cell every time the blue plus is clicked. `DragAndDrop` cells can be deleted via the red button on the left side, and dragged by clicking and holding the icon on the right side. When the close button is pressed, the callback `onRemoveCell` will be called. The callback will contain the ID of the cell to be deleted. When the add cell button is pressed, the callback `onAddCell` will be called. This function should create a new cell with a title and unique id. Use the `data` prop to define the active cells. The `setData` prop is required for drag and drop functionality. ```jsx live () => { const keyRef = useRef(2); const [childCells, setChildCells] = useState([ { id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }, ]); const addCell = () => { keyRef.current = keyRef.current + 1; const newChildCell = { text: `New Item ${keyRef.current}`, id: keyRef.current, }; setChildCells([...childCells, newChildCell]); }; const removeItem = (id) => { setChildCells(childCells.filter((item) => item.id !== id)); }; return ( removeItem(e)} /> {Object.keys(childCells).map((key) => ( ID: {childCells[key].id} | Text: {childCells[key].text} ))} ); }; ``` ## Cell Group Examples ```jsx live () => { const [radioValue, setRadioValue] = useState('one'); const [radioValue2, setRadioValue2] = useState('one'); const [isToggleChecked, setIsToggleChecked] = useState(false); const [isToggleChecked2, setIsToggleChecked2] = useState(true); const [checkboxValue, setCheckboxValue] = useState([]); const [checkboxValue2, setCheckboxValue2] = useState([]); const [checkboxValue3, setCheckboxValue3] = useState([]); return ( <> console.log('info button pressed')} title="Cell Title" /> console.log('info button pressed')} title="Cell Title" pressIcon={} /> console.log('info button pressed')} onPress={() => { console.log('double'); }} title="Cell Title" /> console.log('info button pressed')} title="Cell Title" /> console.log('info button pressed')} eyebrow="CELL eyebrow" title="Cell Title" description="A really long cell description that will wrap. A really long cell description that will wrap. A really long cell description that will wrap. A really long cell description that will wrap. A really long cell description that will wrap. A really long cell description that will wrap. A really long cell description that will wrap." iconBackgroundColor="$pastel1" titleWeight="$bold" icon={ } > This is the badge console.log('info button pressed')} eyebrow={Badge} title="Cell Title" subtitle="Cell subtitle" description="Cell description" titleWeight="$bold" icon={ } /> console.log('info button pressed')} title="Cell Title" subtitle="Cell subtitle" description="Cell description" titleWeight="$bold" icon={ } /> } footer={ Link } /> Badge} title="Cell Title" subtitle="Cell subtitle" description="Cell description" titleWeight="$bold" iconBackgroundColor="$pastel1" icon={ } /> console.log('info button pressed')} title="Cell Title" description="Cell description" titleWeight="$bold" titleColor="$primary1" icon={} /> console.log('info button pressed')} title="Cell Title" description="Cell description" icon={} /> console.log('info button pressed')} title="Cell Title" description="A really long cell description that will wrap A really long cell description that will wrap A really long cell description that will wrap A really long cell description that will wrap A really long cell description that will wrap A really long cell description that will wrap " indicator="Indicator" /> console.log('info button pressed')} title="Cell Title" description="Cell description" indicator={Indicator} /> console.log('info button pressed')} title="Cell Title" description="Cell description" indicator={ } > Badge } /> Link } /> console.log('info button pressed')} /> }> {'MM/DD/YYYY'} } > {'Minneapolis, MN'} ); }; ```
```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` **It is the responsibility of consuming teams to make sure all components within CellGroup are accessible.** --- id: checkbox category: Forms title: Checkbox description: Used to mark an option as true/checked or false/not checked design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=9602%3A34536 --- ```jsx import { Checkbox } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Checkbox', inputs: [ { prop: 'label', type: 'string', }, { prop: 'align', type: 'select', options: [ { label: 'left', value: 'left' }, { label: 'right', value: 'right' }, ], }, { prop: 'isDisabled', type: 'boolean', }, { prop: 'isIndeterminate', type: 'boolean', }, ] } () => { const [isChecked, setChecked] = useState(true); return ( ); }; ``` ## States - Default - The default checkbox is unchecked. - Checked - Use the `isChecked` prop to mark a checkbox as checked. - Indeterminate - Use the `isIndeterminate` prop to set the checkbox as indeterminate. - Disabled - Use the `isDisabled` prop to disable a checkbox. A disabled checkbox is unusable and un-clickable. - Help Text - Use the `helpText` prop to insert helpful text below the checkbox. - Error Message - Use the `errorMessage` prop to display a custom error message below the checkbox. ```jsx live () => { const form = useForm({ defaultValues: { indeterminate: true, 'indeterminate-disabled': true, 'disabled-checked': true, }, }); return ( ); }; ``` ## useForm (Recommended) Using the `useForm` hook allows you to easily manage form state and validation. ```jsx live () => { const form = useForm(); const onSubmit = (data) => { console.log('submitted', data); }; return ( ); }; ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [isChecked, setChecked] = useState(false); return ( ); }; ``` ## Align The `align` prop determines which side the checkbox is on. The options are `left` or `right`. When the align prop is set to `right`, the label stays on the left and only the checkbox is set to the rightmost edge of it's container. The default is `left`. ```jsx live () => { const form = useForm({ defaultValues: { leftAlignedCheckbox: true, rightAlignedCheckbox: true, }, }); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type The checkbox icon scales up to 3XL, while any text passed in scales according to Abyss dynamic type standards. ```jsx render ``` --- id: checkbox-group category: Forms title: CheckboxGroup description: Allows a user to select one or multiple items from a list. --- ```jsx import { CheckboxGroup } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'CheckboxGroup', inputs: [ { prop: 'align', type: 'select', options: [ { label: 'left', value: 'left' }, { label: 'right', value: 'right' }, ], }, { prop: 'isDisabled', type: 'boolean', }, ] } () => { const [value, setValue] = useState([]); const handlePress = () => { console.log(value); }; return ( setValue(value)}> ); }; ``` ## useForm (Recommended) Using the `useForm` hook allows you to manage the state of the checkbox group more effectively, especially when dealing with forms. ```jsx live () => { const form = useForm({ defaultValues: { 'checkbox-form': ['two'], }, }); const onSubmit = (data) => { console.log('submitted', data); }; return ( ); }; ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [value, setValue] = useState(['two']); return ( ); }; ``` ## Value Checkboxes within a `CheckboxGroup` component require the `value` prop to be specified in order to function as part of the checkbox group. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Select All Use the `CheckboxGroup.SelectAll` component to control the checked state for the entire group. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Disabled Use the `isDisabled` prop to disable the entire group. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Align The `align` prop determines which side the checkbox is on for the entire group. The options are `left` or `right`. When the align prop is set to `right`, the label stays on the left and only the checkbox is set to the rightmost edge of it's container. The default is `left`. ```jsx live () => { const form = useForm(); const [align, setAlign] = useState(true); return ( ); }; ``` ## Multi Select Card A child component can be used instead of a traditional checkbox label. The `label` prop is removed and a component is added as a child of each checkbox. See [Card](/mobile/ui/card/#Section) for more details on the Card used below. ```jsx live () => { const form = useForm(); return ( Select All Claims (4) $278.89 Dr. Sharon Tang $93.22 Date of Service 5/11/23 Claim Information Walgreens #927956 $127.93 Date of Service 5/5/23 Claim Information Dr. Edward M Jenner $25.00 Date of Service 10/30/22 Claim Information Odin Medical Group $32.74 Date of Service 12/9/22 Claim Information ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: chip category: Data Display title: Chip description: Chips are clickable, and used for filtering and selections. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=11953-60547&t=N5JdMpNlL65N19qO-0 --- ```jsx import { Chip } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Chip', inputs: [ { prop: 'children', type: 'string', }, { prop: 'isDisabled', type: 'boolean', }, { prop: 'isClosable', type: 'boolean', }, { prop: 'isTag', type: 'boolean', }, { prop: 'isChecked', type: 'boolean', }, ], }; Chip; ``` ## useState Pass the value from the `useState` hook to the `isChecked` prop to set the checked state of the chip. ```jsx live () => { const [isChecked, setIsChecked] = useState(false); return ( setIsChecked(!isChecked)}> Chip ); }; ``` ## Group Group has three variants: `wrap`, `scroll` and `fit`. The `wrap` variant is the default. ### Wrap Chips can be wrapped in a `Group`. When using this to group multiple chips together, a chip that is too long to stack horizontally wraps to the next line. This variant allows any number of chips to be selected when each chip has an `isChecked` and `onChange`. Alternatively, passing the state into the `Group` (and not individual chips) will allow only one chip to be selected at a time. ```jsx live () => { const [isChecked, setIsChecked] = useState(false); const [isChecked2, setIsChecked2] = useState(false); const [isChecked3, setIsChecked3] = useState(false); const [isChecked4, setIsChecked4] = useState(false); return ( setIsChecked(!isChecked)}> Default Chip setIsChecked2(!isChecked2)}> A long time ago in a galaxy far, far away setIsChecked3(!isChecked3)} icon={ } > A long time ago in a galaxy far, far away setIsChecked4(!isChecked4)}> Chip ); }; ``` ### Scroll This variant has a filter button for selection and the chips scroll horizontally. Only one chip can be selected in this group. The `title` prop is used to display a title on the bottom sheet. ```jsx live () => { const [val, setVal] = useState('one'); return ( Chip 1 Chip 2 Chip 3 Chip 4 Chip 5 Chip 6 ); }; ``` ### Fit This variant does not scroll or have a filter button. The chips will fit the width of the parent container. Like the wrap variant, multiple chips can be selected when each chip has an `isChecked` and `onChange`, and only one chip can be selected when the state is passed into the `Group` (as shown). ```jsx live () => { const [val, setVal] = useState('one'); return ( Chip 1 Chip 2 Chip 3 ); }; ``` ## Icons Use the `icon` prop to pass in a specific Icon component. Icons should be 20px and given an accurate title to meet accessibility standards. Find further guidance on material icons in the [Material Icons Tab](/mobile/ui/icon-material/). ```jsx live () => { const [isChecked, setIsChecked] = useState(false); return ( setIsChecked(!isChecked)} icon={ } > Chip ); }; ``` ## Dismissible Chips Use the `isClosable` prop with the `onClose` function to allow a chip to be dismissed. The checked and pressed states are not enabled with a dismissible chip. ```jsx live () => { const [shouldShow, setShouldShow] = useState(true); return ( {shouldShow && ( setShouldShow(false)}> Close Me )} ); }; ``` ## Disabled Use the `isDisabled` prop to disable a chip. ```jsx live } isDisabled={true} > Disabled Chip ``` ## Tag Use the `isTag` prop to create a non-clickable chip. ```jsx live Tag ``` ## Width Chips do not wrap if the text gets longer than the width of the parent container. Instead, the text will truncate. ```jsx live () => { const [isChecked, setIsChecked] = useState(false); return ( setIsChecked(!isChecked)} icon={ } > A long time ago in a galaxy far, far away ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: coachmark-v2 category: CTA title: V2Coachmark description: A temporal message that provides contextual information or help. design: https://www.figma.com/design/5djnh49w0SBYAG5tFJifIG/branch/A6jVbwG9VlIxsupoiwvRjs/v1.72.0-App-Abyss-Global%E2%80%A8Component-Library?node-id=1360-50043&m=dev subDirectory: Coachmark/v2 sourceIsTS: true --- ```jsx import { V2Coachmark } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'V2Coachmark', inputs: [ { prop: 'offset', type: 'number', }, { prop: 'heading', type: 'string', }, { prop: 'children', type: 'string', }, { prop: 'position', type: 'select', options: [ { label: 'Above', value: 'above' }, { label: 'Below', value: 'below' }, ], }, { prop: 'type', type: 'select', options: [ { label: 'Dark', value: 'dark' }, { label: 'Light', value: 'light' }, { label: 'Light Border', value: 'light-border' }, ], default:'light-border' }, { prop: 'isVisible', type: 'boolean', }, { prop: 'dismissible', type: 'boolean', }, ] } Coachmark text ``` ## Usage Coachmarks always take the full width of the screen, should be placed with an 8px horizontal margin, and appear above or below the content it is pointing to. Content along the flat side should have a 16px margin from coachmark. ```jsx live Coachmark text goes here, can be a 100 characters maximum and expands on no more than four lines ``` #### Coachmark Tour For creating guided tours with multiple sequential coachmarks, use the [CoachmarkTour](./CoachmarkTour.mdx) component. CoachmarkTour extends V2Coachmark functionality while providing: - **Automatic step management**: Handles navigation between multiple coachmarks - **Built-in controls**: Provides Previous/Next buttons and step counting - **Coachmark positioning**: Calculates the best position and offset for each coachmark based on available screen space - **Tour state management**: Manages the overall tour lifecycle (start, skip, complete) Use V2Coachmark for standalone contextual help and CoachmarkTour for multi-step guided experiences. ## Position Use the `position` prop to display the notch either above or below the coachmark. The default is `"above"`. ```jsx live This coachmark appears above the content pointing down Content This coachmark appears below the content pointing up ``` ## Type Variants Use the `type` prop to change the visual appearance of the coachmark. Available options are `"dark"`, `"light"`, and `"light-border"`. The default is `"light-border"`. ```jsx live Dark coachmark with white text Light coachmark with dark text Light coachmark with dark text and visible border ``` ## Offset Use the `offset` prop to change the horizontal position of the notch. It is determined as a percent from the left edge of the coachmark. The default is `50`. ```jsx live () => { const [offset, setOffset] = useState(0); return ( The notch can be adjusted to point to specific content. 0 sets the notch 20px from the left and 100 sets the notch 20px from the right. ); }; ``` ## Heading and Content The V2Coachmark supports both a heading and body content. Use the `heading` prop for the title and pass the body content as `children`. ```jsx live This is the body content that provides additional details about the feature being highlighted. ``` ## Footer Content Use the `footer` prop to add custom footer content. **Note:** For guided tours with multiple coachmarks, consider using the [CoachmarkTour component](./CoachmarkTour.mdx) which provides built-in navigation controls and step management. ```jsx live () => { const [currentStep, setCurrentStep] = useState(1); const handleNext = () => { setCurrentStep(currentStep + 1); }; const handlePrev = () => { setCurrentStep(currentStep - 1); }; return ( handleNext()} onPrevious={() => handlePrev()} onComplete={() => setCurrentStep(1)} /> } /> Content Card 1 Content Card 2 handleNext()} onPrevious={() => handlePrev()} onComplete={() => setCurrentStep(1)} /> } > {`This coachmark is inside Content Card and is currently on step ${currentStep}.`} ); }; ``` ## Dismissible Use the `dismissible` prop to control whether the close button is shown. Set to `false` to hide the close button for non-dismissible coachmarks. ```jsx live <> This coachmark cannot be dismissed by the user. This coachmark can be dismissed by the user. ``` ## onClose Use the `onClose` prop to handle the action when the close button is pressed. Built into V2Coachmark is a fade out animation. ```jsx live () => { const [showCoachmark, setShowCoachmark] = useState(true); const handleClose = () => { setShowCoachmark(false); console.log('Coachmark closed'); }; return ( Press the close button for coachmark to fade out. setShowCoachmark(true)} > Show Coachmark ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: coachmark category: CTA title: Coachmark description: A temporal message that provides contextual information or help. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=39456-18059&mode=design&t=FXPtfscG0ClF0Tf2-0 sourcePath: Coachmark/v1 --- ```jsx import { Coachmark } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Coachmark', inputs: [ { prop: 'offset', type: 'number', }, { prop: 'children', type: 'string', }, { prop: 'orientation', type: 'select', options: [ { label: 'top', value: 'top' }, { label: 'bottom', value: 'bottom' }, ], }, { prop: 'colorScheme', type: 'select', options: [ { label: 'Light', value: 'light' }, { label: 'Dark', value: 'dark' }, ], }, { prop: 'isVisible', type: 'boolean', }, { prop: 'showBorder', type: 'boolean', }, ] } Coachmark text ``` ## Usage Coachmarks always take the full width of the screen, should be placed with an 8px horizontal margin, and appear above or below the content it is pointing to. Content along the flat side should have a 16px margin from coachmark. ```jsx live Coachmark text goes here, can be a 100 characters maximum and expands on no more than four lines ``` ## Orientation Use the `orientation` prop to display the notch either on the top or bottom of the coachmark. The default is `"top"`. ```jsx live Coachmark Content below coachmark ``` ## Offset Use the `offset` prop to change the horizontal position of the notch. It is determined as a percent from the left edge of the coachmark. The default is `50`. ```jsx live The notch should be center aligned with the element on the screen it refers to. 0 sets the notch 20px from the left and 100 sets the notch 20px from the right. ``` ## onClose Use the `onClose` prop to handle the action when the close button is pressed. Built into Coachmark is a fade out animation. ```jsx live () => { const [showCoachmark, setShowCoachmark] = useState(true); const handleClose = () => { setShowCoachmark(false); console.log('Coachmark closed'); }; return ( Press the close button for coachmark to fade out. Any content below coachmark should have a slide up animation to fill the remaining space. ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: coachmark-tour category: CTA title: CoachmarkTour description: A guided tour system that displays contextual coachmarks to walk users through multiple steps of an interface. design: https://www.figma.com/design/5djnh49w0SBYAG5tFJifIG/branch/A6jVbwG9VlIxsupoiwvRjs/v1.72.0-App-Abyss-Global%E2%80%A8Component-Library?node-id=1360-50043&m=dev sourceIsTS: true --- ```jsx import { CoachmarkTour } from '@abyss/mobile'; ``` ## Basic Usage The CoachmarkTour system consists of two main components: `CoachmarkTour` (the provider) and `CoachmarkTour.Step` (wrapper for target elements). The tour displays V2Coachmarks in sequence to guide users through your interface. **Important:** The `children` prop in `CoachmarkTour.Step` is different from `V2Coachmark`'s `children` prop. In `CoachmarkTour.Step`, the `children` prop contains the target element to highlight, while the coachmark content should be passed via the `description` prop. This is unlike `V2Coachmark` where the `children` prop contains the coachmark content itself. ```jsx live () => { const [isStarted, setIsStarted] = useState(false); return ( setIsStarted(false)} onComplete={() => setIsStarted(false)} > setIsStarted(true)}> Start Tour Coachmark Tour Target Content ); }; ``` ## Tour Navigation This example demonstrates navigation controls, multiple steps, position control, and different coachmark types. Users can skip the tour at any time using the close button, and callbacks are provided for navigation events. ```jsx live () => { const [isStarted, setIsStarted] = useState(false); const [tourType, setTourType] = useState('light-border'); return ( { console.log('Tour skipped'); setIsStarted(false); }} onNext={(stepId) => console.log(`Moving to step ${stepId}`)} onPrevious={(stepId) => console.log(`Going back to step ${stepId}`)} onComplete={() => { console.log('Tour completed'); setIsStarted(false); }} > setIsStarted(true)} isDisabled={isStarted}> Start Tour {Array.from({ length: 3 }).map((_, i) => { const stepId = i + 3; return ( Item {stepId - 2} ); })} Step 6: Final Step (Below) ); }; ``` ## Step Ordering Steps are displayed in the order of their `stepId` regardless of their DOM order, giving you full control over the tour flow. ```jsx live () => { const [isStarted, setIsStarted] = useState(false); return ( setIsStarted(false)} onComplete={() => setIsStarted(false)} > setIsStarted(true)}> Start Ordered Tour DOM Order: 1st, Tour Order: 3rd DOM Order: 2nd, Tour Order: 1st DOM Order: 3rd, Tour Order: 2nd ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: container category: Layout title: Container description: A responsive container component that adjusts padding based on screen size and safe area insets. design: https://www.figma.com/design/pXUASUBRjlvl1ZEUVuqniE/Landscape-A11y?node-id=647-37364 --- ```jsx import { Container } from '@uhg-abyss/mobile'; ``` The `Container` adjusts its padding based on the following rules: - **Small Screens (< 480px):** Minimum padding of 16px or the safe area inset, whichever is larger. - **Medium Screens (480px - 1023px):** Minimum padding of 44px or the safe area inset, whichever is larger. - **Large Screens (> 1024px):** Padding is calculated dynamically based on the screen width and safe area insets. ## Usage ```jsx live () => { return ( {Array.from({ length: 24 }).map((span, index) => { return ( ); })} ); }; ``` ```jsx render ``` --- id: date-input category: Forms title: DateInput description: Capture date input from user. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=18955-105856&t=YLpsLvAcbJ0Le6G5-0 --- ```jsx import { DateInput } from '@uhg-abyss/mobile'; ``` Important Note: The scrolling picker portion of this component is currently NOT accessible. The input box is accessible and can be used to enter a date or time with the keyboard. When a screen reader is active, the button to activate the picker will be hidden. ## Usage The `DateInput` component allows users to select a date or time from a picker. The picker will display a calendar for date selection and a time picker for time selection. The time picker is an internal component, but [Calendar](/mobile/ui/calendar) is a separate component that can be used independently. ### Keyboard Entry Users can enter the date or time with the native keyboard by pressing the input box. The date or time entered can be read from the `value` prop. Use the `onSubmit` prop to handle the action when the submit key is pressed. The date input will not accept an entry if it is not in the format of `MM/DD/YYYY` with leading zeros. The time input will not accept an entry if it is not in 12-hour `hh:mm AM/PM` format with leading zeros. ## useState The `useState` hook gets values from the component state. A date value is required and will be displayed in the input box and as the selected date or time on the picker. ```jsx live () => { const [date, setDate] = useState(new Date()); return ( ); }; ``` ## Mode Use the `mode` prop to define the type of picker. The default mode is set to `"date"` ### Date ```jsx live () => { const [date, setDate] = useState(new Date()); return ( ); }; ``` ### Time ```jsx live () => { const [date, setDate] = useState(new Date()); return ( ); }; ``` ## Label Use the `label` prop to display a label above the input menu. ```jsx live () => { const [date, setDate] = useState(new Date()); return ( ); }; ``` ## Help Content The `helpContent` prop is used to display a help icon in the top right of the container, which will display the provided content in a BottomSheet when pressed. ```jsx live () => { const [date, setDate] = useState(new Date()); return ( ); }; ``` ## Required Use the `isRequired` prop to display an asterisk next to the label. ```jsx live () => { const [date, setDate] = useState(new Date()); return ( ); }; ``` ## Disabled Set the `isDisabled` prop to `true` to disable the date picker. ```jsx live () => { const [date, setDate] = useState(new Date()); return ( ); }; ``` ## Messages Use the `errorMessage` and `successMessage` props to display a custom error or success message below the menu. ```jsx live () => { const [date, setDate] = useState(new Date()); const errorMessage = useRef(true); const successMessage = useRef(false); const handleChange = (newDate) => { if (newDate) { errorMessage.current = false; successMessage.current = true; } else { errorMessage.current = true; successMessage.current = false; } setDate(newDate); }; return ( ); }; ``` ## Excluded Dates To exclude dates use the `excludeDate` prop. Set a function that receives date as an argument and returns true if date should be disabled. For example, to disable weekends, check if the day is 0 or 6. ```jsx live () => { const [date, setDate] = useState(new Date()); return ( { return date.getDay() === 0 || date.getDay() === 6; }} /> ); }; ``` ## Min/Max Date ### Date Use the `minimumDate` and `maximumDate` props to set the min and max dates in the Calendar dropdown. ```jsx live () => { const [date, setDate] = useState(new Date()); const minDate = new Date(); const maxDate = new Date(); minDate.setFullYear(minDate.getFullYear() - 1); maxDate.setFullYear(maxDate.getFullYear() + 1); return ( ); }; ``` ### Time Use the `minimumDate` and `maximumDate` props to set the min and max times in the Time dropdown. ```jsx live () => { const [date, setDate] = useState(new Date()); const minDate = new Date(); const maxDate = new Date(); minDate.setHours(7); maxDate.setHours(19); return ( ); }; ``` ## onInvalidEntry Use the `onInvalidEntry` prop to handle the date validation. The function returns an object `{ value: Date, input: string, code: number, message: string }` where `value` is the Date instance of the user's attempted entry, `input` is the string submitted by the user, `code` indicates a custom code that references a specific error and `message` describes the error. The explanation of `code` is noted below: - 0 - Indicates the input date is invalid. - 1 - Indicates the input is before the minimum date. - 2 - Indicates the input is after the minimum date. - 3 - Indicates the input is a disabled date. ```jsx live () => { const setFocus = useSetFocus(); const inputRef = useRef(); const [date, setDate] = useState(new Date()); const [dateMessage, setDateMessage] = useState({ success: '', error: '', }); const minimumDate = new Date(2024, 0, 1); const maximumDate = new Date(2024, 2, 31); const handleInvalidEntry = ({ code, message }) => { setFocus(inputRef); if (code === 0) { setDateMessage({ error: message, success: '', }); } else if (code === 1) { setDateMessage({ error: `${message}: ${minimumDate.toDateString()}`, success: '', }); } else if (code === 2) { setDateMessage({ error: `${message}: ${maximumDate.toDateString()}`, success: '', }); } else if (code === 3) { setDateMessage({ error: message, success: '', }); } }; return ( { return date.getDay() === 6; }} /> ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: default-props-provider category: Providers title: DefaultPropsProvider description: An Abyss component that provides default props to all its child components. --- ```jsx import { DefaultPropsProvider } from '@uhg-abyss/mobile/ui/DefaultPropsProvider'; ``` ```jsx render ``` ## Overview `DefaultPropsProvider` lets you set default props for multiple components in one place. This helps keep your app consistent and reduces repeated code. ```jsx {/* ...children */} ``` ## How it Works The provider uses React Context to pass default props down to child components. Each component uses the `useDefaultProps` hook internally to merge the provider's defaults with its own props, with component-specific props taking precedence. Prop Priority (highest to lowest): - Props passed directly to the component - Default props from DefaultPropsProvider - Component's built-in default props ## Opting Out of Defaults To opt out of the defaults, you can set the `disableDefaultProviderProps` prop to `true` on the component. This will prevent the component from inheriting any default props set by the provider. ## V2Button ```jsx sandbox Provider defaults Provider defaults with color override Opt out of provider defaults ``` --- id: divider category: Layout title: Divider description: Used to add visual or semantic separation between content. design: https://www.figma.com/design/wCMblLsq9TxAQvKzY3EfCt/v1.61.0-App-Abyss-UHC-Component-Library?node-id=65731-2323&p=f&t=5FIU5wnT0w5NeJW3-0 sourceIsTS: true --- ```jsx import { Divider } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Divider', inputs: [ { prop: 'orientation', type: 'select', options: [ { label: 'horizontal', value: 'horizontal' }, { label: 'vertical', value: 'vertical' }, ], defaultValue: 'horizontal' }, { prop: 'margin', type: 'string', default: '$sm', }, { prop: 'height', type: 'string', }, { prop: 'width', type: 'string', }, { prop: 'color', type: 'string', defaultValue: '$divider.color.surface.thin' }, ] } ``` ## Usage ```jsx live () => { const VerticalDivider = () => ( ); return ( Abyss Divider Component Add visual separation between content Orientation Width Height Color ); }; ``` ## Orientation Use the `orientation` prop to adjust the orientation to either `horizontal` or `vertical`. The default setting is `horizontal`. ```jsx live ``` ```jsx live ``` ## Width, Height and Margin Use the `width` and `height` props to set the desired sizing dimensions. Depending on the orientation, they default to `2` or `100%` to create a thin line. Use the `margin` prop to set the margin between the divider and the content it is separating. Default is `$sm`. When `horizontal` orientation is selected the settings are applied as follows: - `width` : determines the left-to-right length of the of the divider; default setting is `100%` - `height` : determines the thickness of the divider; default setting is `2px` - `margin`: sets the `marginVertical` property ```jsx live ``` When `vertical` orientation is selected the settings are applied as follows: - `width` : determines the thickness of the divider; default setting is `2px` - `height` : determines the top-to-bottom length of the of the divider; default setting is `100%` - `margin`: sets the `marginHorizontal` property ```jsx live ``` ## Color Use the `color` property to set the color of the divider. The two color tokens fit Abyss design guidelines for thin and thick dividers respectively. The default is set to `thin`. ```jsx live () => { return ( ); }; ``` ```jsx render ``` ```jsx render ``` ## Color Decorative only component -- does not need to meet minimum contrast ratio. ```jsx render ``` --- id: donut-chart-v2 category: Data Viz title: V2DonutChart description: A graphical representation technique that displays data in a circular-shaped graph. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=20095-102112 subDirectory: DonutChart/v2 sourceIsTS: true --- ```jsx import { V2DonutChart } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'V2DonutChart', inputs: [ { prop: 'progress', type: 'number', defaultValue: 20 }, { prop: 'size', type: 'number', defaultValue: 40 }, { prop: 'animationDuration', type: 'number', defaultValue: 500 }, { prop: 'color', type: 'string', defaultValue: '$donut-chart.color.surface.container.green' }, ], } ``` ## Progress The `progress` prop determines how far the accumulator will move. The prop accepts numbers between `0` and `100`. The default is `0`. ```jsx live ``` ## Animation Duration The `animationDuration` prop is used to determine how long the donut chart takes to animate (milliseconds). The default is `500`. ```jsx live () => { const [animated, toggle] = useToggle(false); const progress = animated ? 40 : 0; return ( ); }; ``` ## Color The `color` prop is used to set the color for the donut chart. The default is `$conditional7`. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` Due to React Native limitations, this component enables keyboard access despite not having an interactive element. This component requires an accessibility label for use with a screen reader, which enables keyboard focus. ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: donut-chart category: Data Viz title: DonutChart description: A graphical representation technique that displays data in a circular-shaped graph. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=20095-102112 subDirectory: DonutChart/v1 --- ```jsx import { DonutChart } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'DonutChart', inputs: [ { prop: 'progress', type: 'number', }, { prop: 'size', type: 'number', }, { prop: 'animationDuration', type: 'number', }, ], } ``` ## Progress The `progress` prop determines how far the accumulator will move. The prop accepts numbers between `0` and `100`. The default is `0`. ```jsx live ``` ## Animation Duration The `animationDuration` prop is used to determine how long the donut chart takes to animate (milliseconds). The default is `1000`. ```jsx live () => { const [animated, toggle] = useToggle(false); const progress = animated ? 40 : 0; return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` Due to React Native limitations, this component enables keyboard access despite not having an interactive element. This component requires an accessibility label for use with a screen reader, which enables keyboard focus. --- id: expandable-text-block category: Typography title: ExpandableTextBlock description: Displays a text block that can be expanded or collapsed. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/branch/1Uml7LO8NTEWoBUAl4DTaG/Abyss-Mobile?type=design&node-id=18955-106021&t=DfyeirEdeaLCVZP9-0 --- ```jsx import { ExpandableTextBlock } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'ExpandableTextBlock', inputs: [ { prop: 'children', type: 'string', }, { prop: 'numberOfLines', type: 'number', }, { prop: 'showLess', type: 'boolean', }, ] } Lorem ipsum dolor sit amet, adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Consectetur adipiscing elit pellentesque habitant morbi tristique senectus et. Penatibus et magnis dis parturient montes. Diam in arcu cursus euismod quis viverra nibh cras pulvinar. Lorem mollis aliquam ut porttitor. ``` ## Props and Usage Children are required to use `ExpandableTextBlock`. The `numberOfLines` prop is used to control the number of lines shown while `ExpandableTextBlock` is closed. The default for `numberOfLines` is 2. The `showLess` prop is used to give the consumer the ability to shrink the `ExpandableTextBlock`. The default for `showLess` is `false`. The `onLinkPress` prop can be used when pressing the more/less link needs a callback function. This can take in `expanded` as an argument. ```jsx live () => { const lorum = `Lorem ipsum dolor sit amet, adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Consectetur adipiscing elit pellentesque habitant morbi tristique senectus et. Penatibus et magnis dis parturient montes. Diam in arcu cursus euismod quis viverra nibh cras pulvinar. Lorem mollis aliquam ut porttitor. Mauris augue neque gravida in fermentum et sollicitudin ac. Ultrices tincidunt arcu non sodales neque sodales ut etiam. In hac habitasse platea dictumst. In dictum non consectetur a erat nam at lectus. Sed nisi lacus sed viverra tellus in hac habitasse platea. Vitae ultricies leo integer malesuada nunc vel risus commodo viverra. Sed elementum tempus egestas sed sed risus pretium quam. Tellus id interdum velit laoreet id donec ultrices.`; return ( <> {lorum} { console.log( `The text block is now ${expanded ? 'expanded' : 'collapsed'}.` ); }} > {lorum} ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type Text scales according to Abyss standards. Note that the `numberOfLines` prop also scales according to the font scale. ## Screen Reader Support Accessibility focus may need to be reset to the start of the text paragraph after the "more" button is pressed. This can be done within your `onLinkPress` function. ```jsx render ``` --- id: floating-action-button category: Overlay title: FAB description: Hovers over content to execute a primary action in the application. design: https://www.figma.com/file/anZoHg026SyKJHWGJ7Vf4Q/Abyss-Icons?node-id=1-431&t=O2ewq2cTM45gG9ki-0 --- ```jsx import { FAB } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'FAB', inputs: [ { prop: 'children', type: 'string', }, { prop: 'isActive', type: 'boolean', }, { prop: 'isDisabled', type: 'boolean', }, { prop: 'size', type: 'string', }, ] } Text ``` ## Variants Text can be added to the FAB as a child. ```jsx live () => { return ( Default Chat Active Chat Disabled Chat ); }; ``` ## Example Here we are using react's useState hook to manage FAB's active state. ```jsx live () => { const [isActive, setIsActive] = useState(false); return ( App goes here with FAB in the bottom right {isActive ? The FAB is active : null} { console.log('FAB pressed from parent'), setIsActive(!isActive); }} > Chat ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: filter-button category: CTA title: FilterButton description: A button that serves as an entry point to filtering options. design: https://www.figma.com/design/34gRQMq2NxFgeRynjqd24C/Documentation-%7C-UHC-Abyss-Mobile-App?node-id=104-1483&t=5zdX8H00uo6BCkNJ-0 --- ```jsx import { FilterButton } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'FilterButton', inputs: [ { prop: 'label', type: 'string', }, { prop: 'variant', type: 'select', options: [ { label: 'primary', value: 'primary' }, { label: 'secondary-a', value: 'secondary-a' }, { label: 'secondary-b', value: 'secondary-b' }, ], }, ], } ``` ## Label Use the `label` prop to set the label of the FilterButton. The label can be a filter count or 'filter'. If there is no count, the secondary variants should have the label of 'filter' passed in. ```jsx live () => { const [value, setValue] = useState([]); return ( { console.log('primary FilterButton pressed'); }} /> { console.log('secondary-a FilterButton pressed'); }} /> { console.log('secondary-b FilterButton pressed'); }} /> ); }; ``` ## Variant Use the `variant` prop to change the style of the `FilterButton`. You can set the value to `'primary'`, `'secondary-a'`, and `'secondary-b'`. Please follow design guidelines for each variants use case. ```jsx live Primary } right={ { console.log('primary FilterButton pressed'); }} /> } /> Secondary A { console.log('secondary-a FilterButton pressed'); }} /> Secondary B { console.log('secondary-b FilterButton pressed'); }} /> ``` ```jsx render ``` ```jsx render ``` --- id: font-scale category: Layout title: FontScale description: Used to layout UI elements conditionally by font size. --- ```jsx import { FontScale } from '@uhg-abyss/mobile'; ``` ## Usage Used to conditionally display elements based on the device font scale. The condition is based on the `smallerThan` or `largerThan` props (or both of them at the same time). ```jsx live An icon will appear to the right when the window size is at least extra large: An icon will appear to the right when the window size is less than extra large: An icon will appear to the right when the window size is between large and extra large: ``` ## Smaller Than Use the `smallerThan` prop to specify a font scale that the device must be smaller than for the contents inside the FontScale to display. ```jsx live An icon will appear to the right when the font scale is less than 150%: ``` ## Larger Than Use the `largerThan` prop to specify a font scale that the device must be greater than or equal to for the contents inside the FontScale to display. ```jsx live An icon will appear to the right when the window size is at greater than 90%: ``` ## Preset Scale Values As an alternative to using hardcoded number for `smallerThan` and `largerThan`, you can use preset scale values to ensure consistency across your app. (Scale values are taken from the app's theme configuration.) Possible values are `$xs`, `$sm`, `$md`, `$lg`, and `$xl`. ```jsx live An icon will appear to the right when the window size is at least the size of the $md breakpoint: ``` ```jsx render ``` --- id: footer category: Content title: Footer description: A footer is a component that appears at the bottom of the screen. design: https://www.figma.com/design/34gRQMq2NxFgeRynjqd24C/Documentation-%7C-UHC-Abyss-Mobile-App?node-id=104-2016&p=f&t=jLxszkqX1mcOC0WV-0 sourceIsTS: true --- ```jsx import { Footer } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Footer', inputs: [ { prop: 'variant', type: 'select', defaultValue: 'nested', options: [ { label: 'nested', value: 'nested' }, { label: 'sticky', value: 'sticky' }, ] }, { prop: 'direction', type: 'select', defaultValue: 'vertical', options: [ { label: 'vertical', value: 'vertical' }, { label: 'horizontal', value: 'horizontal' }, ] }, ] }
``` ## Variant Use the `variant` prop to set the variant of the footer. The footer can be `'nested'` or `'sticky'`. ```jsx live Nested Footer
Sticky Footer
``` ## Direction Use the `direction` prop to set the direction of the items in the footer. The footer should house up to three buttons in its vertically staked variant and up to two button in its horizontally stacked variant. ```jsx live Horizontal Footer
Vertical Footer
``` ## Header Use the `header` prop to set the header of the footer. ```jsx live
HSA Available Balance $1,763.20 Payment Towards Balance $426.20 } >
```
```jsx render ``` ```jsx render ``` --- id: form-provider category: Providers title: FormProvider description: Adds form functionality to Abyss inputs. sourceIsTS: true --- ```jsx import { FormProvider } from '@uhg-abyss/mobile'; ``` ## Usage Use `FormProvider` along with the [useForm](/mobile/hooks/use-form) hook in order to better manage your forms and fully utilize the capabilities of form management within Abyss. To achieve this you will need to wrap all form fields and the submission button with the `FormProvider` component and provide state through usage of `useForm`. Please see examples below for additional props to pass into the `FormProvider` and go to [useForm](/mobile/hooks/use-form) for detailed documentation on how to configure your forms and take advantage of all the available features. ```jsx live () => { const form = useForm(); const onSubmit = (data) => { console.log('data', data); // Do something on submit }; return ( ); }; ``` --- id: global-app-process category: Feedback title: GlobalAppProcess description: A type of notification message that communicates system status or background processes. design: https://www.figma.com/design/34gRQMq2NxFgeRynjqd24C/Documentation-%7C-UHC-Abyss-Mobile?node-id=931-196076&node-type=frame&t=ti8wesv8clRgQn1m-0 --- ```jsx import { GlobalAppProcess } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'GlobalAppProcess', inputs: [ { prop: 'label', type: 'string', }, { prop: 'actionText', type: 'string', }, { prop: 'variant', type: 'select', options: [ { label: 'success', value: 'success' }, { label: 'warning', value: 'warning' }, { label: 'error', value: 'error' }, { label: 'info', value: 'info' }, ] }, { prop: 'isVisible', type: 'boolean' }, ] } ``` ## Label Use the `label` prop to set the label of the global app process. Setting a label is required. ```jsx live ``` ## Variants Use the `variant` property to set the color and icon of the `GlobalAppProcess`. The options are `success`, `warning`, `error`, and `info`. All variants have the default icons shown below. `info` is the only variant with a built-in icon animation. ```jsx live ``` ## Icon Use the `icon` property to pass in a specific `Icon` component. Note that if the icon on the `info` variant is replaced it will not animate. ```jsx live } label="Search Title" variant="info" /> ``` ## Button Text Use the `actionText` prop to add a button to the right of the process banner. This will adjust the layout of the banner from centered to stretched. ```jsx live () => { const [isVisible, setIsVisible] = useState(true); return ( { setIsVisible(false); }} /> ); }; ``` ## onActionPress Use the `onActionPress` property to handle the action when the button is pressed. ```jsx live () => { const [isVisible, setIsVisible] = useState(true); return ( { setIsVisible(false); }} /> ); }; ``` ## isVisible Use the `isVisible` prop to change the visibility of the process banner. ```jsx live () => { const [isVisible, setIsVisible] = useState(true); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: grid category: Layout title: Grid description: Provides a brief message about the app processes. --- ```jsx import { Grid } from '@uhg-abyss/mobile'; ``` ## Space Use the `space` prop to determine the amount of space between elements in the grid. ```jsx live ``` ## Span ### Number Regardless of viewport width, the span will remain the same for these columns. Change the span by using [column spans] of the parent container. ```jsx live 12 3 3 3 3 6 6 ``` ### Percent Regardless of viewport width, the span will remain the same for these columns. Change the span by using percentages of the parent container. ```jsx live 100% 33% 33% 33% 20% 20% 20% 20% 20% ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: heading category: Typography title: Heading description: Creates appropriately sized heading elements. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=9470%3A32790 --- ```jsx import { Heading } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Heading', inputs: [ { prop: 'children', type: 'string', defaultValue: 'Heading', }, { prop: 'offset', type: 'select', options: [ { label: '1', value: '1' }, { label: '2', value: '2' }, { label: '3', value: '3' }, { label: '4', value: '4' }, { label: '5', value: '5' }, { label: '6', value: '6' }, ], }, { prop: 'color', type: 'select', options: [ { label: '$primary1', value: '$primary1' }, { label: '$info1', value: '$info1' }, { label: '$gray3', value: '$gray3' }, { label: 'lightseagreen', value: 'lightseagreen' }, { label: '#ff0000', value: '#ff0000' }, ], }, { prop: 'textAlign', type: 'select', options: [ { label: 'left', value: 'left' }, { label: 'center', value: 'center' }, { label: 'right', value: 'right' }, ], }, ] } Heading ``` ## Set Global Heading Font One of the limitations of our library is the inability to install fonts into applications. Because of this, we have reserved a special token, `$heading`, to be added in the createTheme function, which will add the font to all Heading components globally. In the example below, the font 'UHCSerif' is set as the heading font and will now be applied to all Heading components. ```jsx import { ThemeProvider, createTheme } from '@uhg-abyss/mobile'; const theme = createTheme('uhc', { theme: { fonts: { heading: 'UHCSerif', }, }, }); const App = () => { return ...; }; ``` ## Offset If you want to have heading levels relative to the current level, you can provide an offset prop. These are equivalent to using a heading element in HTML. A Heading with an offset of 1 would be the equivalent of an `

`. Headings 1-4 have a default color of `$primary1` and Headings 5 & 6 have a default color of `$black`. You can use `offset={1|2|3|4|5|6}`. ```jsx live Heading 1 Heading 2 Heading 3 Heading 4 Heading 5 Heading 6 ``` ## Level Headings 5 and 6 have an optional property called `level` that allows you to use a lighter version of the text. The default is level 1, the heavier version. The level two prop makes the heading lighter. ```jsx live Heading 5 (level 1) Heading 5 (level 2) Heading 6 (level 1) Heading 6 (level 2) ``` ## Color Use the `color` property to set the color of the text. The default is set to `$primary1`. ```jsx live My Benefits My Benefits My Benefits My Benefits ``` ## Text Align Use the `textAlign` prop to change the alignment of the text. Options include `left`, `center` and `right`. The default is set to `left`. ```jsx live Left Aligned Heading Center Aligned Heading Right Aligned Heading ``` ```jsx render , and so on', default: '1', }, { name: 'textAlign', type: '"left" | "center" | "right"', description: 'Specifies text alignment of the heading text', default: 'left', }, { name: 'color', type: 'string', description: 'Set the color of the heading text', default: 'Headings 1-4: "$primary1", Headings 5-6: "$black"', }, { name: 'animated', type: 'boolean', description: 'Flag to make component animatable', default: 'false', }, { name: 'level', type: '1 | 2', description: 'Set the level of the heading. Used for offset 5 and 6 headings', default: '1', }, { name: 'fontFamily', type: 'string', description: 'Set the font family of the heading', }, ]} /> ``` ```jsx render ``` ```jsx render ``` --- id: home-widget category: Content title: HomeWidget description: A component of an interface, that enables a user to perform a function or access a service. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=12334-61355&t=hhJi0b2ytlH4Ftjo-0 --- ```jsx import { HomeWidget } from '@uhg-abyss/mobile'; ``` ## HomeWidget Card Title & Background Entering a value into the `title` prop will display a header with title and accompanying Background image when applicable. You can customize the Background using the `headerBackground` prop. ```jsx live () => { const CostIcon = styled(Image, { position: 'absolute', zIndex: -20, top: -100, right: -30, width: 550, height: 250, }); const Cost = ({ children, label, number }) => { return ( {label} ${number} {children} ); }; return ( } > console.log('widget has been pressed')} > console.log('widget has been pressed')} > console.log('widget has been pressed')} > console.log('widget has been pressed')} > ); }; ``` ## HomeWidget Customization A title can be added to `HomeWidget` via the `title` prop. Content is added to `HomeWidget` with children. Any content added will be placed inside a horizontal flex container with justifyContent set to `space-between`. By default, content is placed underneath the title, but this can be changed via the `headerAlignment` prop. The `color` prop is used to change the widget color. If a dark color is applied, the text color will adjust to `$white`, this can be overridden via the `titleColor` prop. Use the `activeColor` prop to set the pressed color. If nothing is passed the color will stay the same when pressed. The title position can be adjusted via the `headerAlignment` prop, the title can either be above or below the widget content. The default for `headerAlignment` is `top` A subtitle can be placed below the widget title via the `subtitle` prop. ```jsx live () => { return ( console.log('widget has been pressed')} subtitle="Sub-title" > Total Due $1043.43 console.log('widget has been pressed')} > Total Due $1043.43 console.log('widget has been pressed')} headerAlignment="bottom" > ); }; ``` ## Widget Layout `HomeWidget` will be laid out automatically when placed inside of `HomeWidget.Card`. The widgets will be laid out in a grid with two columns and will grow when given space. `HomeWidget` can be made to occupy the entire width of the card by setting `span={2}`. The widget title will wrap to be two lines if no notification is present, but only one line if a notification is present. ```jsx live () => { return ( console.log('widget has been pressed')} > Available $600 console.log('widget has been pressed')} > Available $425 console.log('widget has been pressed')} > Total Due $1043.43 ); }; ``` ## Custom containers `HomeWidget` can also be used on its own or with a custom container. When standing alone, the `HomeWidget` will grow to full width of its container. [Grid](/mobile/ui/grid) is recommended to control the layout when using a custom container. ```jsx live () => { return ( console.log('widget has been pressed')} headerAlignment="bottom" > console.log('widget has been pressed')} headerAlignment="bottom" > console.log('widget has been pressed')} > Available $600 ); }; ``` ```jsx live () => { return ( console.log('widget has been pressed')} > ); }; ``` ## Example ```jsx live () => { const CostIcon = styled(Image, { position: 'absolute', zIndex: -20, top: -100, right: -30, width: 550, height: 250, }); const Cost = ({ children, label, number }) => { return ( {label} ${number} {children} ); }; const RemainingCost = ({ label, number }) => { return ( {label} ${number + ' '} Remaining ); }; return ( } style={{ padding: 6 }} title="Spending & Rewards" > console.log('widget has been pressed')} > Medical / Rx Dental Vision console.log('widget has been pressed')} > console.log('widget has been pressed')} > console.log('widget has been pressed')} > console.log('widget has been pressed')} subtitle="Medical In-Network" > ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type HomeWidget scales according to Abyss standards. Dynamic type causes some elements to reconfigure in a stacked format. Any [IconBrand](/mobile/brand/uhc/icon-brand/?tab=accessibility) should set `disableScaling={true}`. The internal chevron icon will only scale to 130%. ```jsx render ``` ```jsx render ``` --- id: i18n-provider category: Providers title: I18nProvider description: Used to provide i18n data to the application. --- ```jsx import { I18nProvider } from '@uhg-abyss/mobile/ui/I18nProvider'; ``` ## Usage Abyss supports overriding the default i18n object by using the `I18nProvider` component. The `I18nProvider` component takes a `translations` prop that is an object containing the translations to either override the default translations with or to provide custom translations. The translations object for overrides will be in the following format: ```jsx { [commonWord]: 'Translated Value', [componentName]: { [key]: 'Translated Value', }, } ``` THe `commonWord` key is used to override the default translations for common words used in Abyss component. The `componentName` key with an object is for words within specific Abyss components that allow an additional scope to other keys. Below is an example of a few of the words in our default i18n object: ```jsx { disabled: 'Disabled', submit: 'Submit', SearchBar: { clearText: 'Clear Text', }, TextField: { clear: 'clear', charactersRemaining: '{{count}} characters remaining', }, } ``` When using the `t` function from the [useTranslate](/mobile/hooks/use-translate) hook or the [Translate](/mobile/ui/translate) component, the key will be in dotted notation. For example, the key `'SearchBar.clearText'` will be used to get the value `'Clear Text'` from the i18n object. Our default i18n object can be seen [here](https://github.com/uhc-tech/abyss/blob/main/packages/abyss-mobile/src/tools/i18n/translations/en.ts) ## Example Let's use the [TextField](/mobile/ui/text-field) component as an example. The `TextField` component has a text block that displays the remaining characters available to be typed in the text field when the `maxLength` prop is used. ```jsx render () => { const [value, setValue] = useState('State Default Value'); return ( ); }; ``` Internally, we use the `'TextField.charactersRemaining'` key to get value in our i18n object. This value can be overridden with the `translations` prop in the `I18nProvider` component. Let's change the value of the `'TextField.charactersRemaining'` key to place the amount of characters left at the end of the string. ```jsx live-expanded () => { const [value, setValue] = useState('State Default Value'); return ( ); }; ``` ## Language Translations We can use the same idea to translate text into different languages. ```jsx live () => { const [value, setValue] = useState('Valor por defecto'); return ( ); }; ``` ## Custom I18n You can also use the `I18nProvider` component to not only override the default values used in Abyss components, but you provide your own custom custom values . Those values can then consumed later using the [useTranslate](/mobile/hooks/use-translate) hook or the [Translate](/mobile/ui/translate) component. ```jsx live const MyComponent = () => { const { t } = useTranslate(); return ( {t('myCustomText')} {t('nested.key')} ); }; render(() => { return ( ); }); ``` ## Related Links - [useTranslate](/mobile/hooks/use-translate) - [Translate](/mobile/ui/translate) ```jsx render ``` --- id: icon category: Media title: Icon description: Used to implement icons and adapt their properties. # design: https://www.figma.com/file/tk08Md4NBBVUPNHQYthmqp/Abyss-Design-System?node-id=0%3A10709 --- ```jsx import { Icon } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Icon', inputs: [ { prop: 'size', type: 'string', }, { prop: 'title', type: 'string', }, { prop: 'color', type: 'string', }, ] } () => { const customIcon = ( ` ` ); return ( {customIcon} ); }; ``` ## Usage Use `Icon` to implement custom SVG icons ```jsx live {` `} {` `} ``` ## Colors Use the `color` property to adjust the color of a Google material icon. Theme colors can be found in the [Colors](/brand/colors) documentation section or a hex code can be used. The default color is set to the theme `'interactive1'`. ```jsx live {` `} {` `} ``` ## Size Use the `size` property to adjust the size of an icon by setting it to a specific preset size or number. The default is set to `24px` || `$md`. The size prop can take in px or tokens. Token sizes: `$xs`: 16 `$sm`: 20 `$md`: 24 `$lg`: 40 `$xl`: 48 ```jsx live () => { const customIcon = ` `; return ( {customIcon} {customIcon} {customIcon} ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: icon-material category: Media title: IconMaterial description: Used to implement material icons and adapt their properties. design: https://www.figma.com/file/anZoHg026SyKJHWGJ7Vf4Q/Abyss-Icons?node-id=1%3A432 --- ```jsx import { IconMaterial } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'IconMaterial', inputs: [ { prop: 'icon', type: 'string', }, { prop: 'color', type: 'string', }, { prop: 'size', type: 'string', }, { prop: 'variant', type: 'select', options: [ { label: 'filled', value: 'filled' }, { label: 'outlined', value: 'outlined' }, ], }, ] } ``` ## Icons Use the `icon` property to adjust which icon is being selected. ```jsx live ``` ## Colors Use the `color` property to adjust the color of a Google material icon. Theme colors can be found in the [Colors](/mobile/brand/uhc/colors) documentation section or a hex code can be used. The default color is set to the theme `'interactive1'`. ```jsx live ``` ## Size Use the `size` property to adjust the size of an icon by setting it to a specific number or token. The default size is set to 24. Token sizes: `$xs`: 16 `$sm`: 20 `$md`: 24 `$lg`: 40 `$xl`: 48 ```jsx live ``` ## Material Icon Variants Use the `variant` property to change the style of Material icons. The default variant is `filled`. ```jsx live
filled
outlined
```
```jsx render ``` ```jsx render ``` ```jsx render

Material Icons

``` Abyss uses Google's Material Design System iconography that is simple, modern, friendly, and sometimes quirky. Each icon is created using Google's design guidelines to depict in simple and minimal forms the universal concepts used commonly throughout user interfaces. Ensuring readability and clarity at both large and small sizes, these icons have been optimized for common platforms and display resolutions. The source for these design icons can be found in the Material Icons Library.
```jsx render ``` --- id: icon-symbol category: Media title: IconSymbol description: Used to implement material symbol icons and adapt their properties. design: https://www.figma.com/file/anZoHg026SyKJHWGJ7Vf4Q/Abyss-Icons?node-id=1%3A432 --- ```jsx import { IconSymbol } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'IconSymbol', inputs: [ { prop: 'icon', type: 'string', }, { prop: 'color', type: 'string', }, { prop: 'size', type: 'string', }, { prop: 'variant', type: 'select', options: [ { label: 'filled', value: 'filled' }, { label: 'outlined', value: 'outlined' }, ], }, ] } ``` ## Icons Use the `icon` property to adjust which icon is being selected. ```jsx live ``` ## Colors Use the `color` property to adjust the color of a material symbol icon. Theme colors can be found in the [Colors](/mobile/brand/uhc/colors) documentation section, or a hex code can be used. The default color is set to the theme `'interactive1'`. ```jsx live ``` ## Size Use the `size` property to adjust the size of an icon by setting it to a specific number or token. The default size is set to 24. Token sizes: `$xs`: 16 `$sm`: 20 `$md`: 24 `$lg`: 40 `$xl`: 48 ```jsx live ``` ## Material Symbol Variants Use the `variant` property to change the style of Material symbol icons. The default variant is `filled`. ```jsx live
filled
outlined
```
```jsx render ``` ```jsx render ``` ```jsx render

Material Symbols

```
```jsx render ``` --- id: indicator category: Data Display title: Indicator description: Adds an indicator to wrapped elements. # design: https://www.figma.com/file/tk08Md4NBBVUPNHQYthmqp/branch/sabyctxmnS57eNFcPZQAbi/Abyss-Design-System --- ```jsx import { Indicator } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Indicator', inputs: [ { prop: 'offset', type: 'number', defaultValue: 0 }, { prop: 'overflowCount', type: 'number', defaultValue: 99 }, { prop: 'position', type: 'select', options: [ { label: 'top-start', value: 'top-start' }, { label: 'top-end', value: 'top-end' }, { label: 'bottom-start', value: 'bottom-start' }, { label: 'bottom-end', value: 'bottom-end' }, ], defaultValue: 'top-end', }, { prop: 'color', type: 'string', defaultValue: '$indicator.color.surface.container', }, { prop: 'label', type: 'string', }, ] } Indicator Sandbox ``` ## Offset Use the `offset` prop to change the position of the Indicator. It is useful when the Indicator component is used with children that have border radius. ```jsx live ``` ## Color Use the `color` prop to change the color of the Indicator. Default value is set to `'$error1'`. ```jsx live ``` ## Label Use the `label` prop to add a label to the indicator. A value greater than 99 will display as "99+". Use the `overflowCount` prop to chance the overflow value. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: lagoon-provider category: Providers title: LagoonProvider description: Used to provide Lagoon project table data to the application. --- ```jsx import { LagoonProvider } from '@uhg-abyss/mobile'; ``` ## Usage Applications must be wrapped in the `LagoonProvider` in order to access the Lagoon project data. Through usage of the `useLagoon` hook you can retrieve all table data or pass an optional agrument with the desired table path. ```jsx ``` ## Return all project table data ```jsx live () => { const tableFunctions = useLagoon(); const allData = Object.keys(tableFunctions).map((key) => { return { [key]: tableFunctions[key]() }; }); const obj = Object.assign({}, ...allData); return ( {JSON.stringify(obj, null, 4)} ); }; ``` ## Return data for specified table path ```jsx live () => { const tablePath = 'lagoonprovider-docs-two'; const tableFunction = useLagoon(tablePath); const data = { [tablePath]: tableFunction() }; return ( {JSON.stringify(data, null, 4)} ); }; ``` ## Fallback In the event that Lagoon data cannot be retrieved, a `fallback` prop can be used to pass in data with an object similar to the one expected to be retrieved from the API. ```jsx ``` ```jsx render ``` --- id: layout category: Layout title: Layout description: A single or multi-section container used to display content related to a single subject. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/branch/Sk3MrHYxjT39TKDzDU5LBc/Abyss-Mobile?node-id=20334%3A61355 --- # Overview Layout is a set of organizational components that follow the patterns of Flexbox. ## Layout.Group Used to align elements in a row. ```jsx sandbox { component: 'Layout.Group', inputs: [ { prop: 'space', type: 'number', }, { prop: 'alignLayout', type: 'select', options: [ { label: 'left', value: 'left' }, { label: 'center', value: 'center' }, { label: 'right', value: 'right' }, ], }, { prop: 'alignItems', type: 'select', options: [ { label: 'top', value: 'top' }, { label: 'center', value: 'center' }, { label: 'bottom', value: 'bottom' }, ], }, { prop: 'grow', type: 'boolean', }, ], } Group 1 Group 2 Group 3 ``` ## Layout.Stack Used to align elements in a column. ```jsx sandbox { component: 'Layout.Stack', inputs: [ { prop: 'space', type: 'number', }, { prop: 'alignLayout', type: 'select', options: [ { label: 'left', value: 'left' }, { label: 'center', value: 'center' }, { label: 'right', value: 'right' }, ], }, { prop: 'alignItems', type: 'select', options: [ { label: 'left', value: 'left' }, { label: 'center', value: 'center' }, { label: 'right', value: 'right' }, ], }, { prop: 'grow', type: 'boolean', }, ], } Stack 1 Stack 2 Stack 3 ``` ## Layout.Group and Layout.Stack Props ### Space Use the `space` property to set the spacing for a `Group` or `Stack`. The default is set to `8`. ```jsx live Group Group 1 Group 2 Group 3 Group - 20px space Group 1 Group 2 Group 3 ``` ```jsx live Stack Stack 1 Stack 2 Stack 3 Stack - 20px space Stack 1 Stack 2 Stack 3 ``` ### AlignLayout Use the `alignLayout` property to indicate the horizontal alignment of the items in a `Group` or `Stack`. For a Group, the possible options are `left`, `center`, and `right`. For a Stack, the possible options are `left`, `center`, and `right`. The default is set to `left` in both cases. ```jsx live Group - top align - Default Group Top 1 Group Top 2 Group Top 3 Group - center align Group Default Group Default Group Default Group - bottom align Group Bottom 1 Group Bottom 2 Group Bottom 3 ``` ```jsx live Stack - left align - default Stack Left 1 Stack Left 2 Stack Left 3 Stack - center align Stack Default Stack Default Stack Default Stack - right align Stack Right 1 Stack Right 2 Stack Right 3 ``` ### AlignItems Use the `alignItems` property to indicate the alignment of the items in a `Group` or `Stack`. For a `Group` the vertical alignment is adjusted, whereas for a `Stack` the horizontal alignment is adjusted. For a Group, the possible options are `top`, `center`, and `bottom`. For a Stack, the possible options are `left`, `center`, and `right`. The default is set to `center` in both cases. ```jsx live Group - top align Group Top 1 Group Top 2 Group Top 3 Group - center align - Default Group Default Group Default Group Default Group - bottom align Group Bottom 1 Group Bottom 2 Group Bottom 3 ``` ```jsx live Stack - left align Stack Left 1 Stack Left 2 Stack Left 3 Stack - center align - Default Stack Default Stack Default Stack Default Stack - right align Stack Right 1 Stack Right 2 Stack Right 3 ``` ### Grow Use the `grow` property to indicate whether the grouped components should be stretched to fill the space horizontally. The default is set to `false`. ```jsx live Group - default One Two Three Group - grow Item 1 Item 2 Item 3 ``` ```jsx live Stack - default One Two Three Stack - grow One Two Three ``` ### Shrink Use the `shrink` property to indicate whether the grouped components should be shrunk to fill the space horizontally in a `Group`, to prevent an overflow. The default is set to `false`. ```jsx live Group - default Lorem Ipsum Dolor Lorem Ipsum Dolor Lorem Ipsum Dolor Lorem Ipsum Dolor Group - shrink Lorem Ipsum Dolor Lorem Ipsum Dolor Lorem Ipsum Dolor Lorem Ipsum Dolor ``` ## Layout.Insert Used to place elements before and after a central component. Padding is `$sm` and direction is `row`by default. ```jsx live } after={} padding="$sm" > Center ``` ## Layout.Space Adds a space of height `space`. Default is `16px`. ```jsx live One Two ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: link category: Navigation title: Link description: Used to navigate to other pages, or sections of a page. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=11579-66904&mode=design&t=lQdpLQ0uWdxbRS9p-0 --- ```jsx import { Link } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Link', inputs: [ { prop: 'children', type: 'string', }, { prop: 'size', type: 'select', options: [ { label: 'large', value: 'large' }, { label: 'medium', value: 'medium' }, { label: 'small', value: 'small' }, ], }, { prop: 'href', type: 'string', }, { prop: 'underline', type: 'boolean', }, ] } Link Sandbox ``` **The children passed into the link must be a string or an error will appear.** ## Text Change the children of the link to set the text. Note that the child must be a string. ```jsx live { return ; }} > UHC ``` ## Size Use the `size` prop to set the size of the link. The default is set to `large`. ```jsx live Large Link / 18px Medium Link / 16px Small Link / 14px ``` ## Underline The `underline` prop is used to underline the link text. The default is set to `false`. ```jsx live Normal Link Underlined Link ``` ## Inserting Elements Insert elements into the Link component using the `before` and `after` props. To account for press state color, the `before` and `after` props can accept a function whose arguments contain the properties `color` and `isPressed`. ```jsx live { return ; }} href="https://abyss.uhc.com/" > Abyss Docs { return ( ); }} > Abyss Docs } size="small" > Abyss Docs ``` ## Content Guidelines The informational (left) icon should be used when the link is being used as a Tooltip. Do not use the right icon in this case. Tooltips can appear as icons only, or with text. ```jsx live }> }>What is this? ``` A directional (right) icon should be used when the link will take you to another screen. ```jsx live }>View results ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: loading-spinner-v2 category: Overlay title: V2LoadingSpinner description: Infinite loading spinner. design: https://www.figma.com/design/wCMblLsq9TxAQvKzY3EfCt/v1.66.0-App-Abyss-UHC-Component-Library?node-id=12985-84753&p=f&t=Q9ZAauYOQC2ugiP4-0 sourceIsTS: true --- ```jsx import { V2LoadingSpinner } from '@uhg-abyss/mobile'; ``` ## Overview Loading Spinner requires the `accessibilityLabel` prop to describe what happens while the loading spinner is active. Common labels are 'Submitting Form', 'Downloading Files', 'Content is loading', etc. Be as descriptive as possible. ```jsx ``` ## Size Loading spinner comes in two sizes, `xs` and `sm`. With the `xs` variant being used solely for use on buttons. ```jsx live () => { return ( ); }; ``` ## Color The `color` property allows changing the color of the loading spinner. ```jsx live () => { const [isLoading, setIsLoading] = useState(true); const toggleLoading = () => { setIsLoading(!isLoading); }; return ( ); }; ``` ## Alt The `alt` property changes the color of the loading spinner. ```jsx live () => { return ( ); }; ``` ## Heading Use the `heading` prop to add text below the spinner. Only available when the size is set to `'sm'`. ```jsx live ``` ## Button The Button component has LoadingSpinner integration. Head over to the [Button](/mobile/ui/button) component documentation to learn more. ```jsx live () => { const [isLoading, setIsLoading] = useState(true); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` Following the requirements of WAI-ARIA, Loading Spinner follows the requirements 4.1.3: Status Messages. Status messages are defined by WCAG as messages that provide information on the success or results of a user action, but do not change the user's context (i.e., take focus). Loading Spinner is programmed through the `accessibilityLabel` property, and has been tested using a screen reader to present a status message to assistive technology without receiving focus. Adheres to the Status messages WAI-ARIA design pattern. ```jsx live () => { return ; }; ``` ## Dynamic Type V2LoadingSpinner does not scale. If the `size` prop is set to "xs" and dynamic type scales past 3XL, then `size` will adjust to "sm". ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: loading-spinner category: Overlay title: LoadingSpinner description: Infinite loading spinner. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=16616-88862 --- ```jsx import { LoadingSpinner } from '@uhg-abyss/mobile'; ``` ## Overview Loading Spinner requires the `accessibilityLabel` prop to describe what happens while the loading spinner is active. Common labels are 'Submitting Form', 'Downloading Files', 'Content is loading', etc. Be as descriptive as possible. ```jsx ``` ## Children On size `$lg`, the Loading Spinner takes in and displays a child. For branding or icons, you can go to the Brandmark, Icon Material, or Icon Brand page for options. ```jsx live () => { const [isLoading, setIsLoading] = useState(true); const toggleLoading = () => { setIsLoading(!isLoading); }; return ( ); }; ``` ## Size Loading spinner comes in three sizes, `large, medium, small`. With the `small` variant being used solely for use on buttons. The default setting is set to `size = medium`. ```jsx live () => { const [isLoading, setIsLoading] = useState(true); const toggleLoading = () => { setIsLoading(!isLoading); }; return ( ); }; ``` ## Color The `color` property allows changing the color of the loading spinner. The default color is based on the inherited `colorScheme`. ```jsx live () => { const [isLoading, setIsLoading] = useState(true); const toggleLoading = () => { setIsLoading(!isLoading); }; return ( ); }; ``` ## Label Use the `label` prop to add text below the spinner. ```jsx live ``` ## Button The Button component has Loading Spinner integration. Head over to the [Button](/mobile/ui/button) component documentation to learn more. ```jsx live () => { const [isLoading, setIsLoading] = useState(true); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` Following the requirements of WAI-ARIA, Loading Spinner follows the requirements 4.1.3: Status Messages. Status messages are defined by WCAG as messages that provide information on the success or results of a user action, but do not change the user's context (i.e., take focus). Loading Spinner is programmed through the `accessibilityLabel` property, and has been tested using a screen reader to present a status message to assistive technology without receiving focus. Adheres to the Status messages WAI-ARIA design pattern. ```jsx live () => { const [isLoading, setIsLoading] = useState(true); const toggleLoading = () => { setIsLoading(!isLoading); }; return ( ); }; ``` ## Dynamic Type LoadingSpinner does not scale. If the `size` prop is set to "small" and dynamic type scales past 3XL, then `size` will adjust to "medium". --- id: modal category: Overlay title: Modal description: Appears from the bottom of the screen and fills up the entire screen, requiring user action to clear it. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=15291-83943 --- ```jsx import { Modal } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Modal', inputs: [ { prop: 'children', type: 'string', }, { prop: 'title', type: 'string', }, ] } () => { const [isVisible, setIsVisible] = useState(false); return ( ); } ``` ## useState Pass the value from the `useState` hook to the `isVisible` modal prop to set the open state of the modal. ```jsx live () => { const [modalVisible, setModalVisible] = useState(false); return ( } onActionLeftPress={() => setModalVisible(false)} > My text ); }; ``` ## Title Use the `title` prop to set the title of the modal. For accessibility purposes, a title is required. Please provide a title that accurately describes the content of the modal so a screen reader can provide this description to the user. ```jsx live () => { const [modalVisible, setModalVisible] = useState(false); return ( Custom Text ); }; ``` ## Scrollable Use the `scrollable` prop to make the content scrollable. Setting the prop to `false` changes the Modal's content container from a ScrollView to a View. Defaults to `true`. ## Action Buttons Action Buttons can be placed on either the left or right side of the header using the `actionLeft` and `actionRight` props. Use the `onActionLeftPress` prop to fire a callback when the left action is pressed, and `onActionRightPress` prop to fire a callback when the right action is pressed. If one of the actions is an icon, make sure to use the `title` and `isScreenReadable` props on the icon for accessibility. ```jsx live () => { const [modalVisible, setModalVisible] = useState(false); const showToast = () => { Toast.show({ placement: 'top', text: 'You clicked the right action button', variant: 'info', }); }; return ( } actionRight={ } onActionLeftPress={() => setModalVisible(false)} onActionRightPress={showToast} > The left and right side of the header have an action button Click on the left action button to close the modal Click on the right action button to display a toast message ); }; ``` ## Footer Use the `footer` prop to place content at the bottom of the modal. ```jsx live () => { const [modalVisible, setModalVisible] = useState(false); const showToast = () => { Toast.show({ placement: 'top', text: 'You clicked the right action button', variant: 'info', }); }; return ( } > ); }; ``` ## Modal Section Wrap content in a `Modal.Section` component to add padding to the content. ```jsx live () => { const [modalVisible, setModalVisible] = useState(false); return ( } onActionLeftPress={() => setModalVisible(false)} > This text is inside of the `Modal.Section` component ); }; ``` ## onClose Use `onClose` to close the modal when swiping the modal down on iOS devices. ```jsx () => { const [modalVisible, setModalVisible] = useState(false); return ( } onActionLeftPress={() => setModalVisible(false)} onClose={() => setModalVisible(false)} > Swipe the modal down ); }; ``` ## Advanced Layout Layouts like BottomSheet and Modal can be used in combination with each other to create flows. ```jsx live () => { const team = [ { firstName: 'Michael', lastName: 'White', linkText: 'California', subText: 'MM/DD/YYYY', value: '1', }, { firstName: 'Thomas', lastName: 'Musengwa', linkText: 'Arkansas', subText: 'MM/DD/YYYY', value: '2', }, { firstName: 'Bailey', lastName: 'Surowiec', linkText: 'Illinois', subText: 'MM/DD/YYYY', value: '3', }, { firstName: 'Pablo', lastName: 'Zepeda', linkText: 'California', subText: 'MM/DD/YYYY', value: '4', }, ]; const locations = [ { name: 'Alabama', value: 'AL', }, { name: 'Alaska', value: 'AK', }, { name: 'Arizona', value: 'AZ', }, { name: 'Arkansas', value: 'AR', }, { name: 'California', value: 'CA', }, { name: 'Colorado', value: 'CO', }, { name: 'Connecticut', value: 'CT', }, { name: 'Delaware', value: 'DE', }, { name: 'Florida', value: 'FL', }, { name: 'Georgia', value: 'GA', }, { name: 'Hawaii', value: 'HI', }, { name: 'Idaho', value: 'ID', }, { name: 'Illinois', value: 'IL', }, { name: 'Indiana', value: 'IN', }, { name: 'Iowa', value: 'IA', }, { name: 'Kansas', value: 'KS', }, { name: 'Kentucky', value: 'KY', }, { name: 'Louisiana', value: 'LA', }, { name: 'Maine', value: 'ME', }, { name: 'Maryland', value: 'MD', }, { name: 'Massachusetts', value: 'MA', }, { name: 'Michigan', value: 'MI', }, { name: 'Minnesota', value: 'MN', }, { name: 'Mississippi', value: 'MS', }, { name: 'Missouri', value: 'MO', }, { name: 'Montana', value: 'MT', }, { name: 'Nebraska', value: 'NE', }, { name: 'Nevada', value: 'NV', }, { name: 'New Hampshire', value: 'NH', }, { name: 'New Jersey', value: 'NJ', }, { name: 'New Mexico', value: 'NM', }, { name: 'New York', value: 'NY', }, { name: 'North Carolina', value: 'NC', }, { name: 'North Dakota', value: 'ND', }, { name: 'Ohio', value: 'OH', }, { name: 'Oklahoma', value: 'OK', }, { name: 'Oregon', value: 'OR', }, { name: 'Pennsylvania', value: 'PA', }, { name: 'Rhode Island', value: 'RI', }, { name: 'South Carolina', value: 'SC', }, { name: 'South Dakota', value: 'SD', }, { name: 'Tennessee', value: 'TN', }, { name: 'Texas', value: 'TX', }, { name: 'Utah', value: 'UT', }, { name: 'Vermont', value: 'VT', }, { name: 'Virginia', value: 'VA', }, { name: 'Washington', value: 'WA', }, { name: 'West Virginia', value: 'WV', }, { name: 'Wisconsin', value: 'WI', }, { name: 'Wyoming', value: 'WY', }, ]; const [data, setData] = useState(team); const [value, setValue] = useState(data[0].value); const [member, setMember] = useState(data[0]); const [isVisible, setIsVisible] = useState(false); const [showModal, setShowModal] = useState(false); const getCurrentMember = (data, val) => { return data.find(({ value }) => value === val); }; const getLocation = (locations, val) => { return locations.find(({ value }) => value === val); }; const showToastMessage = () => { Toast.show({ text: 'Member changed', variant: 'success', }); }; const handlePress = () => { setIsVisible(true); }; const updateTeam = (newLocation) => { const newArr = data.map((member) => { if (member.value === value) { member.linkText = newLocation; } return member; }); setData(newArr); }; const handlePressLink = (value) => { const currentMember = getCurrentMember(data, value); setValue(currentMember.value); setShowModal(true); }; const handleButtonPress = () => { const currentMember = getCurrentMember(data, value); setMember(currentMember); setIsVisible(false); showToastMessage(); }; const handleCellPress = (val) => { const newLocation = getLocation(locations, val); updateTeam(newLocation.name); const currentMember = getCurrentMember(data, value); Toast.show({ text: currentMember.firstName + "'s location updated to " + newLocation.name + '!', variant: 'success', }); }; const TextView = styled('View', { justifyContent: 'center', paddingLeft: 9, }); const Content = styled('View', { alignItems: 'center', justifyContent: 'space-between', flexDirection: 'row', backgroundColor: '$primary1', padding: '$md', }); return ( {'For ' + member.firstName} {member.linkText} setIsVisible(false)} title={'Select a member'} footer={} > {data.map( ({ linkText, value, firstName, lastName, subText }, i) => ( } > {subText && ( {subText} )} {linkText && ( handlePressLink(value)} after={ } > {linkText} )} ) )} setShowModal(false)} actionLeft={ } onActionLeftPress={() => { setShowModal(false); }} > {locations.map(({ value, name }) => ( handleCellPress(value)} iconRight={ } > ))} ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ## Focus Guidance Abyss does not control the focus of components on the screen when the Modal is toggled off. To meet accessibility guidelines, the focus must be set to the previous node when closed. The [useSetFocus](/mobile/hooks/use-set-focus) hook can be used for this. For example, if a button is pressed to open a Modal, focus must return to that button once the Modal is closed, so that a screen reader or keyboard user may continue using the app where they left off. ```jsx render ``` --- id: navigation-container category: Navigation title: NavigationContainer description: Responsible for managing app state and linking your top-level navigator to the app environment. --- ```jsx import { NavigationContainer } from '@uhg-abyss/mobile'; ``` The container takes care of platform specific integration and provides various useful functionality: - Deep link integration with the linking prop. - Notify state changes for screen tracking, state persistence etc. - Handle system back button on Android by using the BackHandler API from React Native. ## Usage ```jsx import { NavigationContainer, createStackNavigator } from '@uhg-abyss/mobile'; const Stack = createStackNavigator(); export default function App() { return ( {/* ... */} ); } ``` ## Initial State The `initialState` prop accepts initial state for the navigator. This can be useful for cases such as deep linking, state persistence etc. **Example:** ```jsx {/* ... */} ``` Providing a custom initial state object will override the initial state object obtained via linking configuration or from browser's URL. If you're providing an initial state object, make sure that you don't pass it on web and that there's no deep link to handle. ## State Change The `onStateChange` prop accepts a function that gets called every time navigation state changes. It receives the new navigation state as the argument. You can use it to track the focused screen, persist the navigation state etc. **Example:** ```jsx console.log('New state is', state)} > {/* ... */} ``` ## onReady The `onReady` prop accepts a function which is called after the navigation container and all its children finish mounting for the first time. You can use it for: Making sure that the ref is usable. See docs regarding initialization of the ref for more details. Hiding your native splash screen **Example:** ```jsx console.log('Navigation container is ready')} > {/* ... */} ``` ## onUnhandledAction The `onUnhandledAction` prop accepts a function which is called when a navigation action is not handled by any of the navigators. By default, a development-only error message will be shown when an action was not handled. You can override the default behavior by providing a custom function. ## Linking The `linking` props handles configuration for linking integration used for deep linking, URL support in browsers etc. **Example:** ```jsx import { NavigationContainer } from '@uhg-abyss/mobile'; function App() { const linking = { prefixes: ['https://mychat.com', 'mychat://'], config: { screens: { Home: 'feed/:sort', }, }, }; return ( Loading...}> {/* content */} ); } ``` ## Fallback The `fallback` prop is a React Element to use as a fallback while we resolve deep links. Defaults to `null`. If you have a native splash screen, please use `onReady` instead of fallback prop. **Example:** ```jsx Loading...}> {/* content */} ``` ## Independent Use the `independent` prop when the navigation container should be independent of parent containers. If this is not set to true, this container cannot be nested inside another container. Setting it to true disconnects any children navigators from parent container. You probably don't want to set this to true in a typical React Native app. This is only useful if you have navigation trees that work like their own mini-apps and don't need to navigate to the screens outside of them. ```jsx render ``` --- id: needhelp category: CTA title: NeedHelp description: Provides in-app help content, always at the bottom of the page design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=36393-5275&mode=design&t=kg0BV3Q6dFmAIOBa-0 --- ```jsx import { NeedHelp } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'NeedHelp', inputs: [ { prop: 'title', type: 'string', }, { prop: 'description', type: 'string' }, ] } Go To Results ``` ## Title and Description The `title` and `description` props are used to set the title and description of NeedHelp. Both props are required. The `description` can also take a node. ```jsx live } > Go To Results ``` ## Children Add Children to the NeedHelp component by simply placing elements between the NeedHelp tags. Children should be used for adding either a link or button. Links and buttons can be added with the `NeedHelp.Link` and `NeedHelp.Button` components, respectively. ```jsx live } > Go To Results Claims can be found from a shortcut on the homescreen. Tap the button below to go to your claims. } > My Claims ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: nib-group category: CTA title: NibGroup description: Nibs are pressable components used within a group for displaying pre-engineered copy or user-generated information. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=28575%3A52270 --- ```jsx import { NibGroup } from '@uhg-abyss/mobile'; ``` ## Icon Use the `icon` prop to insert an icon before the text element. The Brand icon should have `variant={'twotone'}` and be paired with a pre-engineered copy, while the Material icon should have the props `color={'$gray3'} variant={'outlined'}` and be paired with user generated information. See Figma for more details. ```jsx live () => { const NibList = [ } > Primary care providers , } > Search History , ]; return ( {NibList} ); }; ``` ## onPress Use the `onPress` function to handle the action when a Nib is pressed. ```jsx live () => { const handlePress = () => { console.log('Pressed'); }; return ( } > Primary care providers in Minneapolis, MN ); }; ``` ## NibGroup Nibs must be wrapped in a group using `NibGroup`. ### Type Use the `type` prop to determine how the Nib Group will be displayed. By default the type is set to `list`. ### List Nibs placed in a list will be displayed in rows of two. When the font scale doubles on a mobile device, the nibs will grow to the full width of the container. ```jsx live () => { const NibList = [ } > Primary care providers , } > Primary care providers , } > Primary care providers , ]; return ( {NibList} ); }; ``` ### Carousel Nibs placed in a carousel will be organized on a slide. ```jsx live () => { const NibList = [ } > Primary care providers in the Essentia Health system , } > Primary care providers in the Essentia Health system in Minneapolis, Minnesota , } > Primary care providers in the Essentia Health system in Eden Prairie, Minnesota , } > Primary care providers in the Essentia Health system in Duluth, Minnesota , ]; return ( { console.log('cell pressed'); }} > {NibList} ); }; ``` ### Sizes Use the `size` prop to determine the size of the Nibs. By default, `size` is set to `small`. Following design guidelines, Nibs using Material Icons and pre-engineered copy should only use the `small` variant. Nibs displaying user-generated information can use `medium` or `large` depending on the anticipated content size. ```jsx live () => { const NibList = [ } > Primary care providers in the Essentia Health system , } > Primary care providers in the Essentia Health system in Minneapolis, Minnesota , } > Primary care providers in the Essentia Health system in Eden Prairie, Minnesota , } > Primary care providers in the Essentia Health system in Duluth, Minnesota , ]; return ( Small } > Primary care providers } > Primary care providers Medium {NibList} Large {NibList} ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: notification category: Feedback title: Notification description: A container used to display information to the user. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=42686-245323&mode=design&t=i3zoWjm6y2FTIEfZ-0 --- ```jsx import { Notification } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Notification', inputs: [ { prop: 'children', type: 'string', }, { prop: 'seen', type: 'boolean', }, ] } Notification Sandbox ``` ## Basic usage The content of the `Notification` component is made up `children` and `date`. The `date` prop is required and takes a Javascript Date object. ```jsx live () => { const [value, setValue] = useState(false); const [value2, setValue2] = useState(false); const date1 = new Date(); const date2 = new Date(2022, 3, 11); return ( Add your dependents to your account to view their claims and coverage. Your claim has been processed. View your Explanation of Benefits. ); }; ``` ## Seen The `seen` prop handles the state of the Notification. When `true` the text of the component is bold, with a round indicator. `onPress` will be called once the Notification is pressed. ```jsx live () => { const [value, setValue] = useState(false); const [value2, setValue2] = useState(false); const date1 = new Date(); const date2 = new Date(2021, 7, 21); return ( setValue(true)} date={date1}> New! Now you can get care cost estimates then compare, save and share them. setValue2(true)} date={date2}> Go paperless and save time! Sign up for electronic delivery of your Explanation of Benefits. ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: number-stepper category: Controls title: NumberStepper description: A two-segment UI control used to incrementally increase or decrease a numeric value. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=52553-4919&mode=design&t=jeurw9GyqLDTHSBG-0 --- ```jsx import { NumberStepper } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'NumberStepper', inputs: [ { prop: 'maximumValue', type: 'number', default: 99, }, { prop: 'minimumValue', type: 'number', default: 0, }, { prop: 'title', type: 'string', }, { prop: 'description', type: 'string' }, { prop: 'isDisabled', type: 'boolean' }, ] } () => { const [value, setValue] = useState(0) return( ); }; ``` ## useState The `useState` hook gets the value from the component state. It is required to pass in the starting number to the `value` prop. ```jsx live () => { const [value, setValue] = useState(1); return ( ); }; ``` ## Title Use the `title` prop to set the title for the input. It is required to pass in a string to the `title` prop. ```jsx live () => { const [value, setValue] = useState(1); return ( ); }; ``` ## Description Use the `description` prop to set the description for the input. ```jsx live () => { const [value, setValue] = useState(1); return ( ); }; ``` ## Min and Max Values Use the `minimumValue` and `maximumValue` to constrain the stepper to a specific range. By default this range is 0-99. ```jsx live () => { const [value, setValue] = useState(1); return ( ); }; ``` ## Disabled Use the `isDisabled` prop to disable the stepper. By default, the add or remove buttons disable when the min or max is reached. ```jsx live () => { const [value, setValue] = useState(1); return ( ); }; ``` ## Error Message Use the `errorMessage` prop to display a message below the description. ```jsx live () => { const [value, setValue] = useState(1); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type AX5 reorders items to a vertical stack. ```jsx render ``` --- id: overlay category: Layout title: Overlay description: Semi-transparent overlay with a cutout, for use with the camera to scan barcodes. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/branch/fg0s3SgvOxjhYn4q6HuCS0/Abyss-Mobile?node-id=19209-94534&t=zrt5RmcAOzZcdk3r-0 --- ```jsx import { Overlay } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Overlay', inputs: [ { prop: 'color', type: 'string', }, { prop: 'frameHeight', type: 'number', }, { prop: 'frameRadius', type: 'number', }, { prop: 'sideWidth', type: 'number', }, { prop: 'topHeight', type: 'number', }, { prop: 'opacity', type: 'slider', minValue: 0, maxValue: 1, step: 0.1, }, ] } () => { return ( ); } ``` ## Header Set the `header` prop to a React component to have that component shown above the overlay. ```jsx live const Container = styled('View', { minHeight: 300, }); const TextContainer = styled('View', { flex: 1, flexDirection: 'column', justifyContent: 'space-around', alignItems: 'center', }); const Header = styled('View', { backgroundColor: '$black', padding: 16, alignItems: 'center', justifyContent: 'center', }); const HeaderText = styled('Text', { color: '$white', }); render(() => { return ( This is the header } > Testing Testing Testing ); }); ``` ## Footer Set the `footer` prop to a React component to have that component shown below the overlay. ```jsx live const Container = styled('View', { minHeight: 300, }); const TextContainer = styled('View', { flex: 1, flexDirection: 'column', justifyContent: 'space-around', alignItems: 'center', }); const FooterContainer = styled('View', { backgroundColor: '$black', padding: 16, alignItems: 'center', justifyContent: 'center', }); const FooterText = styled('Text', { color: '$white', }); render(() => { return ( This is the footer } > Testing Testing Testing ); }); ``` ```jsx render ``` ```jsx render ``` --- id: popover-v2 category: Layout title: V2Popover description: A popover displays content on top of the page in a separate container and requires user action. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=31310-52671&mode=design&t=Ra4lP896wr2B96P7-0 subDirectory: Popover/v2 sourceIsTS: true --- ```jsx import { V2Popover } from '@uhg-abyss/mobile'; ``` ## useState Pass the value from the `useState` hook to the `isVisible` prop to set the open state of the popover. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); return ( setIsVisible(false)} media={Media Content} content={Content Section} footer={} > ); }; ``` ## Heading Use the `heading` prop to set the heading of the popover. For accessibility purposes, a heading is required. Please provide a heading that accurately describes the content of the popover so a screen reader can provide the description to the user. Use the `headingSize` prop to set the size of the heading. The `headingSize` prop accepts `small` and `large` as values. The default size is `large`. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); const [isVisible2, setIsVisible2] = useState(false); return ( setIsVisible(false)} footer={} > ); }; ``` ## Paragraph Use the `paragraph` prop to add a description below the heading. The paragraph should be concise and clear and must not expand on more than 4 lines. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); return ( setIsVisible(false)} footer={} > ); }; ``` ## Content Use the `content` prop to add custom content below the paragraph. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); return ( setIsVisible(false)} content={ The content section is where you can add custom content. } footer={} > ); }; ``` ## Media Use the `media` prop to add custom content above the heading. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); return ( setIsVisible(false)} media={ } footer={ } > ); }; ``` ## Footer Use the `footer` prop to place content at the bottom of the popover. The popover must always have at least one button, so users can close it. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); return ( setIsVisible(false)} footer={ } > ); }; ``` ## onClose Use the `onClose` function to handle the action when close button is triggered or when the background is pressed. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); return ( { setIsVisible(false); console.log('Popover closed by background tap'); }} footer={ } > ); }; ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type To accommodate for larger text sizes, the popover will scroll when it has grown to have a 16px margin on the top and bottom. Depending on the screen size, the height of the popover when scrollable will vary. The footer does not scroll with the rest of the content. Please follow design guidelines when deciding the amount of content to place within popover. ## Focus Guidance Abyss does not control the focus of components on the screen when the Popover is toggled off. To meet accessibility guidelines, the focus must be set to the previous node when closed. The [useSetFocus](/mobile/hooks/use-set-focus) hook can be used for this. For example, if a button is pressed to open a popover, focus must return to that button once the popover is closed, so that a screen reader or keyboard user may continue using the app where they left off. ```jsx render ``` ```jsx render ``` --- id: popover category: Layout title: Popover description: A popover displays content on top of the page in a separate container and requires user action. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=31310-52671&mode=design&t=Ra4lP896wr2B96P7-0 subDirectory: Popover/v1 --- ```jsx import { Popover } from '@uhg-abyss/mobile'; ``` ## useState Pass the value from the `useState` hook to the `isVisible` prop to set the open state of the popover. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); return ( setIsVisible(false)} header={ header content } footer={} > Paragraph copy goes below the title. Copy can be a 100 characters maximum and expands on no more than four lines ); }; ``` ## Title Use the `title` prop to set the title of the popover. For accessibility purposes, a title is required. Please provide a title that accurately describes the content of the popover so a screen reader can provide the description to the user. A node can be passed in instead of a string but note that accessibility will not be passed in automatically. Passing in the prop `variant='error'` will change the title to a level 5 heading. The default is a level 3 heading. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); const [isVisible2, setIsVisible2] = useState(false); return ( setIsVisible(false)} footer={} > The title should be concise and clear and must not expand on more than 2 lines ); }; ``` ## Header Use the `header` prop to add custom content above the title. Use prop `variant='illustration'` to take up all available space in the header. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); return ( setIsVisible(false)} header={ } footer={ } > The header content goes above the title. Here you can add custom content or replace with an existing Abyss component ); }; ``` ## Children Use the `children` prop to add content below the title. This can include paragraph copy and custom elements. Copy should be no more than 100 characters or four lines of text. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); return ( setIsVisible(false)} header={ header content } footer={ } > Paragraph copy goes below the title. Copy can be a 100 characters maximum and expands on no more than four lines } > List item one } > List item two is a bit longer } > List item three could even go on two lines ); }; ``` ## Footer Use the `footer` prop to place content at the bottom of the popover. The popover must always have at least one button, so users can close it. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); return ( setIsVisible(false)} footer={ } > Footer buttons stack vertically or horizontally and contain up to three OS specific buttons in the order that they are placed in the component. The Popover must always have at least one button, so users can close it ); }; ``` ## onClose Use the `onClose` function to handle the action when close button is triggered or when the background is pressed. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); const handleClose = () => { setIsVisible(false); console.log('Popover closed'); }; return ( { setIsVisible(false); console.log('Popover closed by background tap'); }} footer={ } > Paragraph copy goes below the title. Copy can be a 100 characters maximum and expands on no more than four lines ); }; ``` ### Variants Available variants: default, error, brand and illustration. The default variant lacks the below modifications, and is automatically applied if no other variant is passed. - Error: has a serif title, meant for use with the error icon. - Brand: meant for use with large brand icons, has smaller top header margin. - Illustration: heading area has no margins to allow for full width images. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); const [isVisible2, setIsVisible2] = useState(false); const [isVisible3, setIsVisible3] = useState(false); return ( setIsVisible(false)} header={ background of popover } footer={ } > In this example the header has no padding or margin on the top and sides, and reduced margin on the bottom. This allows for full fill images. setIsVisible2(false)} header={} footer={} > Title is set to header 5, and there is increased space between icon and top in comparison to brand. setIsVisible3(false)} header={ } footer={ } > The brand variant is suited to large brand icons. ); }; ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type To accommodate for larger text sizes, the popover will scroll when it has grown to have a 16px margin on the top and bottom. Depending on the screen size, the height of the popover when scrollable will vary. The footer does not scroll with the rest of the content. Please follow design guidelines when deciding the amount of content to place within popover. ## Focus Guidance Abyss does not control the focus of components on the screen when the Popover is toggled off. To meet accessibility guidelines, the focus must be set to the previous node when closed. The [useSetFocus](/mobile/hooks/use-set-focus) hook can be used for this. For example, if a button is pressed to open a popover, focus must return to that button once the popover is closed, so that a screen reader or keyboard user may continue using the app where they left off. --- id: progress-bar category: Data Viz title: ProgressBar description: Used to show users the status of loading an app, ongoing processes, saving changes/updates, and more. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=12952%3A84441&t=OIAOmowjKZ4THvqh-0 --- ```jsx import { ProgressBar } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'ProgressBar', inputs: [ { prop: 'steps', type: 'number', }, { prop: 'currentStep', type: 'number', }, ] } ``` ## useState In this example, we use the `steps` prop to define the number of steps available. React's `useState` hook is used to update the `currentStep` prop. ```jsx live () => { const [value, setValue] = useState(0); const changeValue = (operator) => { setValue((v) => Math.min(Math.max(v + operator, 0), 10)); }; return ( Current step: {value}/10 ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: radio-group category: Forms title: RadioGroup description: A radio input allows people to select only one option from a number of choices. Radio is generally displayed in a radio group. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=9596%3A34313&t=FBnhV14SktRX1IuI-0 --- ```jsx import { RadioGroup } from '@uhg-abyss/mobile'; ``` ### useForm (recommended) Using the `useForm` hook for handling RadioGroup lets the DOM handle form data. ```jsx live () => { const form = useForm(); const handleSubmit = (data) => { console.log('Form submitted with data:', data); }; return ( ); }; ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [radioValue, setRadioValue] = useState('one'); return ( ); }; ``` ## Label Use the `label` prop to pass a string as the text next to the radio button. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Children Content passed to the radio as children will be displayed in place of the label prop. If a value is passed to the label prop in addition to children, the label prop will take priority. ```jsx live () => { const form = useForm(); return ( Abyss 1 Abyss 2 ); }; ``` ## Side Use the `side` prop to place the label text on the left or right side of the radio buttons. By default, the prop is set to `left`, which is the upper example. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## isDisabled `isDisabled` will disable the radio button. It can be added to either the RadioGroup or the individual Radio components. ```jsx live () => { const form = useForm(); return ( Individual radio buttons Disabled All buttons in component disabled ); }; ``` ## hideLabel The `hideLabel` prop hides the label by each radio button, and is set to `false` by default. ```jsx live () => { const [radioValue, setRadioValue] = useState('four'); const [radioValue2, setRadioValue2] = useState('one'); return ( <> Value = {radioValue} Value = {radioValue2} ); }; ``` ## shrink The `shrink` property brings labels and radio buttons closer together than the default. By default, `shrink` prop is set to `false`. ```jsx live () => { const form = useForm(); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type RadioButton scales up to 3XL. ```jsx render ``` --- id: rating-v2 category: Data title: V2Rating description: Graphical representation of the degree of rating scale. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=10068-35503&mode=design&t=XOrzmaAmfVkUUtDy-0 subDirectory: Rating/v2 sourceIsTS: true --- ```jsx import { V2Rating } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'V2Rating', inputs: [ { prop: 'variant', type: 'select', options: [ { label: 'Large', value: 'large' }, { label: 'Small', value: 'small' }, { label: 'Single', value: 'single' }, ] }, { prop: 'rating', type: 'string', }, { prop: 'reviews', type: 'number', }, ] } ``` ## Variants Variant `small` contains the rating to the left side of the populated stars and the number of reviews to the right. Variant `single` consists of a single star with the rating and reviews in parentheses on the right side. Variant `large` has a heading style rating value followed by the reviews and stars underneath. If a variant is not provided it defaults to `small`. ```jsx live Small {Array.from({ length: 6 }).map((x, index) => ( ))} Single {Array.from({ length: 6 }).map((x, index) => ( ))} Large {Array.from({ length: 6 }).map((x, index) => ( ))} ``` ## Advanced Layout and Manipulation `rating` is required and is used to determine the number of active stars as well as the display number. The inputted rating will be formatted to the first decimal point. The `onPress` prop is optional and will transform the review text into a pressable link. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); const [activeReviewNum, setActiveReviewNum] = useState('store1'); const reviewNum = [ { reviews: 5, rating: 5 }, { reviews: 4, rating: 4 }, { reviews: 3, rating: 3 }, { reviews: 2, rating: 2 }, { reviews: 1, rating: 1 }, ]; const handleModal = (reviewNum) => { setIsVisible(true); setActiveReviewNum(reviewNum); return; }; const ReviewList = (store) => { return Array.from({ length: activeReviewNum }).map((x, index) => ( } > )); }; return ( <> Small {Array.from({ length: 5 }).map((x, index) => ( handleModal(reviewNum[index].reviews)} /> ))} Single {Array.from({ length: 5 }).map((x, index) => ( handleModal(reviewNum[index].reviews)} /> ))} Large {Array.from({ length: 5 }).map((x, index) => ( handleModal(reviewNum[index].reviews)} /> ))} } > ); }; ``` ## Hide Reviews and Rating `hideRating` can be used with variant `small` to hide the rating number on the left side. `hideReviews` can be used with variants `small` and `single` to hide the reviews text. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); const [activeReviewNum, setActiveReviewNum] = useState('store1'); const reviewNum = [ { reviews: 5, rating: 5 }, { reviews: 4, rating: 4 }, { reviews: 3, rating: 3 }, { reviews: 2, rating: 2 }, { reviews: 1, rating: 1 }, ]; const handleModal = (reviewNum) => { setIsVisible(true); setActiveReviewNum(reviewNum); return; }; const ReviewList = (store) => { return Array.from({ length: activeReviewNum }).map((x, index) => ( } > )); }; return ( <> Hide Rating {Array.from({ length: 5 }).map((x, index) => ( handleModal(reviewNum[index].reviews)} /> ))} Hide Reviews {Array.from({ length: 5 }).map((x, index) => ( handleModal(reviewNum[index].reviews)} /> ))} Hide Reviews {Array.from({ length: 5 }).map((x, index) => ( handleModal(reviewNum[index].reviews)} /> ))} } > ); }; ``` ## With RatingAccumulator Use with [RatingAccumulator](/mobile/ui/rating-accumulator) for a detailed view of rating data. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: rating category: Data title: Rating description: Graphical representation of the degree of rating scale. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=10068-35503&mode=design&t=XOrzmaAmfVkUUtDy-0 --- ```jsx import { Rating } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Rating', inputs: [ { prop: 'variant', type: 'select', options: [ { label: 'Large', value: 'large' }, { label: 'Small', value: 'small' }, { label: 'Single', value: 'single' }, ] }, { prop: 'rating', type: 'string', }, { prop: 'reviews', type: 'number', }, ] } ``` ## Variants Variant `small` contains the rating to the left side of the populated stars and the number of reviews to the right. Variant `single` consists of a single star with the rating value to the right and review count in parentheses. Variant `large` has only a visual rating of 0-5 populated stars. ```jsx live Small {Array.from({ length: 6 }).map((x, index) => ( ))} Single {Array.from({ length: 6 }).map((x, index) => ( ))} Large {Array.from({ length: 6 }).map((x, index) => ( ))} ``` ## Advanced Layout and Manipulation `rating` is required and is used to determine the number of active stars as well as the display number. The inputted rating will be formatted to the first decimal point. `hideRating` can be used with variant `small` to hide the rating number on the left side. The `onPress` prop is optional and will transform the review text for variant `small` and `single` into a pressable link. ```jsx live () => { const [isVisible, setIsVisible] = useState(false); const [activeReviewNum, setActiveReviewNum] = useState('store1'); const reviewNum = [ { reviews: 5, rating: 5 }, { reviews: 4, rating: 4 }, { reviews: 3, rating: 3 }, { reviews: 2, rating: 2 }, { reviews: 1, rating: 1 }, ]; const handleModal = (reviewNum) => { setIsVisible(true); setActiveReviewNum(reviewNum); return; }; const ReviewList = (store) => { return Array.from({ length: activeReviewNum }).map((x, index) => ( } > )); }; return ( <> Small {Array.from({ length: 5 }).map((x, index) => ( handleModal(reviewNum[index].reviews)} /> ))} Single {Array.from({ length: 5 }).map((x, index) => ( handleModal(reviewNum[index].reviews)} /> ))} Hide Rating {Array.from({ length: 5 }).map((x, index) => ( handleModal(reviewNum[index].reviews)} /> ))} } > ); }; ``` ```jsx render ``` ```jsx render ``` --- id: rating-accumulator category: Data Viz title: RatingAccumulator description: A visual representation of rating distribution using horizontal bars. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=10068-35503&mode=design&t=XOrzmaAmfVkUUtDy-0 sourceIsTS: true --- ```jsx import { RatingAccumulator } from '@uhg-abyss/mobile'; ``` ## Data The `data` prop is an array of numbers representing percentages for each star rating level, from 1 stars (index 0) to 5 stars (index 4). ```jsx live ``` ## Alternate Theme The `alt` prop changes the color theme to an alternate style, useful for displaying against darker backgrounds. ```jsx live ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` Due to React Native limitations, this component enables keyboard access despite not having an interactive element. This component requires an accessibility label for use with a screen reader, which enables keyboard focus. ```jsx render ``` --- id: scroll-wheel category: Controls title: ScrollWheel description: A scrollable list of distinct values that allow selection between several options. --- ```jsx sandbox { component: 'ScrollWheel', } () => { const [index, setIndex] = useState(0); const options = [1, 2, 3, 4, 5, 6]; return ( ) } ``` ## Usage Using the `useState` hook gets values from the component state. ```jsx live () => { const [index, setIndex] = useState(1); const options = [1, 2, 3, 4, 5, 6]; return ( You have Selected: {options[index]} ); }; ``` ## Options Use the `options` prop to set the options to be displayed on the scroll wheel. The `options` prop accepts an array of strings and is a required prop. ```jsx live () => { const [index, setIndex] = useState(1); const options = ['Kendall', 'Reagan', 'Nathan', 'Morgan', 'Billy']; return ( You have selected: {options[index]} ); }; ``` ## Colors Use the `activeColor` prop to set the color of the selected option's text. In addition, use the `indicatorColor` prop to set the background color of the selected indicator. ```jsx live () => { const [index, setIndex] = useState(1); const options = [1, 2, 3, 4, 5, 6]; return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: search-bar category: Forms title: SearchBar description: Provides an input field for searching content within an app to find specific items. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=35761-15587&mode=design&t=5prUNtGYxFI7SAco-0 --- ```jsx import { SearchBar } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'SearchBar', } () => { const form = useForm(); return ( ); }; ``` ## useForm (recommended) Using the `useForm` hook with `FormProvider` sets state for the component. ```jsx live () => { const form = useForm(); const handleSubmit = (data) => { console.log('Submitted:', data); }; return ( ); }; ``` ## useState Using the `useState` hook sets state for the component. ```jsx live () => { const [text, setText] = useState(''); const handleSubmit = () => { console.log('Submitted:', text); }; return ( ); }; ``` ## Placeholder The `placeholder` prop gives users a short description in the search bar before they enter a value. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Submit The `onSubmit` prop fires a callback when the submit action is triggered. The value will be passed in as a parameter. ```jsx live () => { const [text, setText] = useState(''); const handleSubmit = (val) => { console.log(val); }; return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type Similar to all other components used within [AppBar](/mobile/ui/app-bar), SearchBar will scale up to 3XL. --- id: search-input category: Forms title: SearchInput description: A type of input field that allows searching through content. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=41039-16146&mode=design&t=bfPqTvn7mEdSo5pn-0 --- ```jsx import { SearchInput } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'SearchInput', inputs: [ { prop: 'label', type: 'string', }, { prop: 'hintText', type: 'string', }, { prop: 'helpContent', type: 'string', }, ], } () => { const data = [ { value: 'react', label: 'React' }, { value: 'ng', label: 'Angular' }, { value: 'svelte', label: 'Svelte' }, { value: 'vue', label: 'Vue' }, { value: 'alpine', label: 'Alpine' }, { value: 'ember', label: 'Ember' }, { value: 'stimulus', label: 'Stimulus' }, { value: 'preact', label: 'Preact' }, ]; const [value, setValue] = React.useState(''); const [results, setResults] = useState([]); const handleSubmit = ()=>{ console.log(results) } return ( ); }; ``` ## useState Using the `useState` hook sets state for the component. ```jsx live () => { const [text, setText] = useState(''); const handleSubmit = () => { console.log('Submitted:', text); }; return ( ); }; ``` ## onChange The `onChange` prop handles the action for the search results. ```jsx live () => { const data = [ { value: 'react', label: 'React' }, { value: 'ng', label: 'Angular' }, { value: 'svelte', label: 'Svelte' }, { value: 'vue', label: 'Vue' }, { value: 'alpine', label: 'Alpine' }, { value: 'ember', label: 'Ember' }, { value: 'stimulus', label: 'Stimulus' }, { value: 'preact', label: 'Preact' }, ]; const [value, setValue] = React.useState(''); const [results, setResults] = useState([]); return ( ); }; ``` ## Fuse.js Search Input filtering uses the Fuse.js library to fuzzy filter results. What is fuzzy searching? Generally speaking, fuzzy searching (more formally known as approximate string matching) is the technique of finding strings that are approximately equal to a given pattern (rather than exactly). ### Fuse Configurations To adjust the fuse configurations, pass the desired options into the `fuseConfigs` prop. Note that `keys` is required when using fuse. By default the fuse search configurations are: ``` { includeMatches: true, findAllMatches: true, threshold: 0, ignoreLocation: true, minMatchCharLength: searchText.length, keys, } ``` The default configurations return the search results object below: ``` { item: [] } ``` ### Fuse Data The `options` prop is the information that fuse will filter on and display in the search dropdown. You can search on any value(s) in the object, see Fuse Keys below. Required when using fuse. ```jsx live () => { const data = [ { value: 'react', label: 'React' }, { value: 'ng', label: 'Angular' }, { value: 'svelte', label: 'Svelte' }, { value: 'vue', label: 'Vue' }, { value: 'alpine', label: 'Alpine' }, { value: 'ember', label: 'Ember' }, { value: 'stimulus', label: 'Stimulus' }, { value: 'preact', label: 'Preact' }, ]; const [value, setValue] = React.useState(''); const [results, setResults] = useState([]); const ResultsList = styled('ScrollView', { height: 200, }); return ( Results: {(value.length > 0 ? results : data).map((item, i) => { return ; })} ); }; ``` ### Fuse Keys List of keys that will be searched. This supports nested paths, weighted search, searching in arrays of objects. Required when using fuse. Below the `keys` set are "label" and "value". ```jsx live () => { const data = [ { value: 'react', label: 'React' }, { value: 'ng', label: 'Angular' }, { value: 'svelte', label: 'Svelte' }, { value: 'vue', label: 'Vue' }, { value: 'alpine', label: 'Alpine' }, { value: 'ember', label: 'Ember' }, { value: 'stimulus', label: 'Stimulus' }, { value: 'preact', label: 'Preact' }, ]; const [text, setText] = useState(''); const [results, setResults] = useState([]); const ResultsList = styled('ScrollView', { height: 200, }); return ( Results: {(text.length > 0 ? results : data).map((item, i) => { return ; })} ); }; ``` ## Custom Filtering Use the `customFilter` prop to override the fuse.js filtering. In the example function below, the filter is checking the first letter typed in the search bar against the first letter of each item in the list. If the letter is a match the item is included in the filtered list. ```jsx live () => { const data = [ { value: 'react', label: 'React' }, { value: 'ng', label: 'Angular' }, { value: 'svelte', label: 'Svelte' }, { value: 'vue', label: 'Vue' }, { value: 'alpine', label: 'Alpine' }, { value: 'ember', label: 'Ember' }, { value: 'stimulus', label: 'Stimulus' }, { value: 'preact', label: 'Preact' }, ]; const [results, setResults] = useState([]); const [text, setText] = useState(''); const ResultsList = styled('ScrollView', { height: 200, }); const filterFunction = (searchText, list) => { const newList = []; if (searchText.length > 0) { list.forEach((item) => { const title = item.label.toUpperCase(); if (title[0] === searchText[0].toUpperCase()) { newList.push(item); } }); } return newList; }; return ( Results: {(text.length > 0 ? results : data).map((item, i) => { return ; })} ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: search-input-button category: Navigation title: SearchInputButton description: Acts as a placeholder for search bar. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=9600-35750&mode=design&t=9miNk11OVJWfR3kX-0 --- ```jsx import { SearchInputButton } from '@uhg-abyss/mobile'; ``` ## Placeholder The `placeholder` prop gives users a short description in the search bar. ```jsx live () => { return ; }; ``` ## onPress Use the `onPress` prop to determine the action when the search button is pressed. ```jsx live () => { const handlePress = () => { console.log('pressed'); }; return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: segmented-controls category: Forms title: SegmentedControls description: A segmented control is a linear set of two or more segments, each of which functions as a button. There cannot be more than one segment selected, so under the hood this behaves like a radio group. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=9553%3A32917&t=lY6iM2O8OTPyLv8A-0 --- ```jsx import { SegmentedControls } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'SegmentedControls', inputs: [ { prop: 'shrink', type: 'boolean', }, ] } () => { const form = useForm({ defaultValues: { 'segmented-controls-sandbox': 'one', }, }); return ( ); }; ``` ## useForm (recommended) Using the `useForm` hook with `FormProvider` sets state for the component. ```jsx live () => { const form = useForm({ defaultValues: { 'segmented-controls-form': 'one', }, }); const handleSubmit = (data) => { console.log('Submitted:', data); }; return ( ); }; ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [activeValue, setActiveValue] = useState('one'); const handleSubmit = () => { console.log('Submitted:', activeValue); }; return ( ); }; ``` ## Label The `label` prop is used to define the display in each Tab. The `label` can be an Icon or text. ```jsx live () => { const form = useForm({ defaultValues: { 'segmented-controls-label': 'one', 'segmented-controls-label-icons': 'two', }, }); return ( } value="one" /> } value="two" /> } value="three" /> ); }; ``` ## Structure Segmented controls consist of a parent element and several children. The parent element `` handles the active Tab state. While the children define the segments of the component. Each child is a Tab: `` A new child is required for every segment. ```jsx live () => { const form = useForm({ defaultValues: { 'segmented-controls-structure': 'snow', }, }); return ( ); }; ``` ## Shrink The `shrink` prop toggles whether the component will take up the full width of the parent container. When true, they will automatically size based on the number of tabs and their content. Defaults to `false`. ```jsx live () => { const form = useForm({ defaultValues: { 'segmented-controls-one': 'snow', 'segmented-controls-two': 'clouds', 'segmented-controls-three': 'sun', }, }); return ( ); }; ``` ## Example ```jsx live () => { const [activeTab, setActiveTab] = useState('one'); return ( Title {activeTab} ); }; ``` ## Dynamic Type SegmentedControls scale to 3XL. Any icons passed to the label prop will need to set `maxFontSizeMultiplier={1.3}`. ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: select-input-v2 category: Forms title: V2SelectInput description: Allows users to select one value from a provided list of options. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=18828-105743&mode=design&t=Aa48cFkeiLakVzb1-0 --- ```jsx import { V2SelectInput } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'V2SelectInput', inputs: [ { prop: 'label', type: 'string', }, { prop: 'hintText', type: 'string', }, { prop: 'isDisabled', type: 'boolean', }, { prop: 'isRequired', type: 'boolean', }, { prop: 'helpContent', type: 'string', }, ], } () => { const form = useForm(); return ( ); }; ``` ## Usage Use the `options` prop to supply the options that can be selected. `options` is an array of objects, where each object should have two properties: - `label`, a string which is how the option will be displayed in the list - `value`, a string which is the unique identifier for the option Sections can also be specified, in which case the section object should have `title` and `items` properties instead of `label` and `value` (See [Titles](#titles) for more details). ``` options = { [ { label: 'Item 1', value: 'item1' }, { label: 'Item 2', value: 'item2', isDisabled: true }, { label: 'Item 3', value: 'item3' }, { label: 'Item 4', value: 'item4' }, { label: 'Item 5', value: 'item5' }, ] } ``` ## useForm (recommended) Use the `useForm` hook to manage the state of the select input. The `model` prop should be set to a unique string that identifies the form field. ```jsx live () => { const form = useForm(); const handleSubmit = (data) => { console.log('Submitted:', data); }; return ( ); }; ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [selection, setSelection] = useState(); return ( ); }; ``` ## Titles To create sections in the list, pass objects into the `options` array that have the `title` and `items` properties. `title` specifies the name of the title, which will be bolded and not selectable, `items` should contain the options within that section (with the same `label`/`value` format as normal). ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Label Use the `label` prop to display a label above the input menu. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Required Use the `isRequired` prop to display an asterisk next to the label. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Hint Text Use the `hintText` prop to display text below the label. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Help Content Use the `helpContent` prop to display a help icon in the top right of the container, which will display the provided content in a modal screen when pressed. ```jsx live () => { const form = useForm(); return ( This is some text used to explain something to the user, but is too long to be hint text. } options={[ { label: 'item 1', value: 'item11' }, { label: 'item 2', value: 'item12' }, { label: 'item 3', value: 'item13' }, ]} /> ); }; ``` ## Messages Use the `errorMessage` and `successMessage` props to display a custom error or success message below the menu. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Disabled Set the `isDisabled` prop to `true` to disable the select list input field so users cannot select a value. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Disable Option Items Disable an individual option item by setting the `isDisabled` key to `true` within the object. ```jsx live () => { const form = useForm(); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: select-input category: Forms title: SelectInput description: Allows users to select one value from a provided list of options. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=18828-105743&mode=design&t=Aa48cFkeiLakVzb1-0 --- ```jsx import { SelectInput } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'SelectInput', inputs: [ { prop: 'label', type: 'string', defaultValue: 'Sandbox', }, { prop: 'hintText', type: 'string', }, { prop: 'isDisabled', type: 'boolean', }, { prop: 'isRequired', type: 'boolean', }, { prop: 'helpContent', type: 'string', }, ], } () => { const form = useForm(); return ( ); }; ``` ## Usage Use the `options` prop to supply the options that can be selected. `options` is an array of objects, where each object should have two properties: - `label`, a string which is how the option will be displayed in the list - `value`, a string which is the unique identifier for the option Sections can also be specified, in which case the section object should have `title` and `items` properties instead of `label` and `value` (See [Titles](#titles) for more details). ``` options = { [ { label: 'Item 1', value: 'item1' }, { label: 'Item 2', value: 'item2', isDisabled: true }, { label: 'Item 3', value: 'item3' }, { label: 'Item 4', value: 'item4' }, { label: 'Item 5', value: 'item5' }, ] } ``` ## useForm (recommended) Use the `useForm` hook to manage the state of the select input. The `model` prop should be set to a unique string that identifies the form field. ```jsx live () => { const form = useForm(); const handleSubmit = (data) => { console.log('Submitted:', data); }; return ( ); }; ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [selection, setSelection] = useState(); return ( ); }; ``` ## Titles To create sections in the list, pass objects into the `options` array that have the `title` and `items` properties. `title` specifies the name of the title, which will be bolded and not selectable, `items` should contain the options within that section (with the same `label`/`value` format as normal). ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Label Use the `label` prop to display a label above the input menu. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Required Use the `isRequired` prop to display an asterisk next to the label. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Hint Text Use the `hintText` prop to display text below the label. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Help Content Use the `helpContent` prop to display a help icon in the top right of the container, which will display the provided content in a modal screen when pressed. ```jsx live () => { const form = useForm(); return ( This is some text used to explain something to the user, but is too long to be hint text. } options={[ { label: 'item 1', value: 'item11' }, { label: 'item 2', value: 'item12' }, { label: 'item 3', value: 'item13' }, ]} /> ); }; ``` ## Messages Use the `errorMessage` and `successMessage` props to display a custom error or success message below the menu. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Disabled Set the `isDisabled` prop to `true` to disable the select list input field so users cannot select a value. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Disable Option Items Disable an individual option item by setting the `isDisabled` key to `true` within the object. ```jsx live () => { const form = useForm(); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: select-input-multi-v2 category: Forms title: V2SelectInputMulti description: Allows users to select multiple values from a provided list of options. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=18828-105743&mode=design&t=Aa48cFkeiLakVzb1-0 --- ```jsx import { V2SelectInputMulti } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'V2SelectInputMulti', inputs: [ { prop: 'label', type: 'string', }, { prop: 'hintText', type: 'string', }, { prop: 'isDisabled', type: 'boolean', }, { prop: 'isSearchable', type: 'boolean', }, { prop: 'selectAll', type: 'boolean', }, { prop: 'helpContent', type: 'string', }, ], } () => { const form = useForm(); return ( ); }; ``` ## Usage Use the `options` prop to supply the options that can be selected. `options` is an array of objects, where each object should have two properties: - `label`, a string which is how the option will be displayed in the list - `value`, a string which is the unique identifier for the option Sections can also be specified, in which case the section object should have `title`, `value`, and `items` properties instead of `label` and `value` (See [Titles](#titles) for more details). ``` options = { [ { label: 'Item 1', value: 'item1' }, { label: 'Item 2', value: 'item2', isDisabled: true }, { label: 'Item 3', value: 'item3' }, { label: 'Item 4', value: 'item4' }, { label: 'Item 5', value: 'item5' }, ] } ``` ## useForm (recommended) Using the `useForm` hook with `FormProvider` sets state for the component. ```jsx live () => { const form = useForm(); const options = [ { label: 'Colors', value: 'Colors' }, { label: 'Elevation', value: 'E' }, { label: 'Icon Brand', value: 'IB' }, { label: 'Typography', value: 'T' }, { title: 'CTA', value: 'CTA', items: [ { label: 'Button', value: 'button' }, { label: 'Cell Group', value: 'cellGroup' }, { label: 'Chip', value: 'chip' }, ], }, { title: 'Controls', value: 'Controls', items: [ { label: 'Checkbox', value: 'checkbox' }, { label: 'Checkbox Group', value: 'CG' }, { label: 'Radio Group', value: 'RG' }, { label: 'Segmented Controls', value: 'SC' }, { label: 'Toggle Switch', value: 'TS' }, ], }, { title: 'Data', value: 'Title1', items: [ { label: 'Avatar', value: 'avatar' }, { label: 'Indicator', value: 'indicator' }, { label: 'Progress Bar', value: 'PB' }, ], }, { title: 'Data Viz', value: 'DataViz', items: [ { label: 'Accordion', value: 'accordion' }, { label: 'Accumulator', value: 'accumulator' }, { label: 'Donut Chart', value: 'DC' }, ], }, { title: 'Forms', value: 'Forms', items: [ { label: 'Date Input', value: 'DI' }, { label: 'Select Input', value: 'SI' }, { label: 'Select Input Multi', value: 'SIM' }, { label: 'Text Input', value: 'TI' }, ], }, { title: 'Layout', value: 'Layout', items: [ { label: 'Bottom Sheet', value: 'BS' }, { label: 'Modal', value: 'modal' }, { label: 'Card', value: 'card' }, ], }, { title: 'Media', value: 'Media', items: [ { label: 'Icon', value: 'icon' }, { label: 'Icon Custom', value: 'IC' }, { label: 'Icon Material', value: 'IM' }, ], }, { title: 'Navigation', value: 'nav', items: [ { label: 'Link', value: 'link' }, { label: 'Tabs', value: 'tabs' }, ], }, { title: 'Notifications', value: 'Notifications', items: [ { label: 'Alert', value: 'alert' }, { label: 'Badge', value: 'badge' }, { label: 'FAB', value: 'fab' }, { label: 'Toast', value: 'toast' }, ], }, { title: 'Providers', value: 'providers', items: [{ label: 'LagoonProvider', value: 'LP' }], }, { title: 'Typography', value: 'typography', items: [ { label: 'Heading', value: 'heading' }, { label: 'Text', value: 'text' }, ], }, ]; const handleSubmit = (data) => { console.log('Submitted:', data); }; return ( ); }; ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const options = [ { label: 'Colors', value: 'Colors' }, { label: 'Elevation', value: 'E' }, { label: 'Icon Brand', value: 'IB' }, { label: 'Typography', value: 'T' }, { title: 'CTA', value: 'CTA', items: [ { label: 'Button', value: 'button' }, { label: 'Cell Group', value: 'cellGroup' }, { label: 'Chip', value: 'chip' }, ], }, { title: 'Controls', value: 'Controls', items: [ { label: 'Checkbox', value: 'checkbox' }, { label: 'Checkbox Group', value: 'CG' }, { label: 'Radio Group', value: 'RG' }, { label: 'Segmented Controls', value: 'SC' }, { label: 'Toggle Switch', value: 'TS' }, ], }, { title: 'Data', value: 'Title1', items: [ { label: 'Avatar', value: 'avatar' }, { label: 'Indicator', value: 'indicator' }, { label: 'Progress Bar', value: 'PB' }, ], }, { title: 'Data Viz', value: 'DataViz', items: [ { label: 'Accordion', value: 'accordion' }, { label: 'Accumulator', value: 'accumulator' }, { label: 'Donut Chart', value: 'DC' }, ], }, { title: 'Forms', value: 'Forms', items: [ { label: 'Date Input', value: 'DI' }, { label: 'Select Input', value: 'SI' }, { label: 'Select Input Multi', value: 'SIM' }, { label: 'Text Input', value: 'TI' }, ], }, { title: 'Layout', value: 'Layout', items: [ { label: 'Bottom Sheet', value: 'BS' }, { label: 'Modal', value: 'modal' }, { label: 'Card', value: 'card' }, ], }, { title: 'Media', value: 'Media', items: [ { label: 'Icon', value: 'icon' }, { label: 'Icon Custom', value: 'IC' }, { label: 'Icon Material', value: 'IM' }, ], }, { title: 'Navigation', value: 'nav', items: [ { label: 'Link', value: 'link' }, { label: 'Tabs', value: 'tabs' }, ], }, { title: 'Notifications', value: 'Notifications', items: [ { label: 'Alert', value: 'alert' }, { label: 'Badge', value: 'badge' }, { label: 'FAB', value: 'fab' }, { label: 'Toast', value: 'toast' }, ], }, { title: 'Providers', value: 'providers', items: [{ label: 'LagoonProvider', value: 'LP' }], }, { title: 'Typography', value: 'typography', items: [ { label: 'Heading', value: 'heading' }, { label: 'Text', value: 'text' }, ], }, ]; const [value, setValue] = useState([]); const handleSubmit = () => { console.log('Submitted:', value); }; return ( ); }; ``` ## Titles To create sections in the list, pass objects into the `options` array that have the `title`, `value`, and `items` properties. `title` specifies the name of the title, which will be bolded and not selectable, `value` is the unique identifier for the title, while `items` should contain the options within that section (with the same `label`/`value` format as normal). ``` options = { [ { title: 'Title 1', value: 'Title1', items: [ { label: 'item 1', value: 'item11' }, { label: 'item 2', value: 'item12', isDisabled: true }, { label: 'item 3', value: 'item13' }, ], }, ] } ``` ## Label Use the `label` prop to display a label above the input menu. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Required Use the `isRequired` prop to display an asterisk next to the label. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Hint Text Use the `hintText` prop to display text below the label. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Help Content Use the `helpContent` prop to display a help icon in the top right of the container, which will display the provided content in a modal screen when pressed. ```jsx live () => { const form = useForm(); return ( This is some text used to explain something to the user, but is too long to be hint text. } options={[ { value: 'react', label: 'React' }, { value: 'ng', label: 'Angular' }, { value: 'svelte', label: 'Svelte' }, { value: 'vue', label: 'Vue' }, { value: 'alpine', label: 'Alpine' }, { value: 'ember', label: 'Ember' }, { value: 'stimulus', label: 'Stimulus' }, { value: 'preact', label: 'Preact' }, ]} /> ); }; ``` ## Messages Use the `errorMessage` and `successMessage` props to display a custom error or success message below the menu. ```jsx live () => { const form = useForm(); const data = [ { value: 'react', label: 'React' }, { value: 'ng', label: 'Angular' }, { value: 'svelte', label: 'Svelte' }, { value: 'vue', label: 'Vue' }, { value: 'alpine', label: 'Alpine' }, { value: 'ember', label: 'Ember' }, { value: 'stimulus', label: 'Stimulus' }, { value: 'preact', label: 'Preact' }, ]; return ( ); }; ``` ## Disabled Set the `isDisabled` prop to `true` to disable the select list input field so users cannot select a value. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Disable Option Items Disable an individual option item by setting the `isDisabled` key to `true` within the object. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Select All By setting the `selectAll` property to `true` you can make a "Select All" option visible at the top of the dropdown. When selected, all options will be selected. When deselected, all options will be deselected. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Searchable Use the `isSearchable` prop to display an input field for the user to search/filter the list of options that is located inside the dropdown. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Fuse.js Search Bar filtering uses the Fuse.js library to fuzzy filter results. What is fuzzy searching? Generally speaking, fuzzy searching (more formally known as approximate string matching) is the technique of finding strings that are approximately equal to a given pattern (rather than exactly). ### Fuse Data The `options` prop is the information that fuse will filter on and display in the search dropdown. ### Fuse Configurations The fuse search options are set by default as follows. The keys are set to `label` and cannot be changed. ``` { keys: ['label', 'items.label'], includeMatches: true, findAllMatches: true, threshold: 0, ignoreLocation: true, minMatchCharLength: searchText.length, } ``` ### Custom Fuse Configurations You can customize the fuse filter by following the documentation on Fuse and passing your configurations into the `fuseConfigs` prop. To get the filtered list back, be sure `includeMatches` is always set to `true`. ```jsx live () => { const [value, setValue] = useState([]); const customFuseConfigs = { ignoreLocation: false, distance: 0, includeMatches: true, }; return ( ); }; ``` ## Custom Filtering Use the `customFilter` prop to override the fuse.js filtering. The results returned from your customFilter function should be passed into the options prop. In the example function below, the filter is checking the first letter typed in the search bar against the first letter of each item in the list. If the letter is a match the item is included in the filtered list. Any 'type-ahead' styles will not be applied when a custom filtering function is being used. ```jsx live () => { const [value, setValue] = useState([]); const filterFunction = (searchText, list) => { const newList = []; if (searchText.length > 0) { list.forEach((item) => { if (item.label) { if (item.label[0].toUpperCase() == searchText[0].toUpperCase()) { newList.push(item); } } if (item.items) { item.items.forEach((i) => { if (i.label[0].toUpperCase() == searchText[0].toUpperCase()) { newList.push(i); } }); } }); } return newList; }; return ( ); }; ``` ## Notes In order for a user to select an item from the menu while the softkeyboard is up, the prop `keyboardShouldPersistTaps` is set to `'always'`. If the menu is contained in a ScrollView, `keyboardShouldPersistTaps={'always'}` should be set with in that view. This allows for the keyboard to remain open and an item to be selected on initial tap. ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: select-input-multi category: Forms title: SelectInputMulti description: Allows users to select multiple values from a provided list of options. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=18828-105743&mode=design&t=Aa48cFkeiLakVzb1-0 --- ```jsx import { SelectInputMulti } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'SelectInputMulti', inputs: [ { prop: 'label', type: 'string', }, { prop: 'hintText', type: 'string', }, { prop: 'isDisabled', type: 'boolean', }, { prop: 'isSearchable', type: 'boolean', }, { prop: 'selectAll', type: 'boolean', }, { prop: 'helpContent', type: 'string', }, ], } () => { const form = useForm(); return ( ); }; ``` ## Usage Use the `options` prop to supply the options that can be selected. `options` is an array of objects, where each object should have two properties: - `label`, a string which is how the option will be displayed in the list - `value`, a string which is the unique identifier for the option Sections can also be specified, in which case the section object should have `title`, `value`, and `items` properties instead of `label` and `value` (See [Titles](#titles) for more details). ``` options = { [ { label: 'Item 1', value: 'item1' }, { label: 'Item 2', value: 'item2', isDisabled: true }, { label: 'Item 3', value: 'item3' }, { label: 'Item 4', value: 'item4' }, { label: 'Item 5', value: 'item5' }, ] } ``` ## useForm (recommended) Using the `useForm` hook with `FormProvider` sets state for the component. ```jsx live () => { const form = useForm(); const options = [ { label: 'Colors', value: 'Colors' }, { label: 'Elevation', value: 'E' }, { label: 'Icon Brand', value: 'IB' }, { label: 'Typography', value: 'T' }, { title: 'CTA', value: 'CTA', items: [ { label: 'Button', value: 'button' }, { label: 'Cell Group', value: 'cellGroup' }, { label: 'Chip', value: 'chip' }, ], }, { title: 'Controls', value: 'Controls', items: [ { label: 'Checkbox', value: 'checkbox' }, { label: 'Checkbox Group', value: 'CG' }, { label: 'Radio Group', value: 'RG' }, { label: 'Segmented Controls', value: 'SC' }, { label: 'Toggle Switch', value: 'TS' }, ], }, { title: 'Data', value: 'Title1', items: [ { label: 'Avatar', value: 'avatar' }, { label: 'Indicator', value: 'indicator' }, { label: 'Progress Bar', value: 'PB' }, ], }, { title: 'Data Viz', value: 'DataViz', items: [ { label: 'Accordion', value: 'accordion' }, { label: 'Accumulator', value: 'accumulator' }, { label: 'Donut Chart', value: 'DC' }, ], }, { title: 'Forms', value: 'Forms', items: [ { label: 'Date Input', value: 'DI' }, { label: 'Select Input', value: 'SI' }, { label: 'Select Input Multi', value: 'SIM' }, { label: 'Text Input', value: 'TI' }, ], }, { title: 'Layout', value: 'Layout', items: [ { label: 'Bottom Sheet', value: 'BS' }, { label: 'Modal', value: 'modal' }, { label: 'Card', value: 'card' }, ], }, { title: 'Media', value: 'Media', items: [ { label: 'Icon', value: 'icon' }, { label: 'Icon Custom', value: 'IC' }, { label: 'Icon Material', value: 'IM' }, ], }, { title: 'Navigation', value: 'nav', items: [ { label: 'Link', value: 'link' }, { label: 'Tabs', value: 'tabs' }, ], }, { title: 'Notifications', value: 'Notifications', items: [ { label: 'Alert', value: 'alert' }, { label: 'Badge', value: 'badge' }, { label: 'FAB', value: 'fab' }, { label: 'Toast', value: 'toast' }, ], }, { title: 'Providers', value: 'providers', items: [{ label: 'LagoonProvider', value: 'LP' }], }, { title: 'Typography', value: 'typography', items: [ { label: 'Heading', value: 'heading' }, { label: 'Text', value: 'text' }, ], }, ]; const handleSubmit = (data) => { console.log('Submitted:', data); }; return ( ); }; ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const options = [ { label: 'Colors', value: 'Colors' }, { label: 'Elevation', value: 'E' }, { label: 'Icon Brand', value: 'IB' }, { label: 'Typography', value: 'T' }, { title: 'CTA', value: 'CTA', items: [ { label: 'Button', value: 'button' }, { label: 'Cell Group', value: 'cellGroup' }, { label: 'Chip', value: 'chip' }, ], }, { title: 'Controls', value: 'Controls', items: [ { label: 'Checkbox', value: 'checkbox' }, { label: 'Checkbox Group', value: 'CG' }, { label: 'Radio Group', value: 'RG' }, { label: 'Segmented Controls', value: 'SC' }, { label: 'Toggle Switch', value: 'TS' }, ], }, { title: 'Data', value: 'Title1', items: [ { label: 'Avatar', value: 'avatar' }, { label: 'Indicator', value: 'indicator' }, { label: 'Progress Bar', value: 'PB' }, ], }, { title: 'Data Viz', value: 'DataViz', items: [ { label: 'Accordion', value: 'accordion' }, { label: 'Accumulator', value: 'accumulator' }, { label: 'Donut Chart', value: 'DC' }, ], }, { title: 'Forms', value: 'Forms', items: [ { label: 'Date Input', value: 'DI' }, { label: 'Select Input', value: 'SI' }, { label: 'Select Input Multi', value: 'SIM' }, { label: 'Text Input', value: 'TI' }, ], }, { title: 'Layout', value: 'Layout', items: [ { label: 'Bottom Sheet', value: 'BS' }, { label: 'Modal', value: 'modal' }, { label: 'Card', value: 'card' }, ], }, { title: 'Media', value: 'Media', items: [ { label: 'Icon', value: 'icon' }, { label: 'Icon Custom', value: 'IC' }, { label: 'Icon Material', value: 'IM' }, ], }, { title: 'Navigation', value: 'nav', items: [ { label: 'Link', value: 'link' }, { label: 'Tabs', value: 'tabs' }, ], }, { title: 'Notifications', value: 'Notifications', items: [ { label: 'Alert', value: 'alert' }, { label: 'Badge', value: 'badge' }, { label: 'FAB', value: 'fab' }, { label: 'Toast', value: 'toast' }, ], }, { title: 'Providers', value: 'providers', items: [{ label: 'LagoonProvider', value: 'LP' }], }, { title: 'Typography', value: 'typography', items: [ { label: 'Heading', value: 'heading' }, { label: 'Text', value: 'text' }, ], }, ]; const [value, setValue] = useState([]); const handleSubmit = () => { console.log('Submitted:', value); }; return ( ); }; ``` ## Titles To create sections in the list, pass objects into the `options` array that have the `title`, `value`, and `items` properties. `title` specifies the name of the title, which will be bolded and not selectable, `value` is the unique identifier for the title, while `items` should contain the options within that section (with the same `label`/`value` format as normal). ``` options = { [ { title: 'Title 1', value: 'Title1', items: [ { label: 'item 1', value: 'item11' }, { label: 'item 2', value: 'item12', isDisabled: true }, { label: 'item 3', value: 'item13' }, ], }, ] } ``` ## Label Use the `label` prop to display a label above the input menu. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Required Use the `isRequired` prop to display an asterisk next to the label. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Hint Text Use the `hintText` prop to display text below the label. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Help Content Use the `helpContent` prop to display a help icon in the top right of the container, which will display the provided content in a modal screen when pressed. ```jsx live () => { const form = useForm(); return ( This is some text used to explain something to the user, but is too long to be hint text. } options={[ { value: 'react', label: 'React' }, { value: 'ng', label: 'Angular' }, { value: 'svelte', label: 'Svelte' }, { value: 'vue', label: 'Vue' }, { value: 'alpine', label: 'Alpine' }, { value: 'ember', label: 'Ember' }, { value: 'stimulus', label: 'Stimulus' }, { value: 'preact', label: 'Preact' }, ]} /> ); }; ``` ## Messages Use the `errorMessage` and `successMessage` props to display a custom error or success message below the menu. ```jsx live () => { const form = useForm(); const data = [ { value: 'react', label: 'React' }, { value: 'ng', label: 'Angular' }, { value: 'svelte', label: 'Svelte' }, { value: 'vue', label: 'Vue' }, { value: 'alpine', label: 'Alpine' }, { value: 'ember', label: 'Ember' }, { value: 'stimulus', label: 'Stimulus' }, { value: 'preact', label: 'Preact' }, ]; return ( ); }; ``` ## Disabled Set the `isDisabled` prop to `true` to disable the select list input field so users cannot select a value. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Disable Option Items Disable an individual option item by setting the `isDisabled` key to `true` within the object. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Select All By setting the `selectAll` property to `true` you can make a "Select All" option visible at the top of the dropdown. When selected, all options will be selected. When deselected, all options will be deselected. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Searchable Use the `isSearchable` prop to display an input field for the user to search/filter the list of options that is located inside the dropdown. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Fuse.js Search Bar filtering uses the Fuse.js library to fuzzy filter results. What is fuzzy searching? Generally speaking, fuzzy searching (more formally known as approximate string matching) is the technique of finding strings that are approximately equal to a given pattern (rather than exactly). ### Fuse Data The `options` prop is the information that fuse will filter on and display in the search dropdown. ### Fuse Configurations The fuse search options are set by default as follows. The keys are set to `label` and cannot be changed. ``` { keys: ['label', 'items.label'], includeMatches: true, findAllMatches: true, threshold: 0, ignoreLocation: true, minMatchCharLength: searchText.length, } ``` ### Custom Fuse Configurations You can customize the fuse filter by following the documentation on Fuse and passing your configurations into the `fuseConfigs` prop. To get the filtered list back, be sure `includeMatches` is always set to `true`. ```jsx live () => { const [value, setValue] = useState([]); const customFuseConfigs = { ignoreLocation: false, distance: 0, includeMatches: true, }; return ( ); }; ``` ## Custom Filtering Use the `customFilter` prop to override the fuse.js filtering. The results returned from your customFilter function should be passed into the options prop. In the example function below, the filter is checking the first letter typed in the search bar against the first letter of each item in the list. If the letter is a match the item is included in the filtered list. Any 'type-ahead' styles will not be applied when a custom filtering function is being used. ```jsx live () => { const [value, setValue] = useState([]); const filterFunction = (searchText, list) => { const newList = []; if (searchText.length > 0) { list.forEach((item) => { if (item.label) { if (item.label[0].toUpperCase() == searchText[0].toUpperCase()) { newList.push(item); } } if (item.items) { item.items.forEach((i) => { if (i.label[0].toUpperCase() == searchText[0].toUpperCase()) { newList.push(i); } }); } }); } return newList; }; return ( ); }; ``` ## Notes In order for a user to select an item from the menu while the softkeyboard is up, the prop `keyboardShouldPersistTaps` is set to `'always'`. If the menu is contained in a ScrollView, `keyboardShouldPersistTaps={'always'}` should be set with in that view. This allows for the keyboard to remain open and an item to be selected on initial tap. ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: skeleton-v2 category: Overlay title: V2Skeleton description: Placeholder for loading content. design: https://www.figma.com/design/5djnh49w0SBYAG5tFJifIG/v1.66.0-App-Abyss-Global%E2%80%A8Component-Library?m=auto&node-id=1469-39887&t=og176wcAIBNmTzwi-1 sourceIsTS: true subDirectory: Skeleton/v2 --- ```jsx import { V2Skeleton } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'V2Skeleton', inputs: [ { prop: 'variant', type: 'select', options: [ { label: 'Text', value: 'text' }, { label: 'Square', value: 'square' }, { label: 'Round', value: 'round' }, ], }, { prop: 'height', type: 'string', }, { prop: 'width', type: 'string', }, { prop: 'animated', type: 'boolean' }, ], } ``` ## Usage The `Skeleton` component is a placeholder for content that is loading. It helps convey to users that the page is functioning as intended and that content will appear shortly. ###### Common Use Cases: - Displaying a loading state for text, images, or cards. - Providing a visual cue for asynchronous content. - Maintaining layout consistency while data is being fetched. ```jsx live () => { const avatar = 'https://abyss.uhc.com/img/team/AbyssMobile/Thomas-Musengwa.png'; const cardImage = 'https://abyss.uhc.com/img/graphics/card-image-example.png'; const [isDoneLoading, onToggle] = useToggle(false); const returnContent = () => { if (isDoneLoading) { return ( <> Name goes here Description ); } return null; }; const returnSkeletonStack = () => { if (!isDoneLoading) { return ( ); } return null; }; return ( {returnContent()} {returnSkeletonStack()} ); }; ``` ## Width and Height The `height` and `width` props define the dimensions of the component. Setting their values to `100%` will fill its parent container. The default values for `height` and `width` depend on the variant. For more details, refer to the [Variant](#variant) section. ```jsx live () => { return ( ); }; ``` ## Variant & Alt The `alt` prop adjusts the color of the component, changing it to a lighter shade. The `variant` prop defines the default size and border radius of the component. Use this prop to match the skeleton's shape to the content it represents. ###### Available Variants: - **`text`**: The default variant with a `height` of _20px_, a `width` of _180px_, and slightly rounded edges. Ideal for text blocks. - **`square`**: This variant has a default `height` and `width` of _48px_, with slightly rounded edges. Suitable for square images or icons. - **`round`**: This variant also has a default `height` and `width` of _48px_, but features fully rounded edges. Best for avatars or circular icons. ```jsx live () => { const SkeletonGroup = styled(Layout.Group, { padding: 8, variants: { alt: { true: { backgroundColor: '$gray3' } }, }, }); return ( ); }; ``` ## Animations & Color Animation is enabled by default. To disable it, set the `animated` prop to `false`. The `color` prop can be used to customize the color of the component. You can also customize the loader's color and opacity using the `loaderColor` and `loaderOpacity` props. ```jsx live () => { const [isAnimated, onToggle] = useToggle(true); return ( ); }; ``` ## Skeleton with Children Add children to the `Skeleton` component by placing elements between the `Skeleton` tags. ```jsx live () => { const Row = styled('View', { display: 'flex', flexDirection: 'row', marginVertical: 15, }); const InnerWrapper = styled('View', { padding: '$md', justifyContent: 'space-between', height: '100%', }); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ```

Component Tokens

**Note:** Click on the tokens to copy onto your clipboard. ```jsx render ```
```jsx render ``` ```jsx render ``` --- id: skeleton category: Overlay title: Skeleton description: Placeholder for loading content. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=12865-62521 --- ```jsx import { Skeleton } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Skeleton', inputs: [ { prop: 'variant', type: 'select', options: [ { label: 'rectangular', value: 'rectangular' }, { label: 'rounded', value: 'rounded' }, { label: 'circular', value: 'circular' }, ], }, { prop: 'height', type: 'string', }, { prop: 'width', type: 'string', }, { prop: 'animated', type: 'boolean' }, ], } ``` ## Usage `Skeleton` is a placeholder component for content that is loading. `Skeleton` can help convey to users that content is on its way and the page is functioning as intended. ```jsx live () => { const avatar = 'https://abyss.uhc.com/img/team/scott-houser.jpg'; const cardImage = 'https://abyss.uhc.com/img/graphics/card-image-example.png'; const { isOpen: isDoneLoading, onToggle } = Web.useToggle(); const returnContent = () => { if (isDoneLoading) { return ( <> profile has loaded Name goes here Description ); } }; const returnSkeletonStack = () => { if (!isDoneLoading) { return ( ); } }; return ( {returnContent()} {returnSkeletonStack()} ); }; ``` ## Width and Height Use the `width` and `height` props to size the Skeleton. The default settings are `100%` for both width and height, so if no values are provided, the Skeleton will fill the parent container. ```jsx live () => { return (
); }; ``` ## Variant Use the `variant` prop to control the shape of the `Skeleton` component. The default is `rectangular`, which displays with no rounded edges. `rounded` is displayed with rounded edges and a border radius of `8px`. `circular` is displayed with fully rounded edges. ```jsx live () => { return ( ); }; ``` ## Animated Animation is enabled by default. If you'd like to disable animation, pass in the `animated` prop and set it to a value of `false`. ```jsx live () => { return ( ); }; ``` ## Color Use the `color` prop to set the color of the skeleton. Light mode default is white at 8% opacity. Dark mode default is white at 32% opacity. ```jsx live () => { return ( ); }; ``` ## Skeleton with Children Add children to the `Skeleton` component by placing elements between the `Skeleton` tags. `Children` should be used to place nested skeletons within the main skeleton. ```jsx live () => { const Row = styled('View', { display: 'flex', flexDirection: 'row', marginVertical: 15, }); const InnerWrapper = styled('View', { padding: '$md', justifyContent: 'space-between', height: '100%', }); return ( ); }; ```
```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: style-sheet category: Styling title: StyleSheet description: A utility to define and organize styles in an application --- ```jsx import { StyleSheet } from '@uhg-abyss/mobile'; ``` The `StyleSheet` module is a utility for defining and organizing styles in an application. It provides a way to create an abstraction over native styles, ensuring performance optimization and consistency across different platforms. ## Key Features - **Platform Consistency:** By using `StyleSheet`, you can define styles that work across both iOS and Android, ensuring a uniform look and feel for your application.
- **Performance Optimization:** `StyleSheet optimizes style calculations which can significantly improve the rendering performance of your app. It ensures that styles are calculated once then applied efficiently.
- **Readability and Maintainability:** Using `StyleSheet.create`, you can separate style definitions from your component logic, making your code more readable and easier to maintain. ## Abyss StyleSheet vs. React Native StyleSheet The Abyss StyleSheet extends the React Native StyleSheet API, so it can be used as a direct replacement and adds additional features. The additional functionality works in conjunction with the [useStyleSheet](/mobile/hooks/use-style-sheet) hook. ### Token Typescript Support `StyleSheet` extends the normal TypeScript declarations by allowing additional values to be placed in as values, such as tokens, pixels and rem values. ![StyleSheet TypeScript](/mobile/styleSheet/stylesheet-typescript.png) ### Media Query Media queries allows to have different styles for different screens, platform, direction and orientation. They are supported as properties with `@media` prefix. - **width**: _``_ - **height**: _``_ - **min-width**: _``_ - **min-height**: _``_ - **max-width**: _``_ - **max-height**: _``_ - **direction**: (_`ltr`_ | _`rtl`_) - **platform**: (_`ios`_ | _`web`_ | _`android`_) - **orientation**: (_`landscape`_ | _`portrait`_) - **aspect-ratio**: _``_ Resize browser window to see changes on the box. ```jsx live-expanded const themedStyles = StyleSheet.create({ label: { fontWeight: '$bold', textAlign: 'center', '@media (min-width: 800) and (max-width: 1100)': { color: '$interactive1', }, '@media (orientation: portrait)': { paddingHorizontal: 16, }, '@media (orientation: landscape)': { paddingHorizontal: 44, }, '@media (min-width: 900px)': { color: '$white', }, }, box: { width: 250, height: 200, justifyContent: 'center', backgroundColor: '$gray1', '@media web': { borderRadius: 8, }, '@media (max-width: 700px)': { width: 150, backgroundColor: '$error2', }, '@media (min-width: 900px)': { width: 350, backgroundColor: '$success1', }, }, }); render(() => { const styles = useStyleSheet(themedStyles); return ( Resize browser window to see changes on the box. ); }); ``` ### Pixel and Rem Support Similar to [CSS3 rem unit](https://www.sitepoint.com/understanding-and-using-rem-units-in-css/) it allows to define any integer value as relative to the root element. In our case the root value is is set to `16`. It makes easy to scale app depending on screen size and other conditions. `StyleSheet` also accepts string pixel values (e.g. '16px'), which typically isn't available in React Native. ```jsx live const themedStyles = StyleSheet.create({ text: { color: '$info1', fontSize: '2rem', lineHeight: '50px', fontWeight: '$semibold', }, }); render(() => { const styles = useStyleSheet(themedStyles); return Abyss Mobile; }); ``` ### Operations Any value can contain one of following math operations: `*` , `/`, `+`, `-`. Operands can be numbers, tokens, pixels or rems. There must be a space between operands and operations. ```jsx live const themedStyles = StyleSheet.create({ box: { backgroundColor: '$interactive3', borderRadius: '$xl * 2', padding: '$lg * 2', borderWidth: '2 * 3', borderColor: '$gray3', }, text: { fontSize: '$md * 3', fontWeight: '$bold', color: '$gray3', textAlign: 'center', }, }); render(() => { const styles = useStyleSheet(themedStyles); return ( Abyss Mobile ); }); ``` ## Methods ### create() ```tsx static create(styles: Object): Object; ``` Creates a StyleSheet style reference from the given object. ```jsx live const themedStyles = StyleSheet.create({ container: { flex: 1, padding: '$lg', backgroundColor: '$gray1', }, title: { marginTop: '$md', paddingVertical: '$sm', borderWidth: 4, borderColor: '$gray3', borderRadius: '$xl', backgroundColor: '$interactive3', color: '$gray3', textAlign: 'center', fontSize: '$xl', fontWeight: '$xbold', }, }); render(() => { const styles = useStyleSheet(themedStyles); return ( Abyss Mobile ); }); ``` ### createThemed() ```tsx static createThemed(theme: Theme, styles: Object): Object; ``` If you have direct access to a theme and do not want to use the `useStyleSheet` hook, you can add the theme directly as a parameter to the `createThemed` function and use token values in the StyleSheet. If your theme has token overrides, those can be used as well. ```jsx live const theme = createTheme('uhc', { theme: { sizes: { notTooBigNotTooSmall: 200 }, radii: { fullyRounded: 1000000 }, }, }); const styles = StyleSheet.createThemed(theme, { circle: { backgroundColor: '$interactive3', height: '$notTooBigNotTooSmall', width: '$notTooBigNotTooSmall', borderRadius: '$fullyRounded', }, }); render(() => { return ; }); ``` ### compose() ```tsx static compose(style1: Object, style2: Object): Object | Object[]; ``` Combines two styles such that `style2` will override any styles in `style1`. If either style is falsy, the other one is returned without allocating an array, saving allocations and maintaining reference equality for PureComponent checks. ```jsx live const page = StyleSheet.create({ container: { flex: 1, padding: 24, backgroundColor: 'white', }, text: { fontSize: 30, color: '#4B4D4F', }, }); const lists = StyleSheet.create({ listContainer: { flex: 1, backgroundColor: '#D9E9FA', }, listItem: { fontStyle: 'italic', fontWeight: 'bold', }, }); const container = StyleSheet.compose(page.container, lists.listContainer); const text = StyleSheet.compose(page.text, lists.listItem); render(() => { return ( Abyss Mobile ); }); ``` ### flatten() ```tsx static flatten(style: Object[]): Object; ``` Flattens an array of style objects, into one aggregated style object. ```jsx live const page = StyleSheet.create({ container: { flex: 1, padding: 24, alignItems: 'center', }, text: { color: '#000', fontSize: 14, fontWeight: 'bold', }, code: { marginTop: 12, padding: 12, borderRadius: 8, color: '#666', backgroundColor: '#EAEAEA', }, }); const typography = StyleSheet.create({ header: { color: '#004BA0', fontSize: 30, marginBottom: 36, }, }); const flattenedTextStyle = StyleSheet.flatten([page.text, typography.header]); render(() => { return ( React Native Flattened Text Style {JSON.stringify(flattenedTextStyle, null, 2)} ); }); ``` ### setStyleAttributePreprocessor() ```jsx render Breaking changes will probably happen a lot and will not be reliably announced. The whole thing might be deleted, who knows? Use at your own risk. ```
```tsx static setStyleAttributePreprocessor( property: string, process: (propValue: any) => any, ); ``` Sets a function to use to pre-process a style property value. This is used internally to process color and transform values. You should not use this unless you really know what you are doing and have exhausted other options. ## Properties ### absoluteFill A very common pattern is to create overlays with position absolute and zero positioning (`position: 'absolute', left: 0, right: 0, top: 0, bottom: 0`), so `absoluteFill` can be used for convenience and to reduce duplication of these repeated styles. If you want, absoluteFill can be used to create a customized entry in a StyleSheet. ```jsx live const themedStyles = StyleSheet.create({ container: { height: 250, }, box1: { position: 'absolute', top: 40, left: 40, width: 100, height: 100, backgroundColor: '$error1', }, box2: { width: 100, height: 100, backgroundColor: '$info1', }, box3: { position: 'absolute', top: 120, left: 120, width: 100, height: 100, backgroundColor: '$success1', }, text: { color: '$white', fontSize: 80, }, }); render(() => { const styles = useStyleSheet(themedStyles); return ( 1 2 3 ); }); ``` ### absoluteFillObject Sometimes you may want absoluteFill but with a couple tweaks - absoluteFillObject can be used to create a customized entry in a StyleSheet. ```jsx live const themedStyles = StyleSheet.create({ container: { height: 250, }, box1: { position: 'absolute', top: 40, left: 40, width: 100, height: 100, backgroundColor: '$error1', }, box2: { ...StyleSheet.absoluteFillObject, top: 120, left: 50, width: 100, height: 100, backgroundColor: '$info1', }, box3: { ...StyleSheet.absoluteFillObject, top: 120, left: 120, width: 100, height: 100, backgroundColor: '$success1', }, text: { color: '$white', fontSize: 80, }, }); render(() => { const styles = useStyleSheet(themedStyles); return ( 1 2 3 ); }); ``` ### hairlineWidth This is defined as the width of a thin line on the platform. It can be used as the thickness of a border or division between two elements ```jsx live const themedStyles = StyleSheet.create({ container: { flex: 1, padding: '$xl', }, row: { padding: '$sm', borderBottomColor: '$error1', borderBottomWidth: StyleSheet.hairlineWidth, }, }); render(() => { const styles = useStyleSheet(themedStyles); return ( Abyss Mobile ); }); ``` ## Usage with Abyss component All Abyss components are able to parse objects from the StyleSheet directly, so there is no need to use the `useStylesheet` hook if the styles are only going into Abyss components. ```jsx live const styles = StyleSheet.create({ button: { backgroundColor: '$info1', borderWidth: '4px', borderColor: '$error1', }, }); render(() => { return ; }); ``` --- id: tabs category: Content title: Tabs description: The Tabs component is used to navigate to other pages, or sections of a page. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=9678-35146&mode=design&t=DgoELndJt2PBF6Lf-0 --- ```jsx import { Tabs } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Tabs', inputs: [ { prop: 'disableSwipe', type: 'boolean' }, { prop: 'disableTransition', type: 'boolean', }, { prop: 'color', type: 'string' }, ], } Tab 1 Content Tab 2 Content Tab 3 Content ``` ## Title The `title` property provides a label that describes the purpose of the set of tabs. This is a required property as it gives screen reader users important context. ```jsx Tab 1 Content Tab 2 Content Tab 3 Content ``` ## Initial Tab (Uncontrolled) If you do not need to subscribe to the tabs active state use the `initialTab` property to set the tab that is active at build time. The default is set to the first tab in the sequence. If used instead of active the active state will be uncontrolled and handled internally by the component. ```jsx live Tab 1 Content Content starts with tab 2 Tab 3 Content ``` ## Active Tab (Controlled) To control the Tabs active state, pass in the desired tab to the `activeTab` prop. This must be used in combination with `onChange` to ensure that the active tab state is always current. If no default state is passed the active tab will default to the first tab in the sequence. ```jsx live () => { const [activeTab, setActiveTab] = useState(2); return ( Tab 1 Content Content starts with tab 2 Tab 3 Content ); }; ``` ## Disable Swipe Use the `disableSwipe` prop to disable swiping from one tab to another. ```jsx live Swiping is now disabled. You can go to another tab by pressing one of the tab buttons. Tab 2 Content Tab 3 Content ``` ## Disable Transition Use the `disableTransition` prop to disable animating from one tab to another on either a swipe or a tab button press. ```jsx live Tab 1 Content Tab 2 Content Tab 3 Content ``` ## Scrollable Tabs and Menu If there are more than three tabs, the Tabs component will scroll horizontally. If there are fewer than four tabs and the tabs fit on the screen horizontally, the scroll does not apply. If the tabs do not fit, a horizontal scroll is activated. Ideally, there should be no more than eight tabs. The Tab menu opens up a bottom sheet with a list of the tabs. When there are fewer than four tabs, there is not a Tab menu. ```jsx live Tab 1 Content Tab 2 Content Tab 3 Content Tab 4 Content Tab 5 Content Tab 6 Content ``` ## Tab Bar Color Use the `tabBarColor` prop to change the color of the tab bar. ```jsx live Tab 1 Content Content starts with tab 2 Tab 3 Content ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type Text and Icons on Tabs scale to 3XL. ```jsx render ``` --- id: test-provider category: Providers title: TestProvider description: Used to determine strategy for testing sourceIsTS: true --- ```jsx import { TestProvider } from '@uhg-abyss/mobile'; ``` The `TestProvider` is a component that provides a context for testing purposes. It allows you to specify a strategy for testing, which can be either "root" or "class". The provider wraps the rest of the component tree and applies the specified strategy. ### Class strategy (default) The `"class"` strategy (similar to default) allows testing of nested components by appending the Abyss class name to your `testID`. This creates unique identifiers for each element within the component. For example, let's say the `testID` of [ProgressBar](/mobile/ui/ProgressBar) is set to `"your-test-ID"`. ```jsx ``` We can then refer to the classes for the ProgressBar. ```jsx render ``` The resulting test IDs will be _**`"your-test-ID-abyss-progress-bar-root"`**_ & _**`"your-test-ID-abyss-progress-bar-slide"`**_. ### Root strategy The `"root"` strategy applies the `testID` only to the root element of the component. This means you won't be able to target nested elements within the Abyss component for testing. For example, let's say the `testID` of [ProgressBar](/mobile/ui/ProgressBar) is set to `"your-test-ID"`. ```jsx ``` The resulting testID will still be **_`"your-test-ID"`_**. ```jsx render ``` --- id: text category: Typography title: Text description: Used to create segments of text such as phrases, sentences, and paragraphs. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=9470%3A32790 pagination_prev: mobile/ui/heading # pagination_next: null --- ```jsx import { Text } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Text', inputs: [ { prop: 'children', type: 'string', }, { prop: 'color', type: 'string', }, { prop: 'fontWeight', type: 'string', }, { prop: 'size', type: 'string', }, { prop: 'transform', type: 'select', options: [ { label: 'none', value: 'none' }, { label: 'capitalize', value: 'capitalize' }, { label: 'lowercase', value: 'lowercase' }, { label: 'uppercase', value: 'uppercase' }, ], }, { prop: 'textAlign', type: 'select', options: [ { label: 'left', value: 'left' }, { label: 'center', value: 'center' }, { label: 'right', value: 'right' }, ], }, ], } Enter message here ``` ## Set Global Text Font One of the limitations of our library is the inability to install fonts into applications. Because of this, we have reserved a special token, `$text`, to be added in the createTheme function, which will add the font to all Heading components globally. In the example below, the font 'UHCSans' is set as the text font and will now be applied to all Text components. ```jsx import { ThemeProvider, createTheme } from '@uhg-abyss/mobile'; const theme = createTheme('uhc', { theme: { fonts: { text: 'UHCSans', }, }, }); const App = () => { return ...; }; ``` Any individual Text component can override the font with the `fontFamily` prop. ## Color Use the `color` property to set the color of the text. The default is set to `black`. ```jsx live Some filler text - Black Some filler text - error Some filler text - hex Some filler text - color ``` ## Sizes Use the `size` property to change the size of the text. The default is set to md which is 16px. The values for each size are represented by `FontSize - LineHeight`. ```jsx live Body 1 - Large / 18px - 24px Body 2 - Medium / 16px - 20px Body 3 - Small / 14px - 16px Small 1 - Extra Small / 12px - 16px ``` ## Transform Use the `transform` property to change the formatting of the text. Variants available include the default case, `capitalize` the first letter of each word, `lowercase` all letters, or `uppercase` all letters. ```jsx live Default text Capitalize text Lowercase text Uppercase text ``` ## Text Align Use the `textAlign` prop to change the alignment of the text. Options include `left`, `center` and `right`. Default is `left`. ```jsx live Left Aligned Text Center Aligned Text Right Aligned Text ``` ## Animated Use the `animated` prop to use animation styles on the component. The default is set to `false`. ```jsx live () => { const val = useRef(new Animated.Value(0)).current; const animateTo = (toValue) => { return Animated.timing(val, { toValue, duration: 2000, easing: Easing.linear, useNativeDriver: false, }); }; useEffect(() => { Animated.loop(Animated.sequence([animateTo(1), animateTo(0)])).start(); }); return ( Animated Text Color ); }; ``` ### Nesting Nested text components will inherit properties from the outer `Text`. ```jsx live Outside Text... Inside Text ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: text-field category: Forms title: TextField description: Large input allows users to enter a large amount of text and data. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/branch/1Uml7LO8NTEWoBUAl4DTaG/Abyss-Mobile?type=design&node-id=18955-106021&t=DfyeirEdeaLCVZP9-0 --- ```jsx import { TextField } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'TextField', inputs: [ { prop: 'label', type: 'string', }, { prop: 'hintText', type: 'string', }, { prop: 'errorMessage', type: 'string', }, { prop: 'isRequired', type: 'boolean', }, { prop: 'isDisabled', type: 'boolean', }, { prop: 'helpContent', type: 'string', }, ] } () => { const form = useForm(); return ( ); }; ``` ## useForm (recommended) Using the `useForm` hook with `FormProvider` sets state for the component. ```jsx live () => { const form = useForm(); const handleSubmit = (data) => { console.log('Submitted:', data); }; return ( ); }; ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [value, setValue] = useState(''); const onSubmit = () => { console.log('Submitted:', value); }; return ( ); }; ``` ## Label Use the `label` prop to display a label above the text field. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Error Message Use the `errorMessage` prop to display a custom error message below the text field. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Success Message Use the `successMessage` prop to display a custom success message below the text field. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Disabled Set the `isDisabled` prop to `true` to disable the text field so users cannot enter a value. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## MaxLength Use the `maxLength` prop to set the maximum length of characters accepted as input. The amount of characters remaining will appear in the bottom right. The default value of `maxLength` is 500. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Help Content Use the `helpContent` prop to display a help icon in the top right of the container, which will display the provided content in a modal screen when pressed. ```jsx live () => { const form = useForm(); return ( This is some text used to explain something to the user, but is too long to be hint text. } /> ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: text-input category: Forms title: TextInput description: Allows users to enter text into a UI. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=40307-5604&mode=design&t=dirQ2Ey2NpKNpNfn-0 --- ```jsx import { TextInput } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'TextInput', inputs: [ { prop: 'label', type: 'string', }, { prop: 'hintText', type: 'string', }, { prop: 'errorMessage', type: 'string', }, { prop: 'type', type: 'select', options: [ { label: 'Text', value: 'text' }, { label: 'Password', value: 'password' }, { label: 'Price', value: 'price' }, ] }, { prop: 'isRequired', type: 'boolean', }, { prop: 'isDisabled', type: 'boolean', }, { prop: 'helpContent', type: 'string', }, ] } () => { const form = useForm(); return ( ); }; ``` Note: If the TextInput is inside a ScrollView, verify the `keyboardShouldPersistTaps` prop on the ScrollView is set to `"always"` or `"handled"` to allow pressing icons within the TextInput when the keyboard is open. ## useForm (recommended) Using the `useForm` hook with `FormProvider` sets state for the component. ```jsx live () => { const form = useForm(); const handleSubmit = (data) => { console.log('Submitted:', data); }; return ( ); }; ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [value, setValue] = useState(''); const onSubmit = () => { console.log('Submitted:', value); }; return ( ); }; ``` ## Placeholder Use the `placeholder` prop to give users a short description in the input field before they enter a value. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Label Use the `label` prop to display a label above the input. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Error Message Use the `errorMessage` prop to display a custom error message below the input field. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Success Message Use the `successMessage` prop to display a custom success message below the input field. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Disabled Set the `isDisabled` prop to `true` to disable the input field so users cannot enter a value. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Types Use the `type` prop to set the type of input field to be displayed. Types include: `'text'`, `'email'`, `'password'`, `'price'`, `'number'` and `'phone'`. The default is `text`. ### Text ```jsx live () => { const form = useForm(); return ( ); }; ``` ### Password ```jsx live () => { const form = useForm(); return ( ); }; ``` ### Price ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Prefix Use the `prefix` prop to display content before the input field. This is meant for text and decorative icons. The default prefix for `type='price'` is '$'. ```jsx live () => { const form = useForm(); return ( } /> ); }; ``` ## Suffix Use the `suffix` prop to display content after the input field. ```jsx live () => { const form = useForm(); return ( ); }; ``` ## Action Icon Use the `actionIcon` prop to pass in an actionable button. This can be used to further customize the input component. ```jsx live () => { const form = useForm(); const [isVisible, setIsVisible] = useState(false); return ( } onActionIconPress={() => setIsVisible(true)} /> setIsVisible(false)} actionRight={ } onActionRightPress={() => setIsVisible(false)} > This is where location search would be added ); }; ``` ## Validation Use the `validations` prop to set rules for the field to be valid. Each validation must have a key for it's name and an object that describes the validation. The object must have a message to display what should be validated. To create a custom validation, you can add a `validate` function that takes in the text as an argument and should return a boolean. There are 5 built in validations: `minLength`, `maxLength`, `hasUppercaseLetters`, `hasLowercaseLetters` and `hasNumbers`. For the `minLength` and `maxLength` validations, you can pass in a `value` to determine the amount of characters to check for. - minLength - Returns true if the amount of characters is less than the value. Default: 8 - maxLength - Returns true if the amount of characters is less than the value. Default: 20 - hasUppercaseLetters - Returns true if the text contains uppercase letters. - hasLowercaseLetters - Returns true if the text contains lowercase letters. - hasNumbers - Returns true if the text contains numbers. To check the status of the validations, use the second parameter of the `onChangeText` prop. ```jsx live () => { const [text, setText] = useState(''); const success = useRef(false); const handleChangeText = (text, validations) => { console.log(validations); success.current = Object.values(validations).every((v) => v === true); setText(text); }; return ( { return text.endsWith('Q'); }, }, }} /> ); }; ``` ## Help Content Use the `helpContent` prop to display a help icon in the top right of the container, which will display the provided content in a modal screen when pressed. ```jsx live () => { const form = useForm(); return ( This is some text used to explain something to the user, but is too long to be hint text. } /> ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ## Focus Guidance Abyss does not control the focus of components on the screen when handling validation errors. The [useSetFocus](/mobile/hooks/use-set-focus) hook can be used for this. For text inputs, the focus should move back to the input when there is an error (e.g user typed too many characters). ```jsx render ``` --- id: theme-provider category: Providers title: ThemeProvider description: An Abyss component that passes the theme object down the component tree. --- ```jsx import { ThemeProvider } from '@uhg-abyss/mobile'; ``` # Theming Abyss theming supports changing colors, spacing, box-shadows, font families, font sizes and many other properties. Themes let you apply a consistent tone to your app. It allows you to customize all design aspects of your project in order to meet the specific needs of your business or brand. To configure the theme, wrap your app with a `ThemeProvider` component. ## Theme Provider While Abyss components come with a default theme, the `ThemeProvider` is an optional component to change the theme globally. `ThemeProvider` relies on the context feature of React to pass the theme down to the components, so you need to make sure that `ThemeProvider` is a parent of the components you are trying to customize. This component takes a theme prop and applies it to the entire React tree that it is wrapping around. It should preferably be used at the root of The component tree. ```jsx render ``` ```jsx import { ThemeProvider, createTheme } from '@uhg-abyss/mobile'; import { AppRegistry } from 'react-native'; import { name as appName } from './app.json'; const themeOverride = { theme: { colors: { primary1: '#002677', primary2: '#FFFFFF', secondary1: '#00BED5', secondary2: '#F5B700', }, fonts: {...}, }, }; const theme = createTheme('uhc', themeOverride); const App = () => { return ...; }; AppRegistry.registerComponent(appName, () => App); ``` --- id: timeline-v2 category: Data title: V2Timeline description: A timeline step displays a single event or action of the timeline tracker. design: https://www.figma.com/design/34gRQMq2NxFgeRynjqd24C/Documentation-%7C-UHC-Abyss-Mobile-App?node-id=931-225262&t=iguCSwnU4rMOZ7Dn-0 subDirectory: Timeline/v2 sourceIsTS: true --- ```jsx import { V2Timeline } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'V2Timeline', inputs: [ { prop: 'type', type: 'select', options: [ { label: 'Combo', value: 'combo' }, { label: 'Card', value: 'card' }, { label: 'Progress', value: 'progress' }, ], defaultValue: 'combo', }, { prop: 'variant', type: 'select', options: [ { label: 'Default', value: 'default' }, { label: 'Warning', value: 'warning' }, { label: 'Error', value: 'error' }, { label: 'Info', value: 'info' }, ], defaultValue: 'default', }, { prop: 'headingWeight', type: 'string', defaultValue: '$normal', }, { prop: 'currentStep', type: 'select', options: [ { label: '0', value: 0}, { label: '1', value: 1 }, { label: '2', value: 2 }, { label: '3', value: 3}, { label: '4', value: 4}, { label: '5', value: 5}, ], }, ] } Button } /> ``` ## Timeline Props & Usage The `currentStep` prop is used to define the active timeline step. `currentStep` is required. To get the fully complete state for the timeline, `currentStep` should be set to be greater than the total number of steps. For example, if there are 3 steps, `currentStep` should be set to 4 to show the timeline as fully complete. `variant` is used to select the step variant, The options are `default`, `warning`, `error`, and `info`. Use the `type` prop to select what timeline type is most appropriate for you. The default `type` is `combo`. - `combo` displays the active step card with the steps contained in a card directly below. - `card` wraps the steps within a card. `card` does **NOT** contain the active step card. - `progress` displays only the timeline steps without any wrapper. `headingWeight` is used to modify the heading's fontWeight of every **non-active** step. Use `content` to add custom components inside of the active step card. Use `button` to add a button to the active step card. ## Timeline.Step Props & Usage `Timeline.Step` is used to display an individual step, each step has access to the props below providing the ability to manipulate the data displayed. `Timeline.Step` must be a direct child of `Timeline`. `isDisabled` will disable the step. When a step is disabled, it can still be accessed by `Timeline` via `currentStep`. A disabled step will not animate when active/complete. `heading` defines the step's Heading. `date` defines the date value displayed below `heading`, if a Date object is passed into `date` it will be formatted `MM/DD/YYYY`. Use `paragraph` to add a paragraph below `date`. `content` is used to add custom components to the bottom of the step. ```jsx live () => { const [activeStep, setActiveStep] = useState(1); const [value, setValue] = useState(false); return ( } footer={ {}} type="toggle" value={value} onChange={setValue} /> } > Button } /> ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: timeline category: Data title: Timeline description: A timeline step displays a single event or action of the timeline tracker. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=25304-37067&mode=design&t=lc6nuZ3zKpfWkpNH-0 --- ```jsx import { Timeline } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Timeline', inputs: [ { prop: 'type', type: 'select', options: [ { label: 'Combo', value: 'combo' }, { label: 'Card', value: 'card' }, { label: 'Progress', value: 'progress' }, ], }, { prop: 'variant', type: 'select', options: [ { label: 'Default', value: 'default' }, { label: 'Warning', value: 'warning' }, { label: 'Error', value: 'error' }, { label: 'Info', value: 'info' }, ], }, { prop: 'titleWeight', type: 'string', }, { prop: 'currentStep', type: 'number', }, ] } ``` ## Timeline Props & Usage The `currentStep` prop is used to define the active timeline step. `currentStep` is required. To get the fully complete state for the timeline, `currentStep` should be set to be greater than the total number of steps. For example, if there are 3 steps, `currentStep` should be set to 4 to show the timeline as fully complete. `variant` is used to select the step variant, The options are `default`, `warning`, `error`, and `info`. Use the `type` prop to select what timeline type is most appropriate for you. The default `type` is `combo`. - `combo` displays the `active step card` with the steps contained in a card directly below. - `card` wraps the steps within a card. `card` does **NOT** contain the `active step card`. - `progress` displays only the timeline steps without any wrapper. `titleWeight` is used to modify the title's fontWeight of every **non-active** step. Use `content` to add custom components inside of the `active step card`. Use `button` to add a button to the `active step card`. ```jsx live () => { const [variant, setVariant] = useState('default'); const [currentStep, setCurrentStep] = useState(2); const Heading = styled('Text', { paddingVertical: 12, fontSize: 24, color: '$gray4', fontWeight: '$medium', }); const changeValue = (operator) => { setCurrentStep((v) => Math.min(Math.max(v + operator, 1), 3)); }; return ( Type: Combo } button={ } > Type: Card Type: Progress ); }; ``` ## Timeline.Step Props & Usage `Timeline.Step` is used to display an individual step, each step has access to the props below providing the ability to manipulate the data displayed. `Timeline.Step` must be a direct child of `Timeline`. `isDisabled` will disable the step. When a step is disabled, it can still be accessed by `Timeline` via `currentStep`. A disabled step will not animate when active/complete. `title` defines the step's title. `date` defines the date value displayed below `title`, if a Date object is passed into `date` it will be formatted `MM/DD/YYYY`. Use `description` to add a description below `date`. `content` is used to add custom components to the bottom of the step. ```jsx live () => { const [activeTab, setActiveTab] = useState(1); return ( } > Button } /> ); }; ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: toast category: Feedback title: Toast description: Feedback on the result of an operation that disappears without user action. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=9982%3A35190&t=g1imyz1B5bFHjcvc-0 --- ```jsx import { Toast } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'Toast', inputs: [ { prop: 'variant', type: 'select', options: [ { label: 'success', value: 'success'}, { label: 'warning', value: 'warning'}, { label: 'error', value: 'error'}, { label: "info", value: 'info'}, ] }, { prop: 'children', type: 'string', }, { prop: 'href', type: 'string', }, { prop: 'linkText', type: 'string', }, { prop: 'heading', type: 'string', }, { prop: 'underlineLink', type: 'boolean', }, { prop: 'isClosable', type: 'boolean', }, ], } Link to Google ``` ## Variant Use the `variant` property to set the color of the `Toast`. The options are `success`, `warning`, `error`, and `info`. The default is `success`. ```jsx live You can only compare up to 4 providers Your estimate has been saved! Be careful what you wish for! Your estimate did not save! ``` ## Icons Use the `icon` prop to pass in a specific Icon component. The expected size for the icon is 18px to stay consistent with the height of the text within the Toast. Find further guidance on material icons in the [Material Icons Tab](/mobile/ui/icon-material/). ```jsx live }> filled white favorite } > outlined warning } > outlined local_activity ``` ## Heading Use the `heading` prop to add text above the description. This is optional and should only be used to summarize the paragraph if needed. ```jsx live Description text goes here ``` ## Text Change the children of the Toast to set the text. ```jsx live Your estimate has been saved! ``` ## onPress Use the `onPress` function to handle the action when the link within the Toast is pressed. ```jsx live { console.log('Link Pressed'); }} href="https://www.github.com" linkText="Make A Wish" > You can only compare up to 4 providers ``` ## Link Text Change the `linkText` prop to set the text. Use the `underlineLink` prop to underline the linkText. ```jsx live Your estimate has been saved! console.log('Link pressed')} > Be careful what you wish for! ``` ## Dismissible Toast Use the `isClosable` prop to create a dismissible toast. Instead of a set duration, the toast will remain on the screen until a user presses the close button. Accessibility requires a toast that has a link to be dismissible, this cannot be changed. ```jsx live Your estimate has been saved! Your estimate did not save! ``` ## Toast Functions ### Toast.show() ```ts Toast.show(options: ShowToastOptions): string ``` The Toast will appear at the top of the screen for a default duration of 7 seconds per accessibility. It is recommended to increase the duration if more content is placed within the toast. A toast with a heading and description, or a link, should account for the time a user may need to interact with the toast. To use Toast.show(), import the ToastProvider. ```jsx import { ToastProvider } from '@uhg-abyss/mobile'; ``` - duration - Sets the time before the toast disappears. - placement - Determines the placement of the toast. - offset - Offset space for both the top and bottom of the toast. - onPress - Callback fired when the link is pressed. - onClose - Callback fired when the toast disappears. - href - Sets the URL of the link. - icon - Adds an icon to the Toast component. - linkText - Sets the text of the link. - heading - Sets the heading of the toast. - text - Sets the text of the Toast Message. - variant - Sets the color of the toast. - animationDuration - Sets the transition time of the toast. - underlineLink - Sets the heading of the toast. - isClosable - Adds close icon to close the toast. The toast will not disappear until the close icon is pressed. In addition, Toast.show() also accepts the same props as the Toast component. ### Toast.update() ```ts Toast.update(id: string, options: ShowToastOptions): void ``` The function updates the already shown toast by passing in the unique id of the respective toast and the parameters to update. ### Toast.hide() ```ts Toast.hide(id: string): void ``` The function hides the toast by specifying a unique id that is returned by Toast.show(). ### Toast.hideAll() ```ts Toast.hideAll(): void ``` The function hides all toasts rendered on the screen. ```jsx live const StyledButton = styled(Button, { marginBottom: 10, }); const delay = (ms) => { return new Promise((resolve) => { setTimeout(resolve, ms); }); }; const Screen = () => { const showToast = () => { Toast.show({ text: 'Your estimate has been saved!', onClose: () => console.log('Closed'), placement: 'top', variant: 'success', }); }; const showClosableToast = () => { Toast.show({ text: 'Your estimate did not save!', onClose: () => console.log('Closed'), variant: 'error', isClosable: true, }); }; const showUpdatableToast = () => { const toastId = Toast.show({ text: 'You can only compare up to 4 providers', heading: 'Changes not saved', variant: 'error', placement: 'top', onClose: () => { return console.log('Closed'); }, isClosable: false, }); delay(3000).then(() => { Toast.update(toastId, { text: 'You picked one of our providers. Great choice!', variant: 'success', isClosable: true, heading: 'Changes Saved', }); }); }; const showHidableToast = () => { const toastId = Toast.show({ text: 'You can only compare up to 5 providers', heading: 'Changes not saved', variant: 'warning', placement: 'top', onClose: () => { return console.log('Closed'); }, isClosable: true, }); delay(3000).then(() => { Toast.hide(toastId); }); }; return ( Show Toast Show Closable Toast Show & Updatable Toast Show & Hide Toast Hide all Toasts ); }; render(() => { return ( ); }); ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ```jsx render ``` ## Focus Guidance for Dismissable Toasts Abyss does not control the focus of components on the screen when a dissmissable Toast is closed. To meet accessibility guidelines, the focus must be set to the previous node when closed. The [useSetFocus](/mobile/hooks/use-set-focus) hook can be used for this. For example, if a button is pressed that triggers a Toast, focus must return to that button once it is closed, so that a screen reader or keyboard user may continue using the app where they left off. ```jsx render ``` --- id: toggle-switch category: Forms title: ToggleSwitch description: Used to switch between 2 modes. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?type=design&node-id=9549-32657&mode=design&t=7hfDPwhG2zrhoDjN-0 --- ```jsx import { ToggleSwitch } from '@uhg-abyss/mobile'; ``` ```jsx sandbox { component: 'ToggleSwitch', inputs: [ { prop: 'isDisabled', type: 'boolean', }, { prop: 'showAccessibilityIcon', type: 'boolean', }, ] } () => { const form = useForm(); return ( ); }; ``` ## Usage The toggle switch adapts its style based on the operating system. Use the system dropdown at the top right of the screen to update. ## useForm (recommended) Using the `useForm` hook with `FormProvider` sets state for the component. ```jsx live () => { const form = useForm({ defaultValues: { 'toggle-switch-form': true, }, }); const handleSubmit = (data) => { console.log('Submitted:', data); }; return ( ); }; ``` ## useState Using the `useState` hook gets values from the component state. ```jsx live () => { const [example1, toggleExample1] = useState(true); return ; }; ``` ## isDisabled The `isDisabled` prop when set to true will cause the ToggleSwitch to be rendered in a disabled state, preventing user interaction and providing visual feedback that the component cannot be toggled. ```jsx live () => { const form = useForm({ defaultValues: { 'toggle-switch-disabled-one': true, 'toggle-switch-disabled-two': false, }, }); return ( ); }; ``` ## showAccessibilityIcon The `showAccessibilityIcon` prop when set to true will cause the ToggleSwitch to be rendered with the accessibility icon. ```jsx live () => { const form = useForm({ defaultValues: { 'toggle-switch-accessibility-one': true, 'toggle-switch-accessibility-two': true, 'toggle-switch-accessibility-three': false, }, }); return ( ); }; ``` ```jsx render ``` ```jsx render ``` ## Dynamic Type ToggleSwitch scales up to 3XL. ```jsx render ``` --- id: translate category: I18n title: Translate description: Used to get the translated string from the i18n object. --- ## Usage ```jsx import { Translate } from '@uhg-abyss/mobile/ui/Translate'; ``` ## Usage The `Translate` component can use a function as a child which passes an object with the `t` and `i18n` properties. The `t` property is a function that is used to get the translated string from the i18n object. The `i18n` property is the i18n object. The `t` function takes two arguments. ```jsx t(key: string, replacements?: object): string ``` The `key` argument corresponds to the key in the i18n object. The `replacements` argument is an object that contains the values to replace in the translated string. ## Example Let's use an example to illustrate how to use the `Translate` component. In the [TextField](/mobile/ui/text-field) component, we have a text block that displays the remaining characters available to be typed in the text field. We can get that value with the key `TextField.charactersRemaining`. ```jsx live-expanded {({ t }) => {t('TextField.charactersRemaining')}} ``` If we want to replace the value of the remaining characters, we can pass in the `replacements` object with the key `count`. Replacement values should always appear in double curly braces, (e.g. `{{count}}`). ```jsx live-expanded {({ t }) => {t('TextField.charactersRemaining', { count: 10 })}} ``` We can also use the component to get the translated string from the i18n object. ```jsx live-expanded {({ i18n }) => {i18n.TextField.charactersRemaining}} ``` ```jsx render React.ReactNode', description: '', }, ]} /> ``` --- id: video-player category: Media title: VideoPlayer description: A component that provides playback of video. design: https://www.figma.com/file/wCMblLsq9TxAQvKzY3EfCt/Abyss-Mobile?node-id=47768%3A3163&mode=dev --- ```jsx import { VideoPlayer } from '@uhg-abyss/mobile/ui/VideoPlayer'; ``` ## Setup VideoPlayer requires `react-native-video` to be install as a dependency. To start, install the [react-native-video](https://www.npmjs.com/package/react-native-video) package inside of your mobile folder, then follow the react-native-video [Installation directions](https://www.npmjs.com/package/react-native-video#installation). ## Basic usage `VideoPlayer` can be used with a single `source` prop. In this configuration it will use the native controls in `iOS`, and abyss specific controls for `Android`. `VideoPlayer` is built around [react-native-video](https://www.npmjs.com/package/react-native-video), and thus has the ability to consume any prop available in `react-native-video`, execpt: `fullscreen`. ```jsx ``` ```jsx render VideoPlayer IOS Basic usage VideoPlayer Android Basic usage ``` ## Customization You can use the `thumbnail` prop to display a custom thumbnail before the video is started. ```jsx } /> ``` VideoPlayer custom thumbnail ## Subtitles/CC Be sure to add closed captions to your video via the `textTrack` prop. Closed Captions are required for accessibility. You can provide multiple text tracks, and select the default one using the `selectedTextTrack` prop. ```jsx ``` ## Accessibility - Closed Captions is required for all videos. - Be sure to provide an `videoTitle` prop to the `VideoPlayer` component. This will be used as the A11y title for the video. - It is recommneded to provide a text transcript on the same page as the video player. ```jsx render ``` ```jsx render ``` ```jsx render ``` --- id: overview category: White Labeling title: Overview hideHeaderActions: true --- ## White Labeling with Abyss Abyss is comprised of a group of designers, accessibility experts, engineers, and QE who work together to build design kits, component libraries, and documentation sites that are packed with prebuilt, reusable or global assets that align with our enterprise branding and accessibility standards to ensure quality, drive consistency, and help teams reduce redundancies in design and code, forging seamless collaboration across product portfolios. Abyss helps teams create exceptional digital solutions and user journeys, enhancing user experience while driving familiarity for our end users across different platforms. Teams can feel confident using Abyss, leveraging our components and assets, knowing they don’t have to do design or accessibility checks to ensure compliance. In addition to reusability, we also offer flexibility for customization, ensuring products remain streamlined and effortlessly maintainable through the combination of leveraging what’s available in Abyss and allowing teams to focus more of their time on specific user cases or product needs. This includes white labeling, which allows for design and engineering teams to take any of the reusable components, or building blocks, and apply their own styling and themes, rather than leveraging enterprise brand themes for UHC, UHG, or Optum. ### The Goal The Abyss brand aims to empower white label consuming teams to apply their own themes to the design system, ensuring flexibility for both development and design teams to fully own and manage their tokens. This approach reduces dependency on the core design team for updates while preserving a unified system structure, allowing teams to maintain consistency and efficiency in their digital solutions. ## Glossary **White Labeling** - The process of adapting a framework to support a specific brand or multiple brands not supported by Abyss, while allowing for customization. **Tokens Studio for Figma** - Token Studio is the backbone of the abyss tokening strategy. It's a centralized place to create, manage, and export tokens to both Figma variables and as a dev-consumable JSON file. Read more on Tokens Studio here. **Tokens** - Tokens are key-value pairs consisting of the Token Name and value. These represent fundamental design decisions as abstract, reusable data. They allow for easy adaptation and customization of the Abyss Design System while maintaining consistency for any brand or style. Abyss currently supports tokens for: color, spacing, sizing, border radius, border width, and opacity. - `[Token Key]: [Token Value]` **Theme** - A set of tokens that define a brand. **Base Theme** - A theme whose tokens are managed, published and versioned by Abyss, such as `Optum` and `UHC`. **White-Labeled Theme** - A customized theme whose tokens are managed, published, and versioned by an Abyss consumer for the brands they maintain. ## Tokens To effectively leverage white labeling with Abyss, it's important to understand how tokens function within the design system. Teams looking to white label can use the Abyss token formula and variable map, applying their own values to achieve the desired theming and styling. This standardized token formula ensures consistent application of styles across experiences. Its reusability not only streamlines the process but also provides significant time savings and efficiencies for teams building and maintaining digital experiences. ### What are Tokens? Tokens are even smaller building blocks (or sub-atoms) of the design system, used in place of hardcoded values and built in alignment with various standards that allow teams to maintain scalability and consistency. Each token category has its own structure based on defined rules and guidelines, representing design decisions that set the standard for the design system. When creating new or enhanced designs, use tokens to create a cohesive UI that follows established standards and patterns. For a deeper dive into using tokens, visit our [Tokens](/mobile/brand/uhc/tokens) documentation. --- id: design category: White Labeling title: Design Strategy description: Leveraging design tokens (variables) and Figma libraries to rebrand the Abyss Design System hideHeaderActions: true --- ## Overview Utilizing our white labeling strategy, teams can work with Figma files to create new themes for their customers independently of the Abyss team. This allows you to preview themes in Figma using Abyss components, provide design decisions to developers, implement those design decisions into software, and migrate to new Abyss versions without losing your design decisions.

_ Note the Figma links below require having a Figma account and being granted access to the necessary project. If you don't have access, please click the "request access" button and reach out to Jess Wolff (jessica_wolff@uhc.com) for approval._ ## Figma Files and Assets ### Global Files (Abyss owned) The master library files contain tokenized components used across the design system. They are separated into two categories: design-ready and dev-ready. The design-ready components, if not in the backlog, will have the sprint they’re slotted for in the component description. ### Foundations and Token Files (Abyss owned) Contains all the tokens in the form of Figma variables, as well as documentation components for color semantic tier taxonomy. These files are fed by the Tokens Studio plugin and connected with the global versioned and design-ready files as a library. ### Figma File Duplicates (not owned by Abyss) Abyss will provide duplicates of all six files - three per platform: - a versioned file with dev-ready components - a file with design-ready components - a file with tokens (variables) White label teams are responsible for setting up the files following provided documentation by Abyss. The Abyss version, at the time of duplicating the file, will be noted in the file name. White label teams are responsible for maintaining these files in a way that preserves component structure and functionality and token taxonomy. ## Design Guides The Abyss Design Team has provided detailed documentation on how to utilize tokens to rebrand our design system. - Check out the Getting Started documentation on Figma to learn more about applying your own theme to the Abyss Figma Library. - Understanding token taxonomy is crucial for making informed decisions when modifying color tokens. The Token Taxonomy Guide describes ways to utilize the Abyss token taxonomy documentation and learning how color is applied throughout. - The most efficient way to rebrand the Abyss library is by leveraging the provided Figma-branded library file and the Token Studio plugin. Learn how to edit core tokens here. - For cases when the branding requires a color that is non-existent in the Abyss core tokens or belongs in a different bucket, Abyss Design has outlined a guide to remapping semantic tokens. - It is important to stay aligned with the design system. Abyss has two ways of doing so: - Allowing teams to recreate updates. - Allowing teams to stick to a specific version of Abyss and migrate to a newer version at their own pace. --- id: introduction category: Developer Guide title: Introduction description: Implementing a White Label Theme hideHeaderActions: true --- ## Prepare ### Token Syncing - Familiarize yourself with Token Syncing in Tokens Studio. This is an optional step that would allow you to skip importing the token JSON file manually ### Manual import - Get Token JSON from [Design](/mobile/white-labeling/design) - Add to Repository ## 1. Combine Tokens Use the `flattenTokens` function to flatten and combine the imported tokens from the JSON file into a single object consumable by [createTheme](/mobile/tools/create-theme). This function supports a layered system where tokens from different themes can override core, semantic, and component-level tokens. Check out the [flattenTokens](/mobile/tools/flatten-tokens) documentation for an in-depth look at the function. _Note that this approach assumes the token files are exported from Figma Token Studio._ ```typescript import { flattenTokens } from '@uhg-abyss/mobile'; import core_01 from '../01_core.json'; import semantic_02 from '../02_semantic.json'; import brand_03 from '../03_brand.json'; const brandTheme = flattenTokens(core_01, semantic_02, brand_03); ``` For maintaining multiple brands, refer to the [multi-brand](/mobile/white-labeling/developer-guide/multi-brand-implementation) tutorial. ## 2. Create Theme Object Use the [createTheme](/mobile/tools/create-theme) function to create a theme object from the flattened tokens. This theme object will be used to apply the white label theme to your application. The `createTheme` function takes two arguments: the name of the base theme (`"uhc"`, `"uhg"`, `"optum"`, or `"abyss"`) and an optional object for any overrides you wish to apply, such as your white-labeled theme. ```typescript import { createTheme } from '@uhg-abyss/mobile'; const theme = createTheme('optum', brandTheme); ``` ## 3. Implement New Theme Wrap your application with the [ThemeProvider](/mobile/ui/theme-provider) and pass in the created theme object. This ensures that the theme is applied globally to all components within your application. ```typescript import { AppRegistry } from 'react-native'; import { name as appName } from './app.json'; import { ThemeProvider, createTheme, flattenTokens } from '@uhg-abyss/mobile'; import core_01 from '../01_core.json'; import semantic_02 from '../semantic_02.json'; import brand_03 from '../03_brand.json'; const brandTheme = flattenTokens(core_01, semantic_02, brand_03); const theme = createTheme('optum', brandTheme); const App = () => ( {/* Your application components */} ); AppRegistry.registerComponent(appName, () => App); ``` --- id: multi-brand-implementation category: Developer Guide title: Multi-Brand Themes description: White Labeling for Multiple Brands hideHeaderActions: true --- ## 1. Combine Brand Tokens Use the `flattenTokens` function to combine the tokens from the JSON files into a single object for each different brand. This function supports a layered system where tokens from different themes can override core, semantic, and component level tokens. Check out the [flattenTokens](/mobile/tools/flatten-tokens) documentation for an in-depth look at the function. ```typescript import { flattenTokens } from '@uhg-abyss/mobile'; import brand_A from '..tokens/brand_A.json'; import brand_B from '..tokens/brand_B.json'; const brandThemeObjectA = flattenTokens(core, brand_A); const brandThemeObjectB = flattenTokens(core, brand_B); ``` ## 2. Create Theme Objects Use the [createTheme](/mobile/tools/create-theme) function to create a theme object from the flattened tokens. This theme object will be used to apply the white label theme to your application. The `createTheme` function takes two arguments: the name of the base theme (`"uhc"`, `"uhg"`, `"optum"`, or `"abyss"`) and an optional object for any overrides you wish to apply, such as your white-labeled theme. ```typescript import { createTheme } from '@uhg-abyss/mobile'; const brandThemeA = createTheme('optum', brandThemeObjectA); const brandThemeB = createTheme('optum', brandThemeObjectB); ``` ## 3. Implement New Themes Wrap your application with the [ThemeProvider](/mobile/ui/theme-provider). Depending on the brand selected, pass in the needed brand theme to the theme object. This ensures that the brand theme is applied globally to all components within your application. ```typescript import { ThemeProvider, createTheme, flattenTokens } from '@uhg-abyss/mobile'; import core from '..tokens/core.json'; import brand_A from '..tokens/brand_A.json'; import brand_B from '..tokens/brand_B.json'; // flatten each brands tokens const brandThemeObjectA = flattenTokens(core, brand_A); const brandThemeObjectB = flattenTokens(core, brand_B); // create each brand theme const brandThemeA = createTheme('optum', brandThemeObjectA); const brandThemeB = createTheme('optum', brandThemeObjectB); const App = () => ( {/* Your application */} ); ```