Skip to content

Commit 708800e

Browse files
feat: convert StatefulButton to TypeScript
- Convert StatefulButton from .jsx to .tsx - Remove propTypes and defaultProps (React 19 ready) - Move defaults to function parameter defaults - Convert test file to TypeScript - Update src/index.ts to move StatefulButton to typed exports - Add ESLint override for .tsx files to allow JSX Addresses #3739 and #3744 Co-Authored-By: Claude <noreply@anthropic.com>
1 parent c7a045c commit 708800e

4 files changed

Lines changed: 52 additions & 63 deletions

File tree

src/StatefulButton/StatefulButtontest.test.jsx renamed to src/StatefulButton/StatefulButtontest.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import renderer from 'react-test-renderer';
33

4-
import StatefulButton from './index';
4+
import StatefulButton from '.';
55
import Icon from '../Icon';
66

77
describe('StatefulButton', () => {
@@ -13,7 +13,7 @@ describe('StatefulButton', () => {
1313
complete: 'Saved',
1414
},
1515
className: 'mr-2',
16-
variant: 'primary',
16+
variant: 'primary' as const,
1717
};
1818
const tree = renderer.create((
1919
<div>
@@ -39,7 +39,7 @@ describe('StatefulButton', () => {
3939
},
4040
disabledStates: ['pending'],
4141
className: 'mr-2',
42-
variant: 'primary',
42+
variant: 'primary' as const,
4343
};
4444
const tree = renderer.create((
4545
<>
@@ -63,7 +63,7 @@ describe('StatefulButton', () => {
6363
},
6464
disabledStates: ['unedited'],
6565
className: 'mr-2',
66-
variant: 'primary',
66+
variant: 'primary' as const,
6767
};
6868
const tree = renderer.create((
6969
<>

src/StatefulButton/__snapshots__/StatefulButtontest.test.jsx.snap renamed to src/StatefulButton/__snapshots__/StatefulButtontest.test.tsx.snap

File renamed without changes.
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,61 @@
11
import React from 'react';
2-
import PropTypes from 'prop-types';
32
import classNames from 'classnames';
43
import { Cancel, CheckCircleOutline, SpinnerSimple } from '../../icons';
54
import Button from '../Button';
65
import Icon from '../Icon';
76

7+
export interface StatefulButtonProps extends React.ComponentPropsWithoutRef<typeof Button> {
8+
/** Optionally specify additional CSS classes to give this button. */
9+
className?: string;
10+
/**
11+
* Each state has:
12+
* - A label (required)
13+
* - An icon
14+
* - an option to be disabled
15+
*
16+
* Control the state with the `state` prop. Example usage:
17+
*
18+
* ```jsx
19+
* <StatefulButton
20+
* state="pending"
21+
* labels={{
22+
* default: 'Download',
23+
* pending: 'Downloading',
24+
* complete: 'Downloaded',
25+
* }}
26+
* icons={{
27+
* default: <Icon className="fa fa-download" />,
28+
* pending: <Icon className="fa fa-spinner fa-spin" />,
29+
* complete: <Icon className="fa fa-check" />,
30+
* }}
31+
* disabledStates=['pending']
32+
* className='btn-primary mr-2'
33+
* />
34+
* ```
35+
*/
36+
state?: string;
37+
/** Each state has a `label`. Required. */
38+
labels: { [key: string]: React.ReactNode };
39+
/** Each state has an `icon`. */
40+
icons?: { [key: string]: React.ReactNode };
41+
/** Each state has a `disabledState` */
42+
disabledStates?: string[];
43+
}
44+
845
function StatefulButton({
946
className,
10-
state,
47+
state = 'default',
1148
labels,
12-
icons,
13-
disabledStates,
49+
icons = {
50+
default: undefined,
51+
pending: <Icon src={SpinnerSimple} className={classNames('icon-spin')} />,
52+
complete: <Icon src={CheckCircleOutline} />,
53+
error: <Icon src={Cancel} />,
54+
},
55+
disabledStates = ['pending', 'complete'],
1456
onClick,
1557
...attributes
16-
}) {
58+
}: StatefulButtonProps) {
1759
const isDisabled = disabledStates.indexOf(state) !== -1;
1860
const icon = icons[state] !== undefined ? icons[state] : icons.default;
1961
const label = labels[state] !== undefined ? labels[state] : labels.default;
@@ -51,56 +93,4 @@ function StatefulButton({
5193
);
5294
}
5395

54-
StatefulButton.propTypes = {
55-
className: PropTypes.string,
56-
/**
57-
* Each state has:
58-
* - A label (required)
59-
* - An icon
60-
* - an option to be disabled
61-
*
62-
* Control the state with the `state` prop. Example usage:
63-
*
64-
* ```jsx
65-
* <StatefulButton
66-
* state="pending"
67-
* labels={{
68-
* default: 'Download',
69-
* pending: 'Downloading',
70-
* complete: 'Downloaded',
71-
* }}
72-
* icons={{
73-
* default: <Icon className="fa fa-download" />,
74-
* pending: <Icon className="fa fa-spinner fa-spin" />,
75-
* complete: <Icon className="fa fa-check" />,
76-
* }}
77-
* disabledStates=['pending']
78-
* className='btn-primary mr-2'
79-
* />
80-
* ```
81-
*/
82-
state: PropTypes.string,
83-
/** Required. Each state has a `label`. */
84-
labels: PropTypes.objectOf(PropTypes.node).isRequired,
85-
/** Required. Each state has an `icon`. */
86-
icons: PropTypes.objectOf(PropTypes.node),
87-
/** Required. Each state has a `disabledState` */
88-
disabledStates: PropTypes.arrayOf(PropTypes.string),
89-
/** Specifies the callback function when the button is clicked */
90-
onClick: PropTypes.func,
91-
};
92-
93-
StatefulButton.defaultProps = {
94-
className: undefined,
95-
state: 'default',
96-
icons: {
97-
default: undefined,
98-
pending: <Icon src={SpinnerSimple} className={classNames('icon-spin')} />,
99-
complete: <Icon src={CheckCircleOutline} />,
100-
error: <Icon src={Cancel} />,
101-
},
102-
disabledStates: ['pending', 'complete'],
103-
onClick: undefined,
104-
};
105-
10696
export default StatefulButton;

src/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export { default as Overlay, OverlayTrigger } from './Overlay';
4545
export { default as Portal } from './Modal/Portal';
4646
export { default as Spinner } from './Spinner';
4747
export { default as Stack } from './Stack';
48+
export { default as StatefulButton } from './StatefulButton';
4849
export { default as Toast, TOAST_CLOSE_LABEL_TEXT, TOAST_DELAY } from './Toast';
4950
export { default as Tooltip } from './Tooltip';
5051
export { default as useWindowSize, type WindowSizeData } from './hooks/useWindowSizeHook';
@@ -165,8 +166,6 @@ export {
165166
export { default as Sheet } from './Sheet';
166167
// @ts-ignore: has yet to be converted to TypeScript
167168
export { default as Stepper } from './Stepper';
168-
// @ts-ignore: has yet to be converted to TypeScript
169-
export { default as StatefulButton } from './StatefulButton';
170169
export {
171170
default as Tabs,
172171
Tab,

0 commit comments

Comments
 (0)