1
1
import React from 'react' ;
2
2
import PropTypes from 'prop-types' ;
3
3
import styled from 'styled-components' ;
4
- import { Link } from 'react-router-dom' ;
5
-
4
+ import { Link , LinkProps } from 'react-router-dom' ;
6
5
import { remSize , prop } from '../theme' ;
7
6
8
7
const kinds = {
9
8
primary : 'primary' ,
10
- secondary : 'secondary'
11
- } ;
9
+ secondary :'secondary'
10
+ // eslint-disable-next-line prettier/prettier
11
+ } as const ;
12
+
13
+ type Kind = keyof typeof kinds
12
14
13
15
const displays = {
14
16
block : 'block' ,
15
17
inline : 'inline'
16
- } ;
18
+ } as const ;
19
+
20
+ type Display = keyof typeof displays
21
+
22
+ type StyledButtonProps = {
23
+ kind : Kind ,
24
+ display : Display
25
+ }
17
26
18
27
// The '&&&' will increase the specificity of the
19
28
// component's CSS so that it overrides the more
20
29
// general global styles
21
- const StyledButton = styled . button `
30
+ const StyledButton = styled . button < StyledButtonProps > `
22
31
&&& {
23
32
font-weight: bold;
24
33
display: ${ ( { display } ) =>
@@ -108,22 +117,76 @@ const StyledInlineButton = styled.button`
108
117
}
109
118
` ;
110
119
120
+ const buttonTypes = {
121
+ button : 'button' ,
122
+ submit : 'submit'
123
+ } as const ;
124
+
125
+ type ButtonType = keyof typeof buttonTypes
126
+ export type ButtonProps = {
127
+ /**
128
+ * The visible part of the button, telling the user what
129
+ * the action is
130
+ */
131
+ children ?: React . ReactNode ,
132
+ /**
133
+ If the button can be activated or not
134
+ */
135
+ disabled ?: boolean ,
136
+ /**
137
+ * The display type of the button—inline or block
138
+ */
139
+ display ?: Display ,
140
+ /**
141
+ * SVG icon to place after child content
142
+ */
143
+ iconAfter ?: React . ReactNode ,
144
+ /**
145
+ * SVG icon to place before child content
146
+ */
147
+ iconBefore ?: React . ReactNode ,
148
+ /**
149
+ * If the button content is only an SVG icon
150
+ */
151
+ iconOnly ?: boolean ,
152
+ /**
153
+ * The kind of button - determines how it appears visually
154
+ */
155
+ kind ?: Kind ,
156
+ /**
157
+ * Specifying an href will use an <a> to link to the URL
158
+ */
159
+ href ?: string | null ,
160
+ /**
161
+ * An ARIA Label used for accessibility
162
+ */
163
+ 'aria-label' ?: string | null ,
164
+ /**
165
+ * Specifying a to URL will use a react-router Link
166
+ */
167
+ to ?: string | null ,
168
+ /**
169
+ * If using a button, then type is defines the type of button
170
+ */
171
+ type ?: ButtonType ,
172
+ }
173
+
111
174
/**
112
175
* A Button performs an primary action
113
176
*/
114
177
const Button = ( {
115
- children,
116
- display,
117
- href,
118
- kind,
119
- iconBefore,
120
- iconAfter,
121
- iconOnly,
122
- 'aria-label' : ariaLabel ,
123
- to,
124
- type,
178
+ children = null ,
179
+ display = displays . block ,
180
+ href = null ,
181
+ kind = kinds . primary ,
182
+ iconBefore = null ,
183
+ iconAfter = null ,
184
+ iconOnly = false ,
185
+ 'aria-label' : ariaLabel = null ,
186
+ to = null ,
187
+ type = buttonTypes . button ,
125
188
...props
126
- } ) => {
189
+ } : ButtonProps ) => {
127
190
const hasChildren = React . Children . count ( children ) > 0 ;
128
191
const content = (
129
192
< >
@@ -132,11 +195,7 @@ const Button = ({
132
195
{ iconAfter }
133
196
</ >
134
197
) ;
135
- let StyledComponent = StyledButton ;
136
-
137
- if ( iconOnly ) {
138
- StyledComponent = StyledInlineButton ;
139
- }
198
+ const StyledComponent : React . ElementType < any > = iconOnly ? StyledInlineButton : StyledButton ;
140
199
141
200
if ( href ) {
142
201
return (
@@ -181,69 +240,7 @@ const Button = ({
181
240
) ;
182
241
} ;
183
242
184
- Button . defaultProps = {
185
- children : null ,
186
- disabled : false ,
187
- display : displays . block ,
188
- iconAfter : null ,
189
- iconBefore : null ,
190
- iconOnly : false ,
191
- kind : kinds . primary ,
192
- href : null ,
193
- 'aria-label' : null ,
194
- to : null ,
195
- type : 'button'
196
- } ;
197
-
198
243
Button . kinds = kinds ;
199
244
Button . displays = displays ;
200
245
201
- Button . propTypes = {
202
- /**
203
- * The visible part of the button, telling the user what
204
- * the action is
205
- */
206
- children : PropTypes . oneOfType ( [ PropTypes . element , PropTypes . string ] ) ,
207
- /**
208
- If the button can be activated or not
209
- */
210
- disabled : PropTypes . bool ,
211
- /**
212
- * The display type of the button—inline or block
213
- */
214
- display : PropTypes . oneOf ( Object . values ( displays ) ) ,
215
- /**
216
- * SVG icon to place after child content
217
- */
218
- iconAfter : PropTypes . element ,
219
- /**
220
- * SVG icon to place before child content
221
- */
222
- iconBefore : PropTypes . element ,
223
- /**
224
- * If the button content is only an SVG icon
225
- */
226
- iconOnly : PropTypes . bool ,
227
- /**
228
- * The kind of button - determines how it appears visually
229
- */
230
- kind : PropTypes . oneOf ( Object . values ( kinds ) ) ,
231
- /**
232
- * Specifying an href will use an <a> to link to the URL
233
- */
234
- href : PropTypes . string ,
235
- /**
236
- * An ARIA Label used for accessibility
237
- */
238
- 'aria-label' : PropTypes . string ,
239
- /**
240
- * Specifying a to URL will use a react-router Link
241
- */
242
- to : PropTypes . string ,
243
- /**
244
- * If using a button, then type is defines the type of button
245
- */
246
- type : PropTypes . oneOf ( [ 'button' , 'submit' ] )
247
- } ;
248
-
249
246
export default Button ;
0 commit comments