Skip to content

Commit

Permalink
Merge branch 'feature/495-narin-composite-typography-tokens' of githu…
Browse files Browse the repository at this point in the history
…b.com:department-of-veterans-affairs/va-mobile-library into feature/495-narin-composite-typography-tokens
  • Loading branch information
narin committed Nov 5, 2024
2 parents 95a99fd + a11eee9 commit 6d995f0
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 57 deletions.
26 changes: 26 additions & 0 deletions documentation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# Changelog

## [components-v0.27.0](https://github.com/department-of-veterans-affairs/va-mobile-library/tree/components-v0.27.0) (2024-11-01)

[Full Changelog](https://github.com/department-of-veterans-affairs/va-mobile-library/compare/components-v0.26.0...components-v0.27.0)

**Closed issues:**

- DS - Typography Component - Align with VADS and publish documentation [\#557](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/557)
- Assets - Add BitterRegular font family, remove BitterBold font family [\#538](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/538)
- Accessibility: Add Heading Label for Screenreader Users [\#533](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/533)
- Accessibility: Add Item Count to Checkbox Group for Screenreader Users [\#532](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/532)
- Text Input: Prep for handoff [\#498](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/498)
- \[Design Tokens\] Typography: Add tokens to tokens package [\#494](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/494)
- \[Figma\] Add annotations to template [\#489](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/489)
- DS - Checkbox Component Implementation [\#455](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/455)
- DS - Checkbox Group Component Flagship Work Setup [\#433](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/433)
- DS - Checkbox Group Component Unit Tests [\#431](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/431)
- \[Design Tokens\] Typography: Prep for handoff [\#425](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/425)
- \[Design Tokens\] Colors: Create divider and feedback tokens [\#419](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/419)
- DS - Q3 Bug & Maintenance Mgmt [\#402](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/402)
- DS - Create CheckboxGroup Component [\#69](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/69)

**Merged pull requests:**

- \[Bug\] Link - Flagship crash mitigation efforts [\#573](https://github.com/department-of-veterans-affairs/va-mobile-library/pull/573) ([TimRoe](https://github.com/TimRoe))
- \[Feature\] Checkbox/CheckboxGroup: Add heading role and list position [\#545](https://github.com/department-of-veterans-affairs/va-mobile-library/pull/545) ([narin](https://github.com/narin))

## [components-v0.26.0](https://github.com/department-of-veterans-affairs/va-mobile-library/tree/components-v0.26.0) (2024-10-21)

[Full Changelog](https://github.com/department-of-veterans-affairs/va-mobile-library/compare/assets-v0.14.0...components-v0.26.0)
Expand Down
2 changes: 1 addition & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@department-of-veterans-affairs/mobile-component-library",
"version": "0.26.0",
"version": "0.27.0",
"description": "VA Design System Mobile Component Library",
"main": "src/index.tsx",
"scripts": {
Expand Down
35 changes: 32 additions & 3 deletions packages/components/src/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import { ColorValue, useWindowDimensions } from 'react-native'
import { ColorValue, View, ViewStyle, useWindowDimensions } from 'react-native'
import { SvgProps } from 'react-native-svg'
import React, { FC } from 'react'

import { IconMap } from './iconList'
import { useColorScheme, useTheme } from '../../utils'

type nameOrSvg =
type nameOrSvgOrNoIcon =
| {
/** Name of preset icon to use {@link IconMap} **/
name: keyof typeof IconMap
noIcon?: never
svg?: never
}
| {
name?: never
noIcon?: never
/** Custom SVG passed to display */
svg: React.FC<SvgProps>
}
| {
name?: never
/** True to render icon as a null passthrough */
noIcon: boolean
svg?: never
}

type heightAndWidth =
| {
Expand All @@ -37,8 +45,10 @@ type lightDarkModeFill = {
/**
* Props that need to be passed in to {@link Icon}
*/
export type IconProps = nameOrSvg &
export type IconProps = nameOrSvgOrNoIcon &
heightAndWidth & {
/** Wraps in View that aligns the icon with text of line height passed */
alignWithTextLineHeight?: number
/** Fill color for the icon, defaults to light/dark mode primary blue */
fill?: 'default' | 'base' | ColorValue | lightDarkModeFill
/** Optional maximum width when scaled */
Expand All @@ -60,9 +70,11 @@ export type IconProps = nameOrSvg &
*/
export const Icon: FC<IconProps> = ({
name,
noIcon,
svg,
width = 24,
height = 24,
alignWithTextLineHeight,
fill = 'default',
maxWidth,
preventScaling,
Expand All @@ -73,6 +85,8 @@ export const Icon: FC<IconProps> = ({
const fontScale = useWindowDimensions().fontScale
const fs = (val: number) => fontScale * val

if (noIcon) return null

// ! to override TS incorrectly thinking svg can be undefined after update
const _Icon: FC<SvgProps> = name ? IconMap[name] : svg!

Expand All @@ -96,5 +110,20 @@ export const Icon: FC<IconProps> = ({
iconProps = { ...iconProps, width: fs(width), height: fs(height) }
}

if (alignWithTextLineHeight) {
const viewStyle: ViewStyle = {
alignSelf: 'flex-start',
minHeight: alignWithTextLineHeight * fontScale,
alignItems: 'center',
justifyContent: 'center',
}

return (
<View style={viewStyle}>
<_Icon {...iconProps} testID={testID} />
</View>
)
}

return <_Icon {...iconProps} testID={testID} />
}
5 changes: 5 additions & 0 deletions packages/components/src/components/Link/Link.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ export const _ExternalLink: Story = {
onCancel: () => console.log('Analytics event: Canceled'),
onPress: () => console.log('Analytics event: Pressed'),
onConfirm: () => console.log('Analytics event: Confirmed'),
onOpenURLError: (e, url) => {
console.log(JSON.stringify(e))
console.log('Error: ' + e)
console.log('Error opening URL: ' + url)
},
},
},
}
Expand Down
8 changes: 6 additions & 2 deletions packages/components/src/components/Link/Link.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ describe('Link', () => {
render(<Link {...commonProps} />)
const icon = screen.UNSAFE_queryByType(Icon)

expect(icon).toBeNull()
expect(icon?.props.name).toBeUndefined()
expect(icon?.props.svg).toBeUndefined()
expect(icon?.props.noIcon).toBeDefined()
})
})

Expand Down Expand Up @@ -414,7 +416,9 @@ describe('Link', () => {
render(<Link {...iconOverrideProps} icon={'no icon'} />)
const icon = screen.UNSAFE_queryByType(Icon)

expect(icon).toBeFalsy()
expect(icon?.props.name).toBeUndefined()
expect(icon?.props.svg).toBeUndefined()
expect(icon?.props.noIcon).toBeDefined()
})
})

Expand Down
62 changes: 17 additions & 45 deletions packages/components/src/components/Link/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ import {
Text,
TextProps,
TextStyle,
View,
ViewStyle,
useWindowDimensions,
} from 'react-native'
import { spacing } from '@department-of-veterans-affairs/mobile-tokens'
import { t } from 'i18next'
import React, { FC } from 'react'

import { ComponentWrapper } from '../../wrapper'
Expand All @@ -19,7 +16,7 @@ import {
useExternalLink,
} from '../../utils/OSfunctions'
import { Icon, IconProps } from '../Icon/Icon'
import { t } from 'i18next'
import { Spacer } from '../Spacer/Spacer'
import { useTheme } from '../../utils'

// Convenience type to default type-specific props to not existing/being optional
Expand Down Expand Up @@ -96,6 +93,7 @@ export type LinkAnalytics = {
onPress?: () => void
onConfirm?: () => void
onCancel?: () => void
onOpenURLError?: (e?: unknown, url?: LinkProps['url']) => void
}

export type LinkProps = linkTypes & {
Expand Down Expand Up @@ -142,7 +140,6 @@ export const Link: FC<LinkProps> = ({
url,
}) => {
const theme = useTheme()
const fontScale = useWindowDimensions().fontScale
const launchExternalLink = useExternalLink()

// TODO: Replace with typography tokens
Expand All @@ -152,28 +149,26 @@ export const Link: FC<LinkProps> = ({
lineHeight: 30,
}

let _icon: IconProps | 'no icon'
let _icon: IconProps

/** Function to massage Partial<IconProps> data into IconProps based on variant icon defaults */
const setIcon = (name?: IconProps['name']) => {
switch (icon) {
case 'no icon':
return 'no icon'
return { noIcon: true }
case undefined:
if (name) return { name }
return 'no icon'
return { noIcon: true }
default:
if (icon.svg) return icon as IconProps
if (!name && !icon.name) return 'no icon'
if (!name && !icon.name) return { noIcon: true }

if (!icon.name) icon.name = name
return icon as IconProps
}
}

let _onPress: () => void = async () => {
null // Empty function to keep TS happy a function exists
}
let _onPress: () => void

/** Handler for links not using launchExternalLink prompt */
const customOnPress: () => void = () => {
Expand All @@ -192,15 +187,11 @@ export const Link: FC<LinkProps> = ({
break
case 'call':
_icon = setIcon('Phone')
_onPress = async (): Promise<void> => {
launchExternalLink(`tel:${phoneNumber}`, analytics)
}
_onPress = () => launchExternalLink(`tel:${phoneNumber}`, analytics)
break
case 'call TTY':
_icon = setIcon('TTY')
_onPress = async (): Promise<void> => {
launchExternalLink(`tel:${TTYnumber}`, analytics)
}
_onPress = () => launchExternalLink(`tel:${TTYnumber}`, analytics)
break
case 'custom':
_icon = setIcon()
Expand All @@ -209,24 +200,20 @@ export const Link: FC<LinkProps> = ({
case 'directions':
_icon = setIcon('Directions')
const directions = FormDirectionsUrl(locationData)
_onPress = async (): Promise<void> => {
launchExternalLink(directions, analytics, promptText)
}
_onPress = () => launchExternalLink(directions, analytics, promptText)
break
case 'text':
_icon = setIcon('PhoneIphone')
_onPress = async (): Promise<void> => {
launchExternalLink(`sms:${textNumber}`, analytics)
}
_onPress = () => launchExternalLink(`sms:${textNumber}`, analytics)
break
case 'url':
_icon = setIcon('Launch')
_onPress = async (): Promise<void> => {
launchExternalLink(url, analytics, promptText)
}
_onPress = () => launchExternalLink(url, analytics, promptText)
break
}

_icon.alignWithTextLineHeight = font.lineHeight

let linkColor: string

switch (variant) {
Expand All @@ -237,22 +224,6 @@ export const Link: FC<LinkProps> = ({
linkColor = theme.vadsColorActionForegroundDefault
}

const iconViewStyle: ViewStyle = {
marginRight: spacing.vadsSpace2xs, // Spacer to text
// Below keeps icon aligned with first row of text, centered, and scalable
alignSelf: 'flex-start',
minHeight: font.lineHeight * fontScale,
alignItems: 'center',
justifyContent: 'center',
}

const iconDisplay =
_icon === 'no icon' ? null : (
<View style={iconViewStyle}>
<Icon fill={linkColor} {..._icon} />
</View>
)

let ariaValue
if (typeof a11yValue === 'string') {
ariaValue = a11yValue
Expand Down Expand Up @@ -296,7 +267,8 @@ export const Link: FC<LinkProps> = ({
return (
<ComponentWrapper>
<Pressable {...pressableProps} testID={testID}>
{iconDisplay}
<Icon fill={linkColor} {..._icon} />
{_icon.noIcon ? null : <Spacer size="2xs" horizontal />}
<Text style={textStyle}>{text}</Text>
</Pressable>
</ComponentWrapper>
Expand Down
20 changes: 14 additions & 6 deletions packages/components/src/utils/hooks/useExternalLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function useExternalLink(): (
) => void {
const { t } = useTranslation()

return (
return async (
url: string,
analytics?: LinkAnalytics,
text?: leaveAppPromptText,
Expand All @@ -25,9 +25,13 @@ export function useExternalLink(): (
if (analytics?.onCancel) analytics.onCancel()
}

const onOKPress = () => {
if (analytics?.onConfirm) analytics.onConfirm()
return Linking.openURL(url)
const onOKPress = async () => {
try {
if (analytics?.onConfirm) analytics.onConfirm()
await Linking.openURL(url)
} catch (e) {
if (analytics?.onOpenURLError) analytics.onOpenURLError(e, url)
}
}

const body = text?.body ? text.body : t('leaveAppAlert.body')
Expand All @@ -44,12 +48,16 @@ export function useExternalLink(): (
},
{
text: confirm,
onPress: (): Promise<void> => onOKPress(),
onPress: async (): Promise<void> => onOKPress(),
style: 'default',
},
])
} else {
Linking.openURL(url)
try {
await Linking.openURL(url)
} catch (e) {
if (analytics?.onOpenURLError) analytics.onOpenURLError(e, url)
}
}
}
}

0 comments on commit 6d995f0

Please sign in to comment.