;
+ /** The max width in which the compact view of the header will switch to display the step number that is
+ * currently in progress. Options include 'xs', 'sm', 'md', 'lg', 'xl', and 'xxl'.
+ */
+ compactWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
+}
+
+interface StepperHeaderComponent {
+ (props: StepperHeaderProps): JSX.Element | null;
+ Step: typeof StepperHeaderStep;
+}
-function StepperHeader({ className, PageCountComponent, compactWidth }) {
+function StepperHeader({
+ className = null,
+ PageCountComponent = PageCount,
+ compactWidth = 'sm',
+}: StepperHeaderProps) {
const { steps, activeKey } = useContext(StepperContext);
const windowDimensions = useWindowSize();
const size = Size[compactWidth] || 'small';
const breakpointWidth = breakpoints[size].maxWidth || Infinity;
- const isCompactView = windowDimensions.width < breakpointWidth;
+ const isCompactView = (windowDimensions.width ?? 0) < breakpointWidth;
if (isCompactView) {
const activeStepIndex = steps.findIndex(step => step.eventKey === activeKey);
const activeStep = steps[activeStepIndex];
+
+ if (!activeStep) {
+ return null;
+ }
+
return (
@@ -67,37 +103,6 @@ function StepperHeader({ className, PageCountComponent, compactWidth }) {
);
}
-StepperHeader.propTypes = {
- /** Specifies class name to append to the base element. */
- className: PropTypes.string,
- /** A component that receives `activeStepIndex` and `totalSteps` props to display them. */
- PageCountComponent: PropTypes.elementType,
- /** The max width in which the compact view of the header will switch to display the step number that is
- * currently in progress. Options include 'xs', 'sm', 'md', 'lg', 'xl', and 'xxl'.
- */
- compactWidth: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl', 'xxl']),
-};
-
-StepperHeader.defaultProps = {
- className: null,
- PageCountComponent: PageCount,
- compactWidth: 'sm',
-};
-
-StepList.propTypes = {
- steps: PropTypes.arrayOf(PropTypes.shape({
- eventKey: PropTypes.string,
- title: PropTypes.string,
- description: PropTypes.string,
- hasError: PropTypes.bool,
- })),
- activeKey: PropTypes.string.isRequired,
-};
-
-StepList.defaultProps = {
- steps: [],
-};
-
-StepperHeader.Step = StepperHeaderStep;
+(StepperHeader as StepperHeaderComponent).Step = StepperHeaderStep;
export default StepperHeader;
diff --git a/src/Stepper/StepperHeaderStep.jsx b/src/Stepper/StepperHeaderStep.tsx
similarity index 85%
rename from src/Stepper/StepperHeaderStep.jsx
rename to src/Stepper/StepperHeaderStep.tsx
index b8b6b384a6e..0547b409a27 100644
--- a/src/Stepper/StepperHeaderStep.jsx
+++ b/src/Stepper/StepperHeaderStep.tsx
@@ -1,5 +1,4 @@
import React, { useContext } from 'react';
-import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Check, Error } from '../../icons';
@@ -7,14 +6,29 @@ import { StepperContext } from './StepperContext';
import Icon from '../Icon';
import Bubble from '../Bubble';
+export interface StepperHeaderStepProps {
+ /** A number that will be display in the icon of the `HeaderStep`. */
+ index: number;
+ /** A text of the `HeaderStep`. */
+ title: string;
+ /** Specifies that this `HeaderStep` is active. */
+ isActive?: boolean;
+ /** Informs user if this `Step` has errors. */
+ hasError?: boolean;
+ /** A text under the `title`. */
+ description?: string;
+ /** Callback fired when element gets clicked. */
+ onClick?: () => void;
+}
+
function StepperHeaderStep({
title,
- isActive,
- hasError,
+ isActive = false,
+ hasError = false,
description,
index,
onClick,
-}) {
+}: StepperHeaderStepProps) {
const { getIsViewed } = useContext(StepperContext);
const isComplete = getIsViewed(index + 1);
const isViewed = getIsViewed(index);
@@ -71,26 +85,4 @@ function StepperHeaderStep({
);
}
-StepperHeaderStep.propTypes = {
- /** A number that will be display in the icon of the `HeaderStep`. */
- index: PropTypes.number.isRequired,
- /** A text of the `HeaderStep`. */
- title: PropTypes.string.isRequired,
- /** Specifies that this `HeaderStep` is active. */
- isActive: PropTypes.bool,
- /** Informs user if this `Step` has errors. */
- hasError: PropTypes.bool,
- /** A text under the `title`. */
- description: PropTypes.string,
- /** Callback fired when element gets clicked. */
- onClick: PropTypes.func,
-};
-
-StepperHeaderStep.defaultProps = {
- isActive: false,
- hasError: false,
- description: undefined,
- onClick: undefined,
-};
-
export default StepperHeaderStep;
diff --git a/src/Stepper/StepperStep.jsx b/src/Stepper/StepperStep.tsx
similarity index 75%
rename from src/Stepper/StepperStep.jsx
rename to src/Stepper/StepperStep.tsx
index e3f00645590..4b492327078 100644
--- a/src/Stepper/StepperStep.jsx
+++ b/src/Stepper/StepperStep.tsx
@@ -1,8 +1,35 @@
import React, { useContext, useEffect } from 'react';
-import PropTypes from 'prop-types';
import classNames from 'classnames';
import { StepperContext } from './StepperContext';
+export interface StepperStepProps {
+ /** Specifies the content of the `Step`. */
+ children: React.ReactNode;
+ /** Specifies class name to append to the base element */
+ className?: string;
+ /**
+ * An identifier of the `Step`. When `activeKey` on the
+ * `Stepper` equals to the `eventKey`, the `Step` will be displayed.
+ */
+ eventKey: string;
+ /** A text of the `Step`. */
+ title: string;
+ /** A text under the `title`. */
+ description?: string;
+ /** Informs user if this `Step` has errors. */
+ hasError?: boolean;
+ /**
+ * Position of the `Step`, only required if adding error state
+ * or conditionally rendering steps.
+ * */
+ index?: number;
+ /**
+ * Click handler for the `Step`. Takes effect only after the `Step` has been visited, making it clickable
+ * and invoking this function on click. Should be used to provide navigation between steps.
+ */
+ onClick?: () => void;
+}
+
export default function StepperStep({
children,
eventKey,
@@ -10,9 +37,9 @@ export default function StepperStep({
title,
index,
description,
- hasError,
+ hasError = false,
onClick,
-}) {
+}: StepperStepProps) {
const { activeKey, registerStep, removeStep } = useContext(StepperContext);
useEffect(() => {
@@ -39,39 +66,3 @@ export default function StepperStep({
);
}
-
-StepperStep.propTypes = {
- /** Specifies the content of the `Step`. */
- children: PropTypes.node.isRequired,
- /** Specifies class name to append to the base element */
- className: PropTypes.string,
- /**
- * An identifier of the `Step`. When `activeKey` on the
- * `Stepper` equals to the `eventKey`, the `Step` will be displayed.
- */
- eventKey: PropTypes.string.isRequired,
- /** A text of the `Step`. */
- title: PropTypes.string.isRequired,
- /** A text under the `title`. */
- description: PropTypes.string,
- /** Informs user if this `Step` has errors. */
- hasError: PropTypes.bool,
- /**
- * Position of the `Step`, only required if adding error state
- * or conditionally rendering steps.
- * */
- index: PropTypes.number,
- /**
- * Click handler for the `Step`. Takes effect only after the `Step` has been visited, making it clickable
- * and invoking this function on click. Should be used to provide navigation between steps.
- */
- onClick: PropTypes.func,
-};
-
-StepperStep.defaultProps = {
- className: undefined,
- description: undefined,
- hasError: false,
- index: undefined,
- onClick: undefined,
-};
diff --git a/src/Stepper/index.js b/src/Stepper/index.ts
similarity index 100%
rename from src/Stepper/index.js
rename to src/Stepper/index.ts
diff --git a/src/Stepper/tests/Stepper.test.jsx b/src/Stepper/tests/Stepper.test.tsx
similarity index 87%
rename from src/Stepper/tests/Stepper.test.jsx
rename to src/Stepper/tests/Stepper.test.tsx
index 103df484ab2..9c3d9ccac3a 100644
--- a/src/Stepper/tests/Stepper.test.jsx
+++ b/src/Stepper/tests/Stepper.test.tsx
@@ -5,17 +5,33 @@ import userEvent from '@testing-library/user-event';
import Stepper from '../Stepper';
import { stepsReducer } from '../StepperContext';
-const mockWindowSize = { width: 1000, height: 1000 };
+interface MockWindowSize {
+ width: number;
+ height: number;
+}
+
+const mockWindowSize: MockWindowSize = { width: 1000, height: 1000 };
jest.mock('../../hooks/useWindowSizeHook', () => () => mockWindowSize);
+interface ExampleProps {
+ activeKey: string;
+ hasStepWithError?: boolean;
+ hasFourthStep?: boolean;
+ handleStepClick?: (key: string) => void;
+ compactWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
+}
+
function Example({
- // eslint-disable-next-line react/prop-types
- activeKey, hasStepWithError, hasFourthStep, handleStepClick,
-}) {
+ activeKey,
+ hasStepWithError,
+ hasFourthStep,
+ handleStepClick,
+ compactWidth = 'sm',
+}: ExampleProps) {
return (
-
+
{
it('renders the activeKey content', () => {
const { rerender } = render(
- ,
+ ,
);
expect(screen.getByText('Welcome content')).toBeInTheDocument();
@@ -71,7 +87,7 @@ describe('Stepper', () => {
expect(screen.queryByText('Review actions content')).not.toBeInTheDocument();
rerender(
- ,
+ ,
);
expect(screen.getByText('Cat content')).toBeInTheDocument();
@@ -86,7 +102,7 @@ describe('Stepper', () => {
it('ignores onClick function if Step has not been visited yet', async () => {
const onStepClick = jest.fn();
render(
- ,
+ ,
);
await userEvent.click(screen.getByText('Cat'));
@@ -96,13 +112,14 @@ describe('Stepper', () => {
it('invokes onClick function if Step has been visited', async () => {
const onStepClick = jest.fn();
render(
- ,
+ ,
);
await userEvent.click(screen.getByText('Welcome'));
expect(onStepClick).toHaveBeenCalledTimes(1);
});
});
+
describe('stepper header compact view', () => {
beforeEach(() => {
mockWindowSize.width = 200;
@@ -223,22 +240,22 @@ describe('stepsReducer', () => {
const step2WithError = { ...step2, hasError: true };
it('registers a steps in order', () => {
- const action1 = { type: 'register', step: step1 };
+ const action1 = { type: 'register' as const, step: step1 };
const stepsState1 = stepsReducer([], action1);
expect(stepsState1).toEqual([step1]);
- const action2 = { type: 'register', step: step2 };
+ const action2 = { type: 'register' as const, step: step2 };
const stepsState2 = stepsReducer(stepsState1, action2);
expect(stepsState2).toEqual([step1, step2]);
});
it('removes a step', () => {
- const action = { type: 'remove', eventKey: step2.eventKey };
+ const action = { type: 'remove' as const, eventKey: step2.eventKey };
const steps = stepsReducer([step1, step2], action);
expect(steps).toEqual([step1]);
});
it('updates a step', () => {
- const action = { type: 'register', step: step2WithError };
+ const action = { type: 'register' as const, step: step2WithError };
const steps = stepsReducer([step1, step2, step3], action);
expect(steps).toEqual([step1, step2WithError, step3]);
});
diff --git a/tsconfig.json b/tsconfig.json
index ec6f180bdf8..e5c87b0f436 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,7 +3,8 @@
"compilerOptions": {
"noImplicitAny": true,
"allowJs": false,
- "outDir": "dist"
+ "outDir": "dist",
+ "jsx": "react-jsx"
},
"include": [
"lib/**/*",