From 26fa216ffa13d8fcd34759f6f02fc89289888940 Mon Sep 17 00:00:00 2001 From: Tim R Date: Thu, 29 Feb 2024 16:27:31 -0600 Subject: [PATCH 1/2] Adding attachment type to Link --- .../src/components/Link/Link.stories.tsx | 9 +++++ .../components/src/components/Link/Link.tsx | 34 ++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/packages/components/src/components/Link/Link.stories.tsx b/packages/components/src/components/Link/Link.stories.tsx index 1465679e..64eaf08c 100644 --- a/packages/components/src/components/Link/Link.stories.tsx +++ b/packages/components/src/components/Link/Link.stories.tsx @@ -86,6 +86,15 @@ export const Inline: Story = { }, } +export const _Attachment: Story = { + args: { + text: 'Attachment.pdf', + onPress: undefined, // Storybook sends a truthy function shell otherwise + type: 'attachment', + a11yValue: { index: 2, total: 5 }, + }, +} + export const _Calendar: Story = { args: { text: 'Add to calendar', diff --git a/packages/components/src/components/Link/Link.tsx b/packages/components/src/components/Link/Link.tsx index de6bde24..1737c1b9 100644 --- a/packages/components/src/components/Link/Link.tsx +++ b/packages/components/src/components/Link/Link.tsx @@ -18,6 +18,7 @@ import { useExternalLink, } from '../../utils/OSfunctions' import { Icon, IconProps } from '../Icon/Icon' +import { t } from 'i18next' import { useColorScheme, useIsScreenReaderEnabled } from '../../utils' // Convenience type to default type-specific props to not existing/being optional @@ -33,6 +34,12 @@ type nullTypeSpecifics = { url?: never } +type attachment = Omit & { + type: 'attachment' + /** Required onPress override logic */ + onPress: () => void +} + type calendar = Omit & { type: 'calendar' /** Required onPress override logic */ @@ -81,6 +88,7 @@ type url = Omit & { } type linkTypes = + | attachment | calendar | call | callTTY @@ -90,6 +98,12 @@ type linkTypes = | text | url +type a11yValue = { + /** Index value of item in list, will have +1 added to */ + index: number + total: number +} + export type LinkAnalytics = { onPress?: () => void onConfirm?: () => void @@ -107,6 +121,8 @@ export type LinkProps = linkTypes & { a11yLabel?: string /** Optional a11yHint to provide additional context */ a11yHint?: string + /** Optional a11yValue for "[position #] of [list total #]" or a custom value descriptive string */ + a11yValue?: a11yValue | string /** Optional override text for leaving app confirmation prompt */ promptText?: leaveAppPromptText /** Optional analytics event logging */ @@ -128,6 +144,7 @@ export const Link: FC = ({ icon, a11yLabel, a11yHint, + a11yValue, promptText, analytics, inlineSingle, @@ -155,6 +172,10 @@ export const Link: FC = ({ } switch (type) { + case 'attachment': + icon = icon ? icon : { name: 'PaperClip' } + _onPress = customOnPress + break case 'calendar': icon = icon ? icon : { name: 'Calendar' } _onPress = customOnPress @@ -226,9 +247,20 @@ export const Link: FC = ({ ) + let ariaValue + if (typeof a11yValue === 'string') { + ariaValue = a11yValue + } else if (a11yValue) { + ariaValue = t('listPosition', { + position: a11yValue.index + 1, + total: a11yValue.total, + }) + } + const a11yProps: TextProps = { - 'aria-label': a11yLabel, + 'aria-label': a11yLabel || text, // or text for Android not reading text if aria-value set accessibilityHint: a11yHint, + 'aria-valuetext': ariaValue, role: 'link', accessible: true, } From 14bbc1d1bfd7e2b82fcbc1e8204a2aa88ae5943d Mon Sep 17 00:00:00 2001 From: Tim R Date: Thu, 29 Feb 2024 16:49:22 -0600 Subject: [PATCH 2/2] Remove inline link --- .../src/components/Link/Link.stories.tsx | 31 ----- .../components/src/components/Link/Link.tsx | 108 +----------------- 2 files changed, 3 insertions(+), 136 deletions(-) diff --git a/packages/components/src/components/Link/Link.stories.tsx b/packages/components/src/components/Link/Link.stories.tsx index 64eaf08c..ce22afbb 100644 --- a/packages/components/src/components/Link/Link.stories.tsx +++ b/packages/components/src/components/Link/Link.stories.tsx @@ -55,37 +55,6 @@ export const DefaultWithIcon: Story = { }, } -const paragraphText: LinkProps['paragraphText'] = [ - // @ts-ignore: TS being wrong and thinking all should be LinkProps and none normalText - { text: 'A sentence may include a ' }, - { - text: 'link that opens in the app', - type: 'custom', - onPress: () => { - null - }, - a11yLabel: 'a11y override', - }, - // @ts-ignore: TS being wrong and thinking all should be LinkProps and none normalText - { text: ' or a ' }, - { - text: 'link that opens in an external app', - type: 'url', - url: 'https://department-of-veterans-affairs.github.io/va-mobile-app/design/intro', - }, - // @ts-ignore: TS being wrong and thinking all should be LinkProps and none normalText - { text: '.' }, -] - -export const Inline: Story = { - args: { - text: '', - onPress: undefined, // Storybook sends a truthy function shell otherwise - type: 'inline', - paragraphText: paragraphText, - }, -} - export const _Attachment: Story = { args: { text: 'Attachment.pdf', diff --git a/packages/components/src/components/Link/Link.tsx b/packages/components/src/components/Link/Link.tsx index 1737c1b9..13deb486 100644 --- a/packages/components/src/components/Link/Link.tsx +++ b/packages/components/src/components/Link/Link.tsx @@ -8,26 +8,23 @@ import { TextStyle, View, } from 'react-native' -import React, { FC, useState } from 'react' +import React, { FC } from 'react' import { FormDirectionsUrl, LocationData, - isIOS, leaveAppPromptText, useExternalLink, } from '../../utils/OSfunctions' import { Icon, IconProps } from '../Icon/Icon' import { t } from 'i18next' -import { useColorScheme, useIsScreenReaderEnabled } from '../../utils' +import { useColorScheme } from '../../utils' // Convenience type to default type-specific props to not existing/being optional type nullTypeSpecifics = { - calendarData?: never locationData?: never /** Optional onPress override logic */ onPress?: () => void - paragraphText?: never phoneNumber?: never textNumber?: never TTYnumber?: never @@ -67,16 +64,6 @@ type directions = Omit & { locationData: LocationData } -type normalText = { - text: string - textA11y?: string -} - -type inline = Omit & { - type: 'inline' - paragraphText: normalText[] | LinkProps[] -} - type text = Omit & { type: 'text' textNumber: string @@ -94,7 +81,6 @@ type linkTypes = | callTTY | custom | directions - | inline | text | url @@ -127,10 +113,6 @@ export type LinkProps = linkTypes & { promptText?: leaveAppPromptText /** Optional analytics event logging */ analytics?: LinkAnalytics - /** Internally used by 'inline' type. Not recommended for consumer use, but - * available to manually insert a link into a paragraph. True builds link - * component with RN Text instead of Pressable for improved wrapping behavior */ - inlineSingle?: boolean /** Optional TestID */ testID?: string } @@ -147,11 +129,9 @@ export const Link: FC = ({ a11yValue, promptText, analytics, - inlineSingle, testID, // Type-specific props locationData, - paragraphText, phoneNumber, textNumber, TTYnumber, @@ -203,8 +183,6 @@ export const Link: FC = ({ launchExternalLink(directions, analytics, promptText) } break - case 'inline': - return case 'text': icon = icon ? icon : { name: 'Text' } _onPress = async (): Promise => { @@ -235,13 +213,7 @@ export const Link: FC = ({ } const iconDisplay = - icon === 'no icon' ? null : inlineSingle ? ( - <> - - {/* Space forms padding prior to link text */} - - - ) : ( + icon === 'no icon' ? null : ( @@ -297,27 +269,6 @@ export const Link: FC = ({ return { ...(pressed ? pressedFont : regularFont), ...textStyle } } - const [pressStyle, setPressStyle] = useState(false) - if (inlineSingle) { - const onPressProps: TextProps = { - onPressIn: () => { - setPressStyle(true) - }, - onPress: onPress ? onPress : _onPress, - onPressOut: () => { - setPressStyle(false) - }, - } - return ( - - {iconDisplay} - - {text} - - - ) - } - return ( {({ pressed }: PressableStateCallbackType) => ( @@ -329,56 +280,3 @@ export const Link: FC = ({ ) } - -const ParagraphText: FC = ({ text, textA11y }) => { - const colorScheme = useColorScheme() - const isDarkMode = colorScheme === 'dark' - - // TODO: Replace with typography tokens - const regularFont: TextStyle = { - fontFamily: 'SourceSansPro-Regular', - fontSize: 20, - lineHeight: 30, - color: isDarkMode ? Colors.grayLightest : Colors.grayDark, - } - - return ( - - {text} - - ) -} - -const InlineLink: FC> = ({ paragraphText }) => { - const screenReaderEnabled = useIsScreenReaderEnabled() - if (screenReaderEnabled && isIOS) { - return ( - - {paragraphText.map((item, index) => { - // key included as this is a list of React components and the renderer worries about losing track - if ('type' in item) { - // Link if type prop exists - item.inlineSingle = undefined - return - } else { - return - } - })} - - ) - } - return ( - - {paragraphText.map((item, index) => { - // key included as this is a list of React components and the renderer worries about losing track - if ('type' in item) { - // Link if type prop exists - item.inlineSingle = true - return - } else { - return - } - })} - - ) -}