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] Component Implementation - Button #34

Merged
merged 74 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from 62 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
bdde4f5
Finished all variants
narin Nov 6, 2023
4c5dca3
Refactor to use buttonType. Add white variant
narin Nov 8, 2023
73615cb
Add unit tests
narin Nov 8, 2023
6e4cac8
Add color tokens
narin Nov 8, 2023
60208d3
Version bump: tokens-v0.0.13-alpha.0
va-mobile-automation-robot Nov 8, 2023
1265e91
Fix red color values
narin Nov 8, 2023
4c6cb6a
Merge branch '6870-nr-vabutton' of github.com:department-of-veterans-…
narin Nov 8, 2023
20e4377
Add primary-darker color
narin Nov 8, 2023
f8e311a
Version bump: tokens-v0.0.13-alpha.2
va-mobile-automation-robot Nov 8, 2023
cee8a51
Add blue-vivid-30
narin Nov 8, 2023
3c414b1
Merge branch '6870-nr-vabutton' of github.com:department-of-veterans-…
narin Nov 8, 2023
fba51b8
Version bump: tokens-v0.0.13-alpha.3
va-mobile-automation-robot Nov 8, 2023
279daaf
Version bump: tokens-v0.1.0
va-mobile-automation-robot Nov 8, 2023
96971bf
Use tokenized colors
narin Nov 8, 2023
754f314
Remove White story. Add a11yhint in Primary example
narin Nov 8, 2023
1662034
Version bump: components-v0.1.1-alpha.0
va-mobile-automation-robot Nov 8, 2023
a1cd6ff
export VAButton in index.tsx
narin Nov 8, 2023
83ccad2
Version bump: components-v0.1.1-alpha.1
va-mobile-automation-robot Nov 8, 2023
9dfcb9d
Export ButtonTypes
narin Nov 8, 2023
ea6e190
Merge branch '6870-nr-vabutton' of github.com:department-of-veterans-…
narin Nov 8, 2023
3534b11
Version bump: components-v0.1.1-alpha.2
va-mobile-automation-robot Nov 8, 2023
5644693
Refactor VAButton constants and types into enum
narin Nov 8, 2023
80aaac4
Version bump: components-v0.1.1-alpha.3
va-mobile-automation-robot Nov 8, 2023
daaf163
Add tsdoc
narin Nov 8, 2023
488e51c
Export VAButtonVariants
narin Nov 8, 2023
92aafe4
Merge branch '6870-nr-vabutton' of github.com:department-of-veterans-…
narin Nov 8, 2023
dd60e3e
Version bump: components-v0.1.1-alpha.4
va-mobile-automation-robot Nov 8, 2023
36014a9
Remove console log
narin Nov 8, 2023
69a34b2
Merge branch '6870-nr-vabutton' of github.com:department-of-veterans-…
narin Nov 8, 2023
2cdfa2c
Merge branch 'main' into 6870-nr-vabutton
narin Nov 8, 2023
e3e9f4d
Commit updated yarn.lock
narin Nov 8, 2023
f599981
Version bump: components-v0.1.1-alpha.5
va-mobile-automation-robot Nov 8, 2023
3aa460d
Merge branch 'main' into 6870-nr-vabutton
TimRoe Nov 8, 2023
5bee594
Clean up. Add gray colors for base style
narin Nov 9, 2023
2dcccdd
Version bump: tokens-v0.1.1-alpha.0
va-mobile-automation-robot Nov 9, 2023
47d78c5
Merge branch '6870-nr-vabutton' of github.com:department-of-veterans-…
narin Nov 9, 2023
4b59999
Version bump: components-v0.1.1-alpha.6
va-mobile-automation-robot Nov 9, 2023
6bac3e2
Rename VAButton to Button. Refactor styling to use switch statement. …
narin Nov 9, 2023
3718e21
Merge branch '6870-nr-vabutton' of github.com:department-of-veterans-…
narin Nov 9, 2023
3bdd553
Version bump: tokens-v0.1.1-alpha.1
va-mobile-automation-robot Nov 9, 2023
ac99554
Add base variants. Move borderWidth into switch statement
narin Nov 9, 2023
b0fd79c
Add unit tests
narin Nov 9, 2023
a37d505
Version bump: tokens-v0.2.0
va-mobile-automation-robot Nov 9, 2023
a76a7fe
Added a11yLabel example to Base story
narin Nov 9, 2023
3b9b7b8
Bump to use 0.2.0 of tokens
narin Nov 9, 2023
d91c2cc
yarn lock commits
narin Nov 9, 2023
87e8d98
Merge branch '6870-nr-vabutton' of github.com:department-of-veterans-…
narin Nov 9, 2023
a36f94e
Commit root yarn.lock
narin Nov 9, 2023
2902e3e
Version bump: components-v0.1.1-alpha.7
va-mobile-automation-robot Nov 9, 2023
5b9ecde
Add @storybook/addon-docs. Add docs link to Button story
narin Nov 9, 2023
1dffeeb
Version bump: components-v0.1.1-alpha.9
va-mobile-automation-robot Nov 9, 2023
d9ec113
Separate storybook utils
narin Nov 9, 2023
39a42a4
Merge branch '6870-nr-vabutton' of github.com:department-of-veterans-…
narin Nov 9, 2023
5479bb3
Version bump: components-v0.1.1-alpha.10
va-mobile-automation-robot Nov 9, 2023
41697b2
Fix storybook util import
narin Nov 9, 2023
0efee64
Merge branch '6870-nr-vabutton' of github.com:department-of-veterans-…
narin Nov 9, 2023
51b03a2
Version bump: components-v0.1.1-alpha.11
va-mobile-automation-robot Nov 9, 2023
1feef56
Isolate @storybook/addon-docs imports
narin Nov 9, 2023
76b1a9b
Version bump: components-v0.1.1-alpha.12
va-mobile-automation-robot Nov 9, 2023
f0b50d1
Add missing react import
narin Nov 9, 2023
76e3cee
Merge branch '6870-nr-vabutton' of github.com:department-of-veterans-…
narin Nov 9, 2023
252de4a
Version bump: components-v0.1.1-alpha.13
va-mobile-automation-robot Nov 9, 2023
68b132b
webStorybookColorScheme fix, storybook-dark-mode devDependency'd
TimRoe Nov 15, 2023
747e61c
Set enum string values
narin Nov 16, 2023
9f6a783
Update/add a11y labels and hints
narin Nov 16, 2023
ce5d3a5
Group variant tests
narin Nov 16, 2023
c1ccd01
Grouped tests with default (primary) variant
narin Nov 16, 2023
fe1be63
Cleaned up pressed logic
narin Nov 16, 2023
72ce74c
Combined imports
narin Nov 16, 2023
13fa90c
Version bump: components-v0.1.1-alpha.14
va-mobile-automation-robot Nov 16, 2023
c78fb9c
Further fixing webStorybookColorScheme, removing DOM stuff from npm p…
TimRoe Nov 16, 2023
e27de2d
Version bump: components-v0.1.1-alpha.15
va-mobile-automation-robot Nov 16, 2023
3e74b2e
Version bump: components-v0.2.0
va-mobile-automation-robot Nov 16, 2023
6e53452
Trigger GitHub status checks
narin Nov 16, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ try {

const getStories = () => {
return {
"./src/components/Button/Button.stories.tsx": require("../../src/components/Button/Button.stories.tsx"),
"./src/components/SegmentedControl/SegmentedControl.stories.tsx": require("../../src/components/SegmentedControl/SegmentedControl.stories.tsx"),
};
};
Expand Down
5 changes: 3 additions & 2 deletions 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.1.0",
"version": "0.1.1-alpha.13",
"description": "VA Design System Mobile Component Library",
"main": "src/index.tsx",
"scripts": {
Expand Down Expand Up @@ -39,7 +39,7 @@
},
"homepage": "https://department-of-veterans-affairs.github.io/va-mobile-library",
"dependencies": {
"@department-of-veterans-affairs/mobile-tokens": "0.0.10",
"@department-of-veterans-affairs/mobile-tokens": "0.2.0",
"@os-team/i18next-react-native-language-detector": "^1.0.28",
"i18next": "^23.4.3",
"react-i18next": "^13.0.3",
Expand All @@ -63,6 +63,7 @@
"@react-native-community/slider": "^4.4.2",
"@storybook/addon-actions": "^6.5.16",
"@storybook/addon-controls": "^6.5.16",
"@storybook/addon-docs": "^6.5.16",
"@storybook/addon-essentials": "^6.5.16",
"@storybook/addon-links": "^6.5.16",
"@storybook/addon-ondevice-actions": "^6.5.4",
Expand Down
87 changes: 87 additions & 0 deletions packages/components/src/components/Button/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Button, ButtonProps, ButtonVariants } from './Button'
import { Meta, StoryObj } from '@storybook/react-native'
import { View } from 'react-native'
import { generateDocs } from '../../utils/storybook'
import React from 'react'

const meta: Meta<ButtonProps> = {
title: 'Button',
component: Button,
argTypes: {
onPress: {
action: 'onPress event',
},
},
parameters: {
docs: generateDocs({
narin marked this conversation as resolved.
Show resolved Hide resolved
name: 'Button',
docUrl:
'https://department-of-veterans-affairs.github.io/va-mobile-app/docs/Flagship%20design%20library/Components/Buttons%20and%20Links/VAButton/',
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if @jessicawoodin had an in progress ticket where we'd rename the URL to just be Button instead of VAButton so it matches the component name as well and correspondingly update this reference.

Copy link
Contributor

Choose a reason for hiding this comment

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

Once the Storybook is live, I'm going to publish the documentation to a new page called Button in the design system section of the doc site. No need to worry about renaming this here -- it'll be deleted once the new page is live.

}),
},
decorators: [
(Story) => (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}
>
<Story />
</View>
),
],
}

export default meta

type Story = StoryObj<ButtonProps>

export const Base: Story = {
storyName: 'Base',
args: {
buttonType: ButtonVariants.Base,
label: 'Button text',
a11yLabel: 'A11y label override',
narin marked this conversation as resolved.
Show resolved Hide resolved
},
}

export const BaseSecondary: Story = {
storyName: 'Base Secondary',
args: {
buttonType: ButtonVariants.BaseSecondary,
label: 'Button text',
},
}

export const Destructive: Story = {
storyName: 'Destructive',
args: {
buttonType: ButtonVariants.Destructive,
label: 'Button text',
},
}

export const Primary: Story = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpicky so don't feel strongly, but if there's a simple way within the Doc section to bump Primary to be at the top instead of Base that would probably make more sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could not figure out a way to sort these properly. According to Storybook docs, they're supposed to be sorted by the way they are written in code but I'm not finding that to be the case. There's ways to set the manually, but we'd have to list out each story in an array which will be a bit cumbersome to do everytime we add a new story.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, initially tried just moving it to the top and was disappointed to see the sort didn't change. Oh well.

storyName: 'Primary',
args: {
label: 'Button text',
},
}

export const Secondary: Story = {
storyName: 'Secondary',
args: {
buttonType: ButtonVariants.Secondary,
label: 'Button text',
},
}

export const White: Story = {
storyName: 'White',
args: {
buttonType: ButtonVariants.White,
label: 'Button text',
},
}
271 changes: 271 additions & 0 deletions packages/components/src/components/Button/Button.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
import 'react-native'
import { RenderAPI, fireEvent, render } from '@testing-library/react-native'
import React from 'react'
// Note: test renderer must be required after react-native.
import 'jest-styled-components'

import * as DesignTokens from '@department-of-veterans-affairs/mobile-tokens'

import { Button, ButtonVariants } from './Button'

describe('Button', () => {
let component: RenderAPI
const label = 'Button text'
const onPressSpy = jest.fn()

it('initializes correctly', async () => {
component = render(<Button label={label} onPress={onPressSpy} />)
narin marked this conversation as resolved.
Show resolved Hide resolved
expect(component).toBeTruthy()
})

it('should call onChange', async () => {
component = render(<Button label={label} onPress={onPressSpy} />)
fireEvent.press(component.getByRole('button'))
expect(onPressSpy).toBeCalled()
})

it('should render label', async () => {
component = render(<Button label={label} onPress={onPressSpy} />)
expect(component.findByText('Button text')).toBeTruthy()
})

it('should render Primary variant by default', async () => {
component = render(<Button label={label} onPress={onPressSpy} />)

const button = component.getByRole('button')
const text = component.getByText(label)
const { backgroundColor, borderWidth } = button.props.style
const { color } = text.props.style

expect(backgroundColor).toEqual(
Copy link
Contributor

Choose a reason for hiding this comment

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

Thought about mentioning it, but you'd already ran with integrating into jest tests which is fine for now.

However: wonder if we should explore other testing options (maybe jest has extensions for it too?) that do an actual UI snapshot to test styles instead of programmatically doing it which is a bit forced. I know at some RN virtual conference back in like 2020 or 2021 there was a presentation on a visual testing library that would just have pictures of before and compare to the after and highlight any differences down to the pixel (intentional or not) to confirm. Probably beyond the scope of this ticket anyway, but maybe worth a spike to explore since that seems a much more intuitive way to unit tests styles if it wasn't too much of a lift. In theory, it'd probably be much quicker/less tedious as well if we committed to style testing every component which likely does make sense for a design system.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

DesignTokens.colorUswdsSystemColorBlueVivid60,
)
expect(borderWidth).toEqual(0)
expect(color).toEqual(DesignTokens.colorGrayLightest)
})

it('should render Primary pressed variant', async () => {
component = render(
<Button label={label} onPress={onPressSpy} testOnlyPressed={true} />,
Copy link
Contributor

@TimRoe TimRoe Nov 15, 2023

Choose a reason for hiding this comment

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

Was testOnlyPressed also needed for detox or anything? If not, wonder if there's a way to check the pressed state without needing to add a prop to force it--seems like there should be. Taking a very brief glance at the testing library docs, I see fireEvent.keyDown exists and wonder if there's similarly a press-and-hold function that would also yield the pressed styles. Or maybe even that one works even though it's not a "key" for us.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Was initially trying to use fireEvent(button, 'pressIn') which is the RN equivalent, but it was not working to render the pressed styles. testOnlyPressed was the only one I had success with, but it's not ideal since it has to be exposed as a prop.

)

const button = component.getByRole('button')
const text = component.getByText(label)
const { backgroundColor, borderWidth } = button.props.style
const { color } = text.props.style

expect(backgroundColor).toEqual(
DesignTokens.colorUswdsSystemColorBlueWarmVivid80,
)
expect(borderWidth).toEqual(0)
expect(color).toEqual(DesignTokens.colorGrayLightest)
})

it('should render Base variant', async () => {
component = render(
<Button
label={label}
onPress={onPressSpy}
buttonType={ButtonVariants.Base}
/>,
)

const button = component.getByRole('button')
const text = component.getByText(label)

const { backgroundColor, borderWidth } = button.props.style
const { color } = text.props.style
expect(backgroundColor).toEqual(DesignTokens.colorGrayMedium)
expect(borderWidth).toEqual(0)
expect(color).toEqual(DesignTokens.colorGrayLightest)
})

it('should render Base pressed variant', async () => {
component = render(
<Button
label={label}
onPress={onPressSpy}
buttonType={ButtonVariants.Base}
testOnlyPressed={true}
/>,
)

const button = component.getByRole('button')
const text = component.getByText(label)
const { backgroundColor, borderWidth } = button.props.style
const { color } = text.props.style

expect(backgroundColor).toEqual(DesignTokens.colorUswdsSystemColorGray80)
expect(borderWidth).toEqual(0)
expect(color).toEqual(DesignTokens.colorGrayLightest)
})

it('should render Destructive variant', async () => {
component = render(
<Button
label={label}
onPress={onPressSpy}
buttonType={ButtonVariants.Destructive}
/>,
)

const button = component.getByRole('button')
const text = component.getByText(label)

const { backgroundColor, borderWidth, borderColor } = button.props.style
const { color } = text.props.style

expect(backgroundColor).toEqual(
DesignTokens.colorUswdsSystemColorRedVivid60,
)
expect(borderColor).toEqual('none')
expect(borderWidth).toEqual(0)
expect(color).toEqual(DesignTokens.colorGrayLightest)
})

it('should render Destructive pressed variant', async () => {
component = render(
<Button
label={label}
onPress={onPressSpy}
buttonType={ButtonVariants.Destructive}
testOnlyPressed={true}
/>,
)

const button = component.getByRole('button')
const text = component.getByText(label)
const { backgroundColor, borderWidth } = button.props.style
const { color } = text.props.style

expect(backgroundColor).toEqual(
DesignTokens.colorUswdsSystemColorRedVivid80,
)
expect(borderWidth).toEqual(0)
expect(color).toEqual(DesignTokens.colorGrayLightest)
})

it('should render White variant', async () => {
component = render(
<Button
label={label}
onPress={onPressSpy}
buttonType={ButtonVariants.White}
/>,
)

const button = component.getByRole('button')
const text = component.getByText(label)
const { backgroundColor, borderWidth, borderColor } = button.props.style
const { color } = text.props.style

expect(backgroundColor).toEqual(DesignTokens.colorGrayLightest)
expect(borderColor).toEqual('none')
expect(borderWidth).toEqual(0)
expect(color).toEqual(DesignTokens.colorBlack)
})

it('should render White pressed variant', async () => {
component = render(
<Button
label={label}
onPress={onPressSpy}
buttonType={ButtonVariants.White}
testOnlyPressed={true}
/>,
)

const button = component.getByRole('button')
const text = component.getByText(label)
const { backgroundColor, borderWidth } = button.props.style
const { color } = text.props.style

expect(backgroundColor).toEqual(DesignTokens.colorUswdsSystemColorGray30)
expect(borderWidth).toEqual(0)
expect(color).toEqual(DesignTokens.colorBlack)
})

it('should render Secondary variant', async () => {
component = render(
<Button
label={label}
onPress={onPressSpy}
buttonType={ButtonVariants.Secondary}
/>,
)

const button = component.getByRole('button')
const text = component.getByText(label)
const { backgroundColor, borderWidth, borderColor } = button.props.style
const { color } = text.props.style

expect(backgroundColor).toEqual(DesignTokens.colorWhite)
expect(borderColor).toEqual(DesignTokens.colorUswdsSystemColorBlueVivid60)
expect(borderWidth).toEqual(2)
expect(color).toEqual(DesignTokens.colorUswdsSystemColorBlueVivid60)
})

it('should render Secondary pressed variant', async () => {
component = render(
<Button
label={label}
onPress={onPressSpy}
buttonType={ButtonVariants.Secondary}
testOnlyPressed={true}
/>,
)

const button = component.getByRole('button')
const text = component.getByText(label)
const { backgroundColor, borderColor, borderWidth } = button.props.style
const { color } = text.props.style

expect(backgroundColor).toEqual(DesignTokens.colorWhite)
expect(borderColor).toEqual(
DesignTokens.colorUswdsSystemColorBlueWarmVivid80,
)
expect(borderWidth).toEqual(2)
expect(color).toEqual(DesignTokens.colorUswdsSystemColorBlueWarmVivid80)
})

it('should render BaseSecondary variant', async () => {
component = render(
<Button
label={label}
onPress={onPressSpy}
buttonType={ButtonVariants.BaseSecondary}
/>,
)

const button = component.getByRole('button')
const text = component.getByText(label)
const { backgroundColor, borderWidth, borderColor } = button.props.style
const { color } = text.props.style

expect(backgroundColor).toEqual(DesignTokens.colorWhite)
expect(borderColor).toEqual(DesignTokens.colorGrayMedium)
expect(borderWidth).toEqual(2)
expect(color).toEqual(DesignTokens.colorGrayMedium)
})

it('should render BaseSecondary pressed variant', async () => {
component = render(
<Button
label={label}
onPress={onPressSpy}
buttonType={ButtonVariants.BaseSecondary}
testOnlyPressed={true}
/>,
)

const button = component.getByRole('button')
const text = component.getByText(label)
const { backgroundColor, borderColor, borderWidth } = button.props.style
const { color } = text.props.style

expect(backgroundColor).toEqual(DesignTokens.colorWhite)
expect(borderColor).toEqual(DesignTokens.colorUswdsSystemColorGray80)
expect(borderWidth).toEqual(2)
expect(color).toEqual(DesignTokens.colorUswdsSystemColorGray80)
})
})
Loading
Loading