Skip to content

Commit

Permalink
Merge branch 'main' into CU/454-RemoveWhiteButtonVariant
Browse files Browse the repository at this point in the history
  • Loading branch information
TimRoe authored Sep 4, 2024
2 parents ffda2a6 + fa40507 commit 08c3319
Show file tree
Hide file tree
Showing 7 changed files with 566 additions and 16 deletions.
15 changes: 15 additions & 0 deletions documentation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## [components-v0.22.1](https://github.com/department-of-veterans-affairs/va-mobile-library/tree/components-v0.22.1) (2024-09-04)

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

**Closed issues:**

- DS - Tech Discovery for Checkbox Group [\#426](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/426)
- DS - SnackBar Accessibility Review [\#411](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/411)
- DS - Snackbar - Visual QA [\#366](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/366)
- CU - Move CHANGELOG to documentation folder [\#137](https://github.com/department-of-veterans-affairs/va-mobile-library/issues/137)

**Merged pull requests:**

- \[Tests\] Snackbar Unit Tests [\#451](https://github.com/department-of-veterans-affairs/va-mobile-library/pull/451) ([TimRoe](https://github.com/TimRoe))

## [linting-v0.22.1](https://github.com/department-of-veterans-affairs/va-mobile-library/tree/linting-v0.22.1) (2024-08-26)

[Full Changelog](https://github.com/department-of-veterans-affairs/va-mobile-library/compare/components-v0.22.0...linting-v0.22.1)
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.22.0",
"version": "0.22.1",
"description": "VA Design System Mobile Component Library",
"main": "src/index.tsx",
"scripts": {
Expand Down
218 changes: 218 additions & 0 deletions packages/components/src/components/Snackbar/Snackbar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import { fireEvent, render, screen } from '@testing-library/react-native'
import React from 'react'

import { Icon } from '../Icon/Icon'
import { Snackbar, SnackbarProps } from './Snackbar'

// Set so setTimeout for `announceForAccessibility` doesn't persist rendering after the jest test
jest.useFakeTimers()

const onPressHideSpy = jest.fn()
const onPressActionSpy = jest.fn()
const mockedColorScheme = jest.fn()

jest.mock('react-native/Libraries/Utilities/useColorScheme', () => {
return {
default: mockedColorScheme,
}
})

const commonProps: SnackbarProps = {
message: 'Test Snackbar Text',
data: {
isError: false,
messageA11y: 'message a11y',
onActionPressed: onPressActionSpy,
},
onHide: onPressHideSpy,
}

const errorProps: SnackbarProps = {
...commonProps,
data: {
...commonProps.data,
isError: true,
},
}

const getMessageText = () => screen.getByText(commonProps.message)
const getDismissText = () => screen.getByText('Dismiss')
const getIcon = async () => screen.root.findByType(Icon)

describe('Snackbar', () => {
afterEach(() => {
onPressHideSpy.mockReset()
onPressActionSpy.mockReset()
})

describe('Basic UI tests', () => {
it('initializes correctly with `message` text', () => {
render(<Snackbar {...commonProps} />)

expect(getMessageText()).toBeOnTheScreen()
})

it('renders the `Dismiss` button text', () => {
render(<Snackbar {...commonProps} />)

expect(getDismissText()).toBeOnTheScreen()
})

it('calls onHide when `Dismiss` tapped', () => {
render(<Snackbar {...commonProps} />)
fireEvent.press(getDismissText())

expect(onPressHideSpy).toHaveBeenCalled()
expect(onPressActionSpy).not.toHaveBeenCalled()
})

it('renders default `CheckCircle` icon', async () => {
render(<Snackbar {...commonProps} />)
const icon = await getIcon()

expect(icon).toBeDefined()
expect(icon.props.name).toBe('CheckCircle')
})
})

describe('Light mode', () => {
it('renders background color', () => {
render(<Snackbar {...commonProps} />)

expect(screen.root).toHaveStyle({ backgroundColor: '#1b1b1b' })
})

it('renders `message` text and icon color', () => {
render(<Snackbar {...commonProps} />)

expect(getMessageText()).toHaveStyle({ color: '#f0f0f0' })
})

it('renders button text color', () => {
render(<Snackbar {...commonProps} />)

expect(getDismissText()).toHaveStyle({ color: '#ffffff' })
})

it('renders alternate button text color when pressed', () => {
render(<Snackbar {...commonProps} />)
const longPress = getDismissText()
fireEvent(longPress, 'onResponderGrant', {
persist: jest.fn(),
nativeEvent: {
timestamp: Date.now(),
},
})

expect(longPress).toHaveStyle({ color: '#a9aeb1' })
})
})

describe('Dark mode', () => {
beforeAll(() => mockedColorScheme.mockImplementation(() => 'dark'))
afterAll(() => mockedColorScheme.mockReset())

it('renders background color', () => {
render(<Snackbar {...commonProps} />)

expect(screen.root).toHaveStyle({ backgroundColor: '#f0f0f0' })
})

it('renders `message` text and icon color', () => {
render(<Snackbar {...commonProps} />)

expect(getMessageText()).toHaveStyle({ color: '#1b1b1b' })
})

it('renders button text color', () => {
render(<Snackbar {...commonProps} />)

expect(getDismissText()).toHaveStyle({ color: '#000000' })
})

it('renders alternate button text color when pressed', () => {
render(<Snackbar {...commonProps} />)
const longPress = getDismissText()
fireEvent(longPress, 'onResponderGrant', {
persist: jest.fn(),
nativeEvent: {
timestamp: Date.now(),
},
})

expect(longPress).toHaveStyle({ color: '#565c65' })
})
})

describe('Action button', () => {
it('renders `Undo` button by default', () => {
render(<Snackbar {...commonProps} />)

expect(screen.getByText('Undo')).toBeOnTheScreen()
expect(screen.queryByText('Try again')).not.toBeOnTheScreen()
})

it('renders `CheckCircle` icon by default', async () => {
render(<Snackbar {...commonProps} />)
const icon = await getIcon()

expect(icon).toBeDefined()
expect(icon.props.name).toBe('CheckCircle')
})

it('error renders `Try again` button', () => {
render(<Snackbar {...errorProps} />)

expect(screen.getByText('Try again')).toBeOnTheScreen()
expect(screen.queryByText('Undo')).not.toBeOnTheScreen()
})

it('error renders `Warning` icon', async () => {
render(<Snackbar {...errorProps} />)
const icon = await getIcon()

expect(icon).toBeDefined()
expect(icon.props.name).toBe('Warning')
})

it('calls action button and onHide logic when pressed', () => {
render(<Snackbar {...commonProps} />)
fireEvent.press(getDismissText())

expect(onPressHideSpy).toHaveBeenCalledTimes(1)
expect(onPressActionSpy).toHaveBeenCalledTimes(0)

fireEvent.press(screen.getByText('Undo'))

expect(onPressHideSpy).toHaveBeenCalledTimes(2)
expect(onPressActionSpy).toHaveBeenCalledTimes(1)
})
})

describe('Accessibility', () => {
it('message has aria-label override when present', () => {
render(<Snackbar {...commonProps} />)

expect(getMessageText()).toHaveAccessibleName('message a11y')
})

it('message defers to text displayed when no aria-label', () => {
render(<Snackbar {...commonProps} data={{}} />)

expect(getMessageText()).not.toHaveAccessibleName('message a11y')
expect(getMessageText()).toHaveAccessibleName('Test Snackbar Text')
})

it('Snackbar has `alert` accessibility role', () => {
render(<Snackbar {...commonProps} />)

expect(screen.getByRole('alert')).toBeOnTheScreen()
})

it('buttons have `button` accessibility role', () => {
render(<Snackbar {...commonProps} />)

expect(screen.getAllByRole('button').length).toEqual(2)
})
})
})
27 changes: 15 additions & 12 deletions packages/components/src/components/Snackbar/Snackbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ToastProps } from 'react-native-toast-notifications/lib/typescript/toas
import { useTranslation } from 'react-i18next'
import React, { FC, useEffect } from 'react'

import { ComponentWrapper } from '../../wrapper'
import { Icon, IconProps } from '../Icon/Icon'
import { Spacer } from '../Spacer/Spacer'
import { isAndroid } from '../../utils/OSfunctions'
Expand Down Expand Up @@ -55,7 +56,7 @@ const SnackbarButton: FC<SnackbarButtonProps> = ({ text, onPress }) => {
* All options associated with the useSnackbar.show function
*/
export type SnackbarOptions = SnackbarData & {
/** offset from bottom of screen. defaults to 50 in SnackbarProvider */
/** offset from bottom of screen. defaults to NavBar height + device inset */
offset?: number
}

Expand Down Expand Up @@ -238,18 +239,20 @@ export const Snackbar: FC<SnackbarProps> = (toast) => {
}

return (
<View {...containerProps}>
<View {...iconAndMessageContainer}>
<View style={iconViewStyle}>
<Icon {...iconProps} />
<ComponentWrapper>
<View {...containerProps}>
<View {...iconAndMessageContainer}>
<View style={iconViewStyle}>
<Icon {...iconProps} />
</View>
<Spacer size={sizing._8} horizontal />
<Text {...messageProps}>{toast.message}</Text>
</View>
<View {...buttonContainer}>
{actionButton()}
<SnackbarButton text={t('dismiss')} onPress={toast.onHide} />
</View>
<Spacer size={sizing._8} horizontal />
<Text {...messageProps}>{toast.message}</Text>
</View>
<View {...buttonContainer}>
{actionButton()}
<SnackbarButton text={t('dismiss')} onPress={toast.onHide} />
</View>
</View>
</ComponentWrapper>
)
}
Loading

0 comments on commit 08c3319

Please sign in to comment.