Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Alert Collapse/Expand #288

Merged
merged 45 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
8bbbaed
Add expanded component state, collapse functionality, collapse icon
narin Apr 15, 2024
f64e30d
Add collapsible story
narin Apr 15, 2024
721c440
Remove commented haptics and scrollTo code. Fixed left margin for col…
narin Apr 15, 2024
91ccb43
Change story header text
narin Apr 15, 2024
3e09d1d
Refactor to make entire header area pressable
narin Apr 15, 2024
b5f67f3
Clean up unneeded imports
narin Apr 15, 2024
bed831d
Clean up prop comments
narin Apr 15, 2024
10bcdb6
Sort internal functions by display order
narin Apr 15, 2024
f7c17bd
Remove unneeded collapsed variable
narin Apr 15, 2024
64c104e
Clean up
narin Apr 15, 2024
b08aada
Version bump: components-v0.12.1-beta.2
va-mobile-automation-robot Apr 15, 2024
cf5c3f4
Export Alert
narin Apr 15, 2024
eafac84
Version bump: components-v0.12.1-beta.3
va-mobile-automation-robot Apr 15, 2024
09c4d9c
Add initialCollapsedState prop
narin Apr 15, 2024
a6219ff
Version bump: components-v0.12.1-beta.4
va-mobile-automation-robot Apr 15, 2024
887b01a
Merge branch 'main' into feature/246-narin-alert-collapsible
narin Apr 15, 2024
edccedf
Clean up header text logic
narin Apr 15, 2024
5c357f9
Remove initial collapsed state from story
narin Apr 15, 2024
28eb3f1
Use expandable terminology instead of collapsible to match VADS. Add …
narin Apr 15, 2024
66942fc
Rename story
narin Apr 15, 2024
c07fdd5
Reorder initialExpandedState possible values
narin Apr 15, 2024
aefcb0a
Add comments. Remove unneeded View
narin Apr 15, 2024
0f787b8
Add comments
narin Apr 15, 2024
1f33742
Remove unneeded flex
narin Apr 15, 2024
19bfa38
Rename initialExpandedState to initializeExpanded and made boolean fo…
narin Apr 17, 2024
e42ceee
Add TS prop rules
narin Apr 17, 2024
49cd8bc
Remove description logic from _header()
narin Apr 17, 2024
a2b5e9f
Update expandable comment
narin Apr 17, 2024
4e4d294
Update initializeExpanded comment
narin Apr 17, 2024
a28c2e1
Merge branch 'main' into feature/246-narin-alert-collapsible
narin Apr 17, 2024
99f1a6f
Add font scaling for icons
narin Apr 18, 2024
a604070
Add expandable and header comments for default prop type
narin Apr 18, 2024
e2ebc33
Set preventScaling on variant icon. Set maxWidth on chevron icon to 2…
narin Apr 18, 2024
fb28c2c
Merge branch 'main' into feature/246-narin-alert-collapsible
narin Apr 18, 2024
217fcec
Expand touchable area with hitSlop
narin Apr 19, 2024
6d1726d
Clean up hitSlop and spacer
narin Apr 19, 2024
58f6da8
Remove unneeded sizing token
narin Apr 19, 2024
cec6f69
Cleanup
narin Apr 19, 2024
5670a3e
Fix comment typo
narin Apr 19, 2024
cdf451f
Add preventScaling to alert icon, clean up header logic, change space…
narin Apr 23, 2024
0392eaa
Merge branch 'main' into feature/246-narin-alert-collapsible
narin Apr 23, 2024
27b88ca
Replace accessibilityRole with role
narin Apr 23, 2024
bd0fef9
Replace accessibilityRole with role
narin Apr 23, 2024
631ae25
Replace accessibilityState with aria-expanded
narin Apr 23, 2024
d18b496
Fix aria-expanded value
narin Apr 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.12.0",
"version": "0.12.1-beta.4",
"description": "VA Design System Mobile Component Library",
"main": "src/index.tsx",
"scripts": {
Expand Down
22 changes: 22 additions & 0 deletions packages/components/src/components/Alert/Alert.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,25 @@ export const Info: Story = {
},
},
}

export const Expandable: Story = {
args: {
variant: 'info',
header: 'Header',
description: 'Description',
children: children,
expandable: true,
primaryButton: {
label: 'Button Text',
onPress: () => {
null
},
},
secondaryButton: {
label: 'Button Text',
onPress: () => {
null
},
},
},
}
148 changes: 80 additions & 68 deletions packages/components/src/components/Alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { Colors } from '@department-of-veterans-affairs/mobile-tokens'
// import { HapticFeedbackTypes } from 'react-native-haptic-feedback'
import { Text, TextStyle, View, ViewStyle } from 'react-native'
// ^ ScrollView,
import React, { FC } from 'react'
// ^ , RefObject, useEffect, useState

// import { triggerHaptic } from 'utils/haptics'
// import { useAutoScrollToElement } from 'utils/hooks'
import { Pressable, Text, TextStyle, View, ViewStyle } from 'react-native'
import React, { FC, useState } from 'react'

import { BaseColor, Spacer, useColorScheme } from '../../utils'
import { Button, ButtonProps, ButtonVariants } from '../Button/Button'
Expand All @@ -29,15 +23,15 @@ export type AlertProps = {
/** Optional custom content to nest inside Alert
* Use AlertContentColor or appropriate component props to set light/dark mode 'base' gray colors */
children?: React.ReactNode
/** Optional boolean to determine whether the alert should be expandable */
narin marked this conversation as resolved.
Show resolved Hide resolved
expandable?: boolean
narin marked this conversation as resolved.
Show resolved Hide resolved
/** Optional initial state for expandable alert. Defaults to collapsed */
initialExpandedState?: 'collapsed' | 'expanded'
narin marked this conversation as resolved.
Show resolved Hide resolved
/** Optional primary action button */
primaryButton?: ButtonProps
/** Optional secondary action button */
secondaryButton?: ButtonProps
/** Optional boolean for determining when to focus on error alert boxes (e.g. onSaveClicked). */
// focusOnError?: boolean
/** Optional ref for the parent scroll view. Used for scrolling to error alert boxes. */
// scrollViewRef?: RefObject<ScrollView>
/** optional testID */
/** Optional testID */
testId?: string
}

Expand All @@ -52,41 +46,23 @@ export const Alert: FC<AlertProps> = ({
description,
descriptionA11yLabel,
children,
expandable,
initialExpandedState = 'collapsed',
primaryButton,
secondaryButton,
// focusOnError = true,
// scrollViewRef,
testId,
}) => {
const colorScheme = useColorScheme()
const isDarkMode = colorScheme === 'dark'
// const [scrollRef, viewRef, scrollToAlert] = useAutoScrollToElement()
// const [shouldFocus, setShouldFocus] = useState(true)

// useEffect(() => {
// if (
// variant === 'error' &&
// scrollViewRef?.current &&
// (header || description)
// ) {
// scrollRef.current = scrollViewRef.current
// scrollToAlert(-boxPadding)
// }
// setShouldFocus(focusOnError)
// }, [
// variant,
// header,
// description,
// focusOnError,
// scrollRef,
// scrollToAlert,
// scrollViewRef,
// ])
const [expanded, setExpanded] = useState(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't feel strongly since maybe it's clearer this way, but wouldn't this work just as well only using let expanded = ... and flipping the variable instead of useState?

expandable ? initialExpandedState === 'expanded' : true,
)

// TODO: Replace with sizing/dimension tokens
const Sizing = {
_8: 8,
_12: 12,
_16: 16,
_20: 20,
_30: 30,
}
Expand Down Expand Up @@ -151,7 +127,7 @@ export const Alert: FC<AlertProps> = ({
borderLeftWidth: Sizing._8,
padding: Sizing._20,
paddingLeft: Sizing._12, // Adds with borderLeftWidth for 20
width: '100%' // Ensure Alert fills horizontal space, regardless of flexing content
width: '100%', // Ensure Alert fills horizontal space, regardless of flexing content
}

const iconViewStyle: ViewStyle = {
Expand All @@ -170,14 +146,6 @@ export const Alert: FC<AlertProps> = ({
</View>
narin marked this conversation as resolved.
Show resolved Hide resolved
)

// const vibrate = (): void => {
// if (variant === 'error') {
// triggerHaptic(HapticFeedbackTypes.notificationError)
// } else if (variant === 'warning') {
// triggerHaptic(HapticFeedbackTypes.notificationWarning)
// }
// }

// TODO: Replace with typography tokens
const headerFont: TextStyle = {
color: contentColor,
Expand All @@ -194,6 +162,52 @@ export const Alert: FC<AlertProps> = ({
lineHeight: 30,
}

const _header = () => {
const headerText = header ? (
<Text style={headerFont}>{header}</Text>
) : description ? (
narin marked this conversation as resolved.
Show resolved Hide resolved
<Text style={descriptionFont}>{description}</Text>
) : null

const a11yLabel = header
? headerA11yLabel || header
: description
? descriptionA11yLabel || description
: ''

/**
* Wrap header text and expand icon in Pressable if the Alert is expandable
* Otherwise wrap in View with accessibility props
*/
if (expandable) {
return (
<Pressable
onPress={() => setExpanded(!expanded)}
accessibilityRole="tab"
narin marked this conversation as resolved.
Show resolved Hide resolved
narin marked this conversation as resolved.
Show resolved Hide resolved
accessibilityState={{ expanded }}
narin marked this conversation as resolved.
Show resolved Hide resolved
aria-label={a11yLabel}
style={{ flexDirection: 'row' }}>
<View style={{ flex: 1 }}>{headerText}</View>
<View style={iconViewStyle}>
<Spacer horizontal />
<Icon
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth checking with Jessica by bumping the Slack Q&A thread but based on Figma boxes for expandable Alert examples, it visually appears like the the chevron should also carve out space from the content so that text doesn't appear below the chevron when expanded and would wrap before being out that far.

If that is the case, I think this Icon needs to be moved below to be in the same View with iconDisplay below.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a Q&A in the thread for this while adding another somewhat related Q&A.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a spacer to the right of the content to achieve this margin when expandable. Considered using flexbox for this, but it would have required me separating the single Pressable into 2 Pressables for the title and chevron. Thought it'd be better for the screenreader to just have the one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are going to want to unify this in some capacity with the logic below. Can check with Jessica, but playing with the component currently the touch target for expand/collapse is currently only the header text (and trailing space) + the chevron. It doesn't include the alert icon (info/check/etc.) or space that's visually part of the collapsed Alert but not close enough to the text/chevron, leaving a fairly large area to the left end and the border on top/right/bottom sides that doesn't toggle the expand/collapse behavior.

Intuitively, I think tapping anywhere on the collapsed Alert should expand it--and similarly tapping anywhere along the top portion should collapse when expanded. Perhaps even further: when expanded tapping anywhere on the Alert that isn't an internal pressable (Buttons, children components with onPress) should also collapse it.

Basically: wondering if the entire Alert should be wrapped in the Pressable and we just have no onPress for non-expandable versions (and correspondingly hide the expand/collapse a11y stuff behind expandable).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed with Jessica that the entire Alert should be pressable when it's collapsed. Will have to rework the structure so that header is separate from content.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, perhaps we use hitSlop with a Rect object to fill out the padding to be pressable? Doing it that way, we could also pull the Chevron out (to use flexbox like the alert icon) and ensure the entire "header" is pressable when open or closed. Not sure if that might get weird around font scaling with the hitSlop though.

fill={contentColor}
width={Sizing._16}
narin marked this conversation as resolved.
Show resolved Hide resolved
height={Sizing._16}
name={expanded ? 'ChevronUp' : 'ChevronDown'}
/>
</View>
</Pressable>
)
}

return (
<View accessible={true} aria-label={a11yLabel} role="heading">
{headerText}
</View>
)
}

const _primaryButton = () => {
if (!primaryButton) return null

Expand Down Expand Up @@ -229,31 +243,29 @@ export const Alert: FC<AlertProps> = ({
<View style={{ flexDirection: 'row' }}>
{iconDisplay}
narin marked this conversation as resolved.
Show resolved Hide resolved
<View style={{ flex: 1 }}>
{header ? (
<View
// ref={viewRef}
accessible={true}
aria-label={headerA11yLabel || header}
role="heading">
<Text style={headerFont}>{header}</Text>
</View>
) : null}
{header && (description || children) ? <Spacer /> : null}
{description ? (
<View
// ref={!header ? viewRef : undefined}
accessible={true}
aria-label={descriptionA11yLabel || description}>
<Text style={descriptionFont}>{description}</Text>
{_header()}
{expanded && (
<View>
{header && (description || children) ? <Spacer /> : null}
{header && description ? (
<View
accessible={true}
aria-label={descriptionA11yLabel || description}>
<Text style={descriptionFont}>{description}</Text>
</View>
) : null}
{description && children ? <Spacer /> : null}
{children}
</View>
) : null}
{description && children ? <Spacer /> : null}
{children}
{/* {shouldFocus && vibrate()} */}
)}
</View>
</View>
{_primaryButton()}
{_secondaryButton()}
{expanded && (
<>
{_primaryButton()}
{_secondaryButton()}
</>
)}
</View>
)
}
1 change: 1 addition & 0 deletions packages/components/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ if (expoApp && App.initiateExpo) {
}

// Export components here so they are exported through npm
export { Alert } from './components/Alert/Alert'
export { Button, ButtonVariants } from './components/Button/Button'
export { Icon } from './components/Icon/Icon'
export { Link } from './components/Link/Link'
Expand Down
Loading