@primer/components 33.0.0-rc.b495ba4a → 33.1.0-rc.6856bcf5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/.devcontainer/devcontainer.json +1 -1
  2. package/.github/workflows/ci.yml +1 -1
  3. package/.github/workflows/release.yml +6 -27
  4. package/.github/workflows/release_canary.yml +4 -60
  5. package/.github/workflows/release_candidate.yml +5 -51
  6. package/.github/workflows/statuses.yml +32 -0
  7. package/.gitignore +1 -0
  8. package/.nvmrc +1 -1
  9. package/CHANGELOG.md +20 -0
  10. package/contributor-docs/CONTRIBUTING.md +14 -61
  11. package/dist/browser.esm.js +2 -2209
  12. package/dist/browser.esm.js.map +1 -1
  13. package/dist/browser.umd.js +2 -2209
  14. package/dist/browser.umd.js.map +1 -1
  15. package/docs/content/AnchoredOverlay.mdx +121 -1
  16. package/docs/content/Avatar.mdx +29 -14
  17. package/docs/content/AvatarPair.mdx +47 -0
  18. package/docs/content/AvatarStack.mdx +14 -6
  19. package/docs/content/Box.mdx +13 -11
  20. package/docs/content/BranchName.mdx +52 -0
  21. package/docs/content/{Breadcrumbs.md → Breadcrumbs.mdx} +21 -13
  22. package/docs/content/Link.mdx +75 -0
  23. package/docs/content/Radio.md +90 -0
  24. package/docs/content/TextInput.mdx +125 -0
  25. package/docs/content/drafts/ActionList2.mdx +484 -0
  26. package/docs/content/drafts/ActionMenu2.mdx +302 -0
  27. package/docs/src/@primer/gatsby-theme-doctocat/live-code-scope.js +9 -1
  28. package/docs/src/@primer/gatsby-theme-doctocat/mdx-components.js +15 -2
  29. package/docs/src/@primer/gatsby-theme-doctocat/nav.yml +4 -0
  30. package/docs/src/component-checklist.js +10 -2
  31. package/docs/src/props-table.js +165 -0
  32. package/docs/src/props.js +14 -28
  33. package/lib/ActionList/Header.js +1 -1
  34. package/lib/ActionList/Item.js +10 -10
  35. package/lib/ActionList/List.js +1 -1
  36. package/lib/ActionList2/ActionListContainerContext.d.ts +10 -0
  37. package/lib/ActionList2/ActionListContainerContext.js +15 -0
  38. package/lib/ActionList2/Divider.d.ts +3 -2
  39. package/lib/ActionList2/Divider.js +10 -5
  40. package/lib/ActionList2/Item.js +22 -8
  41. package/lib/ActionList2/List.js +12 -2
  42. package/lib/ActionList2/Selection.js +11 -0
  43. package/lib/ActionList2/index.d.ts +1 -2
  44. package/lib/ActionMenu2.d.ts +317 -0
  45. package/lib/ActionMenu2.js +125 -0
  46. package/lib/Autocomplete/Autocomplete.d.ts +2 -1
  47. package/lib/Autocomplete/AutocompleteInput.d.ts +2 -1
  48. package/lib/BaseStyles.js +2 -20
  49. package/lib/BorderBox.js +1 -1
  50. package/lib/Box.js +1 -1
  51. package/lib/BranchName.js +1 -1
  52. package/lib/Breadcrumbs.js +3 -3
  53. package/lib/Button/Button.d.ts +2 -2
  54. package/lib/Button/Button.js +1 -1
  55. package/lib/Button/ButtonClose.d.ts +2 -2
  56. package/lib/Button/ButtonDanger.d.ts +2 -2
  57. package/lib/Button/ButtonGroup.js +1 -1
  58. package/lib/Button/ButtonInvisible.d.ts +2 -2
  59. package/lib/Button/ButtonOutline.d.ts +2 -2
  60. package/lib/Button/ButtonPrimary.d.ts +2 -2
  61. package/lib/Checkbox.d.ts +1 -1
  62. package/lib/Checkbox.js +1 -1
  63. package/lib/CircleOcticon.d.ts +35 -35
  64. package/lib/Details.js +1 -1
  65. package/lib/Dialog.d.ts +37 -37
  66. package/lib/Dropdown.d.ts +6 -6
  67. package/lib/DropdownMenu/DropdownButton.d.ts +6 -3
  68. package/lib/FilterList.d.ts +1 -1
  69. package/lib/FilteredActionList/FilteredActionList.js +1 -1
  70. package/lib/Flex.js +1 -1
  71. package/lib/LabelGroup.js +1 -1
  72. package/lib/Overlay.js +1 -1
  73. package/lib/Pagination/Pagination.js +2 -2
  74. package/lib/Position.d.ts +4 -4
  75. package/lib/Position.js +1 -1
  76. package/lib/Radio.d.ts +38 -0
  77. package/lib/Radio.js +55 -0
  78. package/lib/SelectMenu/SelectMenu.d.ts +11 -10
  79. package/lib/SelectMenu/SelectMenu.js +1 -1
  80. package/lib/SelectMenu/SelectMenuFilter.js +1 -1
  81. package/lib/SelectMenu/SelectMenuFooter.js +1 -1
  82. package/lib/SelectMenu/SelectMenuItem.d.ts +1 -1
  83. package/lib/SelectMenu/SelectMenuItem.js +1 -1
  84. package/lib/SelectMenu/SelectMenuModal.d.ts +1 -1
  85. package/lib/SelectMenu/SelectMenuTab.js +1 -1
  86. package/lib/SelectMenu/SelectMenuTabPanel.js +1 -1
  87. package/lib/SelectMenu/SelectMenuTabs.js +1 -1
  88. package/lib/StateLabel.js +1 -1
  89. package/lib/StyledOcticon.js +1 -1
  90. package/lib/SubNav.js +3 -3
  91. package/lib/TextInputWithTokens.d.ts +2 -1
  92. package/lib/ThemeProvider.d.ts +1 -0
  93. package/lib/ThemeProvider.js +17 -4
  94. package/lib/Timeline.js +4 -4
  95. package/lib/Token/AvatarToken.d.ts +1 -1
  96. package/lib/Token/AvatarToken.js +1 -1
  97. package/lib/Token/IssueLabelToken.d.ts +1 -1
  98. package/lib/Token/Token.d.ts +1 -1
  99. package/lib/Token/TokenBase.js +1 -1
  100. package/lib/Tooltip.js +1 -1
  101. package/lib/UnderlineNav.js +2 -2
  102. package/lib/__tests__/Radio.test.d.ts +2 -0
  103. package/lib/__tests__/Radio.test.js +202 -0
  104. package/lib/__tests__/ThemeProvider.test.js +114 -0
  105. package/lib/drafts.d.ts +1 -0
  106. package/lib/drafts.js +13 -0
  107. package/lib/hooks/index.d.ts +1 -0
  108. package/lib/hooks/index.js +9 -1
  109. package/lib/index.d.ts +2 -0
  110. package/lib/index.js +8 -0
  111. package/lib/stories/ActionList.stories.js +3 -3
  112. package/lib/stories/ActionList2.stories.js +1 -1
  113. package/lib/stories/ActionMenu2.stories.js +455 -0
  114. package/lib/stories/Checkbox.stories.js +4 -4
  115. package/lib/stories/Radio.stories.js +146 -0
  116. package/lib/stories/ThemeProvider.stories.js +1 -5
  117. package/lib/stories/useFocusTrap.stories.js +1 -11
  118. package/lib/stories/useFocusZone.stories.js +2 -6
  119. package/lib-esm/ActionList/Header.js +1 -1
  120. package/lib-esm/ActionList/Item.js +10 -10
  121. package/lib-esm/ActionList/List.js +1 -1
  122. package/lib-esm/ActionList2/ActionListContainerContext.d.ts +10 -0
  123. package/lib-esm/ActionList2/ActionListContainerContext.js +3 -0
  124. package/lib-esm/ActionList2/Divider.d.ts +3 -2
  125. package/lib-esm/ActionList2/Divider.js +8 -5
  126. package/lib-esm/ActionList2/Item.js +20 -8
  127. package/lib-esm/ActionList2/List.js +10 -2
  128. package/lib-esm/ActionList2/Selection.js +9 -0
  129. package/lib-esm/ActionList2/index.d.ts +1 -2
  130. package/lib-esm/ActionMenu2.d.ts +317 -0
  131. package/lib-esm/ActionMenu2.js +100 -0
  132. package/lib-esm/Autocomplete/Autocomplete.d.ts +2 -1
  133. package/lib-esm/Autocomplete/AutocompleteInput.d.ts +2 -1
  134. package/lib-esm/BaseStyles.js +2 -20
  135. package/lib-esm/BorderBox.js +1 -1
  136. package/lib-esm/Box.js +1 -1
  137. package/lib-esm/BranchName.js +1 -1
  138. package/lib-esm/Breadcrumbs.js +3 -3
  139. package/lib-esm/Button/Button.d.ts +2 -2
  140. package/lib-esm/Button/Button.js +1 -1
  141. package/lib-esm/Button/ButtonClose.d.ts +2 -2
  142. package/lib-esm/Button/ButtonDanger.d.ts +2 -2
  143. package/lib-esm/Button/ButtonGroup.js +1 -1
  144. package/lib-esm/Button/ButtonInvisible.d.ts +2 -2
  145. package/lib-esm/Button/ButtonOutline.d.ts +2 -2
  146. package/lib-esm/Button/ButtonPrimary.d.ts +2 -2
  147. package/lib-esm/Checkbox.d.ts +1 -1
  148. package/lib-esm/Checkbox.js +1 -1
  149. package/lib-esm/CircleOcticon.d.ts +35 -35
  150. package/lib-esm/Details.js +1 -1
  151. package/lib-esm/Dialog.d.ts +37 -37
  152. package/lib-esm/Dropdown.d.ts +6 -6
  153. package/lib-esm/DropdownMenu/DropdownButton.d.ts +6 -3
  154. package/lib-esm/FilterList.d.ts +1 -1
  155. package/lib-esm/FilteredActionList/FilteredActionList.js +1 -1
  156. package/lib-esm/Flex.js +1 -1
  157. package/lib-esm/LabelGroup.js +1 -1
  158. package/lib-esm/Overlay.js +1 -1
  159. package/lib-esm/Pagination/Pagination.js +2 -2
  160. package/lib-esm/Position.d.ts +4 -4
  161. package/lib-esm/Position.js +1 -1
  162. package/lib-esm/Radio.d.ts +38 -0
  163. package/lib-esm/Radio.js +40 -0
  164. package/lib-esm/SelectMenu/SelectMenu.d.ts +11 -10
  165. package/lib-esm/SelectMenu/SelectMenu.js +1 -1
  166. package/lib-esm/SelectMenu/SelectMenuFilter.js +1 -1
  167. package/lib-esm/SelectMenu/SelectMenuFooter.js +1 -1
  168. package/lib-esm/SelectMenu/SelectMenuItem.d.ts +1 -1
  169. package/lib-esm/SelectMenu/SelectMenuItem.js +1 -1
  170. package/lib-esm/SelectMenu/SelectMenuModal.d.ts +1 -1
  171. package/lib-esm/SelectMenu/SelectMenuTab.js +1 -1
  172. package/lib-esm/SelectMenu/SelectMenuTabPanel.js +1 -1
  173. package/lib-esm/SelectMenu/SelectMenuTabs.js +1 -1
  174. package/lib-esm/StateLabel.js +1 -1
  175. package/lib-esm/StyledOcticon.js +1 -1
  176. package/lib-esm/SubNav.js +3 -3
  177. package/lib-esm/TextInputWithTokens.d.ts +2 -1
  178. package/lib-esm/ThemeProvider.d.ts +1 -0
  179. package/lib-esm/ThemeProvider.js +17 -4
  180. package/lib-esm/Timeline.js +4 -4
  181. package/lib-esm/Token/AvatarToken.d.ts +1 -1
  182. package/lib-esm/Token/AvatarToken.js +1 -1
  183. package/lib-esm/Token/IssueLabelToken.d.ts +1 -1
  184. package/lib-esm/Token/Token.d.ts +1 -1
  185. package/lib-esm/Token/TokenBase.js +1 -1
  186. package/lib-esm/Tooltip.js +1 -1
  187. package/lib-esm/UnderlineNav.js +2 -2
  188. package/lib-esm/__tests__/Radio.test.d.ts +2 -0
  189. package/lib-esm/__tests__/Radio.test.js +183 -0
  190. package/lib-esm/__tests__/ThemeProvider.test.js +114 -0
  191. package/lib-esm/drafts.d.ts +1 -0
  192. package/lib-esm/drafts.js +2 -1
  193. package/lib-esm/hooks/index.d.ts +1 -0
  194. package/lib-esm/hooks/index.js +2 -1
  195. package/lib-esm/index.d.ts +2 -0
  196. package/lib-esm/index.js +1 -0
  197. package/lib-esm/stories/ActionList.stories.js +3 -3
  198. package/lib-esm/stories/ActionList2.stories.js +1 -1
  199. package/lib-esm/stories/ActionMenu2.stories.js +393 -0
  200. package/lib-esm/stories/Checkbox.stories.js +5 -5
  201. package/lib-esm/stories/Radio.stories.js +121 -0
  202. package/lib-esm/stories/ThemeProvider.stories.js +1 -5
  203. package/lib-esm/stories/useFocusTrap.stories.js +1 -11
  204. package/lib-esm/stories/useFocusZone.stories.js +2 -6
  205. package/package-lock.json +1366 -3544
  206. package/package.json +14 -8
  207. package/script/component-status-project/build.ts +100 -0
  208. package/script/component-status-project/deploy.rb +142 -0
  209. package/src/ActionList2/ActionListContainerContext.tsx +14 -0
  210. package/src/ActionList2/Divider.tsx +13 -8
  211. package/src/ActionList2/Item.tsx +14 -6
  212. package/src/ActionList2/List.tsx +6 -2
  213. package/src/ActionList2/Selection.tsx +9 -0
  214. package/src/ActionMenu2.tsx +116 -0
  215. package/src/BranchName.tsx +2 -1
  216. package/src/Radio.tsx +76 -0
  217. package/src/ThemeProvider.tsx +22 -5
  218. package/src/__tests__/Radio.test.tsx +174 -0
  219. package/src/__tests__/ThemeProvider.test.tsx +116 -0
  220. package/src/__tests__/__snapshots__/BranchName.test.tsx.snap +3 -1
  221. package/src/__tests__/__snapshots__/Radio.test.tsx.snap +16 -0
  222. package/src/drafts.ts +1 -0
  223. package/src/hooks/index.ts +1 -0
  224. package/src/index.ts +2 -0
  225. package/src/stories/ActionMenu2.stories.tsx +605 -0
  226. package/src/stories/Checkbox.stories.tsx +1 -3
  227. package/src/stories/Radio.stories.tsx +126 -0
  228. package/stats.html +1 -1
  229. package/tsconfig.build.json +1 -1
  230. package/tsconfig.json +1 -1
  231. package/docs/content/ActionList2.mdx +0 -379
  232. package/docs/content/BranchName.md +0 -39
  233. package/docs/content/Link.md +0 -29
  234. package/docs/content/TextInput.md +0 -42
package/src/Radio.tsx ADDED
@@ -0,0 +1,76 @@
1
+ import styled from 'styled-components'
2
+ import React, {InputHTMLAttributes, ReactElement} from 'react'
3
+ import sx, {SxProp} from './sx'
4
+
5
+ export type RadioProps = {
6
+ /**
7
+ * A unique value that is never shown to the user.
8
+ * Used during form submission and to identify which radio button in a group is selected
9
+ */
10
+ value: string
11
+ /**
12
+ * Name attribute of the input element. Required for grouping radio inputs
13
+ */
14
+ name: string
15
+ /**
16
+ * Apply inactive visual appearance to the radio button
17
+ */
18
+ disabled?: boolean
19
+ /**
20
+ * Indicates whether the radio button is selected
21
+ */
22
+ checked?: boolean
23
+ /**
24
+ * Forward a ref to the underlying input element
25
+ */
26
+ ref?: React.RefObject<HTMLInputElement>
27
+ /**
28
+ * Indicates whether the radio button must be checked before the form can be submitted
29
+ */
30
+ required?: boolean
31
+ /**
32
+ * Indicates whether the radio button validation state is non-standard
33
+ */
34
+ validationStatus?: 'error' | 'success' // TODO: hoist to Validation typings
35
+ } & InputHTMLAttributes<HTMLInputElement> &
36
+ SxProp
37
+
38
+ const StyledRadio = styled.input`
39
+ cursor: pointer;
40
+
41
+ ${props => props.disabled && `cursor: not-allowed;`}
42
+
43
+ ${sx}
44
+ `
45
+
46
+ /**
47
+ * An accessible, native radio component for selecting one option from a list.
48
+ */
49
+ const Radio = React.forwardRef<HTMLInputElement, RadioProps>(
50
+ (
51
+ {checked, disabled, sx: sxProp, required, validationStatus, value, name, ...rest}: RadioProps,
52
+ ref
53
+ ): ReactElement => {
54
+ return (
55
+ <StyledRadio
56
+ type="radio"
57
+ value={value}
58
+ name={name}
59
+ ref={ref}
60
+ disabled={disabled}
61
+ aria-disabled={disabled ? 'true' : 'false'}
62
+ checked={checked}
63
+ aria-checked={checked ? 'true' : 'false'}
64
+ required={required}
65
+ aria-required={required ? 'true' : 'false'}
66
+ aria-invalid={validationStatus === 'error' ? 'true' : 'false'}
67
+ sx={sxProp}
68
+ {...rest}
69
+ />
70
+ )
71
+ }
72
+ )
73
+
74
+ Radio.displayName = 'Radio'
75
+
76
+ export default Radio
@@ -24,6 +24,7 @@ const ThemeContext = React.createContext<{
24
24
  colorScheme?: string
25
25
  colorMode?: ColorModeWithAuto
26
26
  resolvedColorMode?: ColorMode
27
+ resolvedColorScheme?: string
27
28
  dayScheme?: string
28
29
  nightScheme?: string
29
30
  setColorMode: React.Dispatch<React.SetStateAction<ColorModeWithAuto>>
@@ -52,7 +53,10 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({children, ...props}
52
53
  const systemColorMode = useSystemColorMode()
53
54
  const resolvedColorMode = resolveColorMode(colorMode, systemColorMode)
54
55
  const colorScheme = chooseColorScheme(resolvedColorMode, dayScheme, nightScheme)
55
- const resolvedTheme = React.useMemo(() => applyColorScheme(theme, colorScheme), [theme, colorScheme])
56
+ const {resolvedTheme, resolvedColorScheme} = React.useMemo(
57
+ () => applyColorScheme(theme, colorScheme),
58
+ [theme, colorScheme]
59
+ )
56
60
 
57
61
  // Update state if props change
58
62
  React.useEffect(() => {
@@ -74,6 +78,7 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({children, ...props}
74
78
  colorScheme,
75
79
  colorMode,
76
80
  resolvedColorMode,
81
+ resolvedColorScheme,
77
82
  dayScheme,
78
83
  nightScheme,
79
84
  setColorMode,
@@ -156,9 +161,15 @@ function chooseColorScheme(colorMode: ColorMode, dayScheme: string, nightScheme:
156
161
  }
157
162
  }
158
163
 
159
- function applyColorScheme(theme: Theme, colorScheme: string) {
164
+ function applyColorScheme(
165
+ theme: Theme,
166
+ colorScheme: string
167
+ ): {resolvedTheme: Theme; resolvedColorScheme: string | undefined} {
160
168
  if (!theme.colorSchemes) {
161
- return theme
169
+ return {
170
+ resolvedTheme: theme,
171
+ resolvedColorScheme: undefined
172
+ }
162
173
  }
163
174
 
164
175
  if (!theme.colorSchemes[colorScheme]) {
@@ -167,10 +178,16 @@ function applyColorScheme(theme: Theme, colorScheme: string) {
167
178
 
168
179
  // Apply the first defined color scheme
169
180
  const defaultColorScheme = Object.keys(theme.colorSchemes)[0]
170
- return deepmerge(theme, theme.colorSchemes[defaultColorScheme])
181
+ return {
182
+ resolvedTheme: deepmerge(theme, theme.colorSchemes[defaultColorScheme]),
183
+ resolvedColorScheme: defaultColorScheme
184
+ }
171
185
  }
172
186
 
173
- return deepmerge(theme, theme.colorSchemes[colorScheme])
187
+ return {
188
+ resolvedTheme: deepmerge(theme, theme.colorSchemes[colorScheme]),
189
+ resolvedColorScheme: colorScheme
190
+ }
174
191
  }
175
192
 
176
193
  export default ThemeProvider
@@ -0,0 +1,174 @@
1
+ import React from 'react'
2
+ import {Radio} from '..'
3
+ import {behavesAsComponent, checkExports} from '../utils/testing'
4
+ import {render, cleanup, fireEvent} from '@testing-library/react'
5
+ import {toHaveNoViolations} from 'jest-axe'
6
+ import 'babel-polyfill'
7
+ import '@testing-library/jest-dom'
8
+
9
+ expect.extend(toHaveNoViolations)
10
+
11
+ describe('Radio', () => {
12
+ const defaultProps = {
13
+ name: 'mock',
14
+ value: 'mock value'
15
+ }
16
+
17
+ beforeEach(() => {
18
+ jest.resetAllMocks()
19
+ cleanup()
20
+ })
21
+
22
+ behavesAsComponent({Component: Radio})
23
+
24
+ checkExports('Radio', {
25
+ default: Radio
26
+ })
27
+
28
+ it('renders a valid radio input', () => {
29
+ const {getByRole} = render(<Radio {...defaultProps} />)
30
+
31
+ const radio = getByRole('radio')
32
+
33
+ expect(radio).toBeDefined()
34
+ })
35
+
36
+ it('renders an unchecked radio by default', () => {
37
+ const {getByRole} = render(<Radio {...defaultProps} />)
38
+
39
+ const radio = getByRole('radio') as HTMLInputElement
40
+
41
+ expect(radio.checked).toEqual(false)
42
+ })
43
+
44
+ it('accepts and applies value and name attributes', () => {
45
+ const {getByRole} = render(<Radio {...defaultProps} />)
46
+
47
+ const radio = getByRole('radio') as HTMLInputElement
48
+
49
+ expect(radio).toHaveAttribute('name', defaultProps.name)
50
+ expect(radio).toHaveAttribute('value', defaultProps.value)
51
+ })
52
+
53
+ it('renders an active radio when checked attribute is passed', () => {
54
+ const handleChange = jest.fn()
55
+ const {getByRole} = render(<Radio {...defaultProps} checked onChange={handleChange} />)
56
+
57
+ const radio = getByRole('radio') as HTMLInputElement
58
+
59
+ expect(radio.checked).toEqual(true)
60
+ })
61
+
62
+ it('accepts a change handler that can alter a single radio state', () => {
63
+ const handleChange = jest.fn()
64
+ const {getByRole} = render(<Radio {...defaultProps} onChange={handleChange} />)
65
+
66
+ const radio = getByRole('radio') as HTMLInputElement
67
+
68
+ expect(radio.checked).toEqual(false)
69
+
70
+ fireEvent.click(radio)
71
+ expect(handleChange).toHaveBeenCalled()
72
+ expect(radio.checked).toEqual(true)
73
+ })
74
+
75
+ it('renders correct behavior for multiple radio buttons in a group', () => {
76
+ const handleChange = jest.fn()
77
+ const RadioGroup = () => (
78
+ <form>
79
+ <Radio {...defaultProps} value="radio-one" onChange={handleChange} />
80
+ <Radio {...defaultProps} value="radio-two" onChange={handleChange} />
81
+ </form>
82
+ )
83
+ const {getByDisplayValue} = render(<RadioGroup />)
84
+
85
+ const radioOne = getByDisplayValue('radio-one') as HTMLInputElement
86
+ const radioTwo = getByDisplayValue('radio-two') as HTMLInputElement
87
+
88
+ expect(radioOne).not.toBeChecked()
89
+ expect(radioTwo).not.toBeChecked()
90
+
91
+ fireEvent.click(radioOne)
92
+
93
+ expect(radioOne).toBeChecked()
94
+ expect(radioTwo).not.toBeChecked()
95
+
96
+ fireEvent.click(radioTwo)
97
+
98
+ expect(radioOne).not.toBeChecked()
99
+ expect(radioTwo).toBeChecked()
100
+ })
101
+
102
+ it('renders an inactive radio state correctly', () => {
103
+ const handleChange = jest.fn()
104
+ const {getByRole, rerender} = render(<Radio {...defaultProps} disabled onChange={handleChange} />)
105
+
106
+ const radio = getByRole('radio') as HTMLInputElement
107
+
108
+ expect(radio.disabled).toEqual(true)
109
+ expect(radio).not.toBeChecked()
110
+ expect(radio).toHaveAttribute('aria-disabled', 'true')
111
+
112
+ fireEvent.change(radio)
113
+
114
+ expect(radio.disabled).toEqual(true)
115
+ expect(radio).not.toBeChecked()
116
+ expect(radio).toHaveAttribute('aria-disabled', 'true')
117
+
118
+ // remove disabled attribute and retest
119
+ rerender(<Radio {...defaultProps} onChange={handleChange} />)
120
+
121
+ expect(radio).toHaveAttribute('aria-disabled', 'false')
122
+ })
123
+
124
+ it('renders an uncontrolled component correctly', () => {
125
+ const {getByRole} = render(<Radio {...defaultProps} defaultChecked />)
126
+
127
+ const radio = getByRole('radio') as HTMLInputElement
128
+
129
+ expect(radio.checked).toEqual(true)
130
+ })
131
+
132
+ it('renders an aria-checked attribute correctly', () => {
133
+ const handleChange = jest.fn()
134
+ const {getByRole, rerender} = render(<Radio {...defaultProps} checked={false} onChange={handleChange} />)
135
+
136
+ const radio = getByRole('radio') as HTMLInputElement
137
+
138
+ expect(radio).toHaveAttribute('aria-checked', 'false')
139
+
140
+ rerender(<Radio {...defaultProps} checked={true} onChange={handleChange} />)
141
+
142
+ expect(radio).toHaveAttribute('aria-checked', 'true')
143
+ })
144
+
145
+ it('renders an invalid aria state when validation prop indicates an error', () => {
146
+ const handleChange = jest.fn()
147
+ const {getByRole, rerender} = render(<Radio {...defaultProps} onChange={handleChange} />)
148
+
149
+ const radio = getByRole('radio') as HTMLInputElement
150
+
151
+ expect(radio).toHaveAttribute('aria-invalid', 'false')
152
+
153
+ rerender(<Radio {...defaultProps} onChange={handleChange} validationStatus="success" />)
154
+
155
+ expect(radio).toHaveAttribute('aria-invalid', 'false')
156
+
157
+ rerender(<Radio {...defaultProps} onChange={handleChange} validationStatus="error" />)
158
+
159
+ expect(radio).toHaveAttribute('aria-invalid', 'true')
160
+ })
161
+
162
+ it('renders an aria state indicating the field is required', () => {
163
+ const handleChange = jest.fn()
164
+ const {getByRole, rerender} = render(<Radio {...defaultProps} onChange={handleChange} />)
165
+
166
+ const radio = getByRole('radio') as HTMLInputElement
167
+
168
+ expect(radio).toHaveAttribute('aria-required', 'false')
169
+
170
+ rerender(<Radio {...defaultProps} onChange={handleChange} required />)
171
+
172
+ expect(radio).toHaveAttribute('aria-required', 'true')
173
+ })
174
+ })
@@ -439,3 +439,119 @@ describe('useColorSchemeVar', () => {
439
439
  expect(screen.getByText('Hello')).toHaveStyleRule('background-color', 'blue')
440
440
  })
441
441
  })
442
+
443
+ describe('useTheme().resolvedColorScheme', () => {
444
+ it('is undefined when not in a theme', () => {
445
+ const Component = () => {
446
+ const {resolvedColorScheme} = useTheme()
447
+
448
+ return <Text data-testid="text">{resolvedColorScheme}</Text>
449
+ }
450
+
451
+ render(<Component />)
452
+
453
+ expect(screen.getByTestId('text').textContent).toEqual('')
454
+ })
455
+
456
+ it('is undefined when the theme has no colorScheme object', () => {
457
+ const Component = () => {
458
+ const {resolvedColorScheme} = useTheme()
459
+
460
+ return <Text data-testid="text">{resolvedColorScheme}</Text>
461
+ }
462
+
463
+ render(
464
+ <ThemeProvider theme={{color: 'red'}}>
465
+ <Component />
466
+ </ThemeProvider>
467
+ )
468
+
469
+ expect(screen.getByTestId('text').textContent).toEqual('')
470
+ })
471
+
472
+ it('is the same as the applied colorScheme, when that colorScheme is in the theme', () => {
473
+ const Component = () => {
474
+ const {resolvedColorScheme} = useTheme()
475
+
476
+ return <Text data-testid="text">{resolvedColorScheme}</Text>
477
+ }
478
+
479
+ const schemeToApply = 'dark'
480
+
481
+ render(
482
+ <ThemeProvider theme={exampleTheme} colorMode="day" dayScheme={schemeToApply}>
483
+ <Component />
484
+ </ThemeProvider>
485
+ )
486
+
487
+ expect(exampleTheme.colorSchemes).toHaveProperty(schemeToApply)
488
+ expect(screen.getByTestId('text').textContent).toEqual(schemeToApply)
489
+ })
490
+
491
+ it('is the value of the fallback colorScheme applied when attempting to apply an invalid colorScheme', () => {
492
+ const Component = () => {
493
+ const {resolvedColorScheme} = useTheme()
494
+
495
+ return <Text data-testid="text">{resolvedColorScheme}</Text>
496
+ }
497
+
498
+ const schemeToApply = 'totally-invalid-colorscheme'
499
+ render(
500
+ <ThemeProvider theme={exampleTheme} colorMode="day" dayScheme={schemeToApply}>
501
+ <Component />
502
+ </ThemeProvider>
503
+ )
504
+
505
+ const defaultThemeColorScheme = Object.keys(exampleTheme.colorSchemes)[0]
506
+
507
+ expect(defaultThemeColorScheme).not.toEqual(schemeToApply)
508
+ expect(exampleTheme.colorSchemes).not.toHaveProperty(schemeToApply)
509
+ expect(screen.getByTestId('text').textContent).toEqual('light')
510
+ })
511
+
512
+ describe('nested theme', () => {
513
+ it('is the same as the applied colorScheme, when that colorScheme is in the theme', () => {
514
+ const Component = () => {
515
+ const {resolvedColorScheme} = useTheme()
516
+
517
+ return <Text data-testid="text">{resolvedColorScheme}</Text>
518
+ }
519
+
520
+ const schemeToApply = 'dark'
521
+
522
+ render(
523
+ <ThemeProvider theme={exampleTheme} colorMode="day" dayScheme={schemeToApply}>
524
+ <ThemeProvider>
525
+ <Component />
526
+ </ThemeProvider>
527
+ </ThemeProvider>
528
+ )
529
+
530
+ expect(exampleTheme.colorSchemes).toHaveProperty(schemeToApply)
531
+ expect(screen.getByTestId('text').textContent).toEqual(schemeToApply)
532
+ })
533
+
534
+ it('is the value of the fallback colorScheme applied when attempting to apply an invalid colorScheme', () => {
535
+ const Component = () => {
536
+ const {resolvedColorScheme} = useTheme()
537
+
538
+ return <Text data-testid="text">{resolvedColorScheme}</Text>
539
+ }
540
+
541
+ const schemeToApply = 'totally-invalid-colorscheme'
542
+ render(
543
+ <ThemeProvider theme={exampleTheme} colorMode="day" dayScheme={schemeToApply}>
544
+ <ThemeProvider>
545
+ <Component />
546
+ </ThemeProvider>
547
+ </ThemeProvider>
548
+ )
549
+
550
+ const defaultThemeColorScheme = Object.keys(exampleTheme.colorSchemes)[0]
551
+
552
+ expect(defaultThemeColorScheme).not.toEqual(schemeToApply)
553
+ expect(exampleTheme.colorSchemes).not.toHaveProperty(schemeToApply)
554
+ expect(screen.getByTestId('text').textContent).toEqual('light')
555
+ })
556
+ })
557
+ })
@@ -6,9 +6,11 @@ exports[`BranchName renders consistently 1`] = `
6
6
  padding: 2px 6px;
7
7
  font-size: 12px;
8
8
  font-family: SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;
9
- color: #57606a;
9
+ color: #0969da;
10
10
  background-color: #ddf4ff;
11
11
  border-radius: 6px;
12
+ -webkit-text-decoration: none;
13
+ text-decoration: none;
12
14
  }
13
15
 
14
16
  <a
@@ -0,0 +1,16 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Radio renders consistently 1`] = `
4
+ .c0 {
5
+ cursor: pointer;
6
+ }
7
+
8
+ <input
9
+ aria-checked="false"
10
+ aria-disabled="false"
11
+ aria-invalid="false"
12
+ aria-required="false"
13
+ className="c0"
14
+ type="radio"
15
+ />
16
+ `;
package/src/drafts.ts CHANGED
@@ -8,3 +8,4 @@
8
8
  // Components
9
9
  export * from './ActionList2'
10
10
  export * from './NewButton'
11
+ export * from './ActionMenu2'
@@ -9,3 +9,4 @@ export {useAnchoredPosition} from './useAnchoredPosition'
9
9
  export {useOverlay} from './useOverlay'
10
10
  export type {UseOverlaySettings} from './useOverlay'
11
11
  export {useRenderForcingRef} from './useRenderForcingRef'
12
+ export {useProvidedStateOrCreate} from './useProvidedStateOrCreate'
package/src/index.ts CHANGED
@@ -27,6 +27,8 @@ export {useOverlay} from './hooks/useOverlay'
27
27
  export {useConfirm} from './Dialog/ConfirmationDialog'
28
28
 
29
29
  // Components
30
+ export {default as Radio} from './Radio'
31
+ export type {RadioProps} from './Radio'
30
32
  export {ActionList} from './ActionList'
31
33
  export {ActionMenu} from './ActionMenu'
32
34
  export type {ActionMenuProps} from './ActionMenu'