diff --git a/ui/components/component-library/button-base/__snapshots__/button-base.test.js.snap b/ui/components/component-library/button-base/__snapshots__/button-base.test.tsx.snap
similarity index 100%
rename from ui/components/component-library/button-base/__snapshots__/button-base.test.js.snap
rename to ui/components/component-library/button-base/__snapshots__/button-base.test.tsx.snap
diff --git a/ui/components/component-library/button-base/button-base.constants.js b/ui/components/component-library/button-base/button-base.constants.js
deleted file mode 100644
index 73387a710ae1..000000000000
--- a/ui/components/component-library/button-base/button-base.constants.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import { Size } from '../../../helpers/constants/design-system';
-
-export const BUTTON_BASE_SIZES = {
- SM: Size.SM,
- MD: Size.MD,
- LG: Size.LG,
-};
diff --git a/ui/components/component-library/button-base/button-base.js b/ui/components/component-library/button-base/button-base.js
deleted file mode 100644
index ec32327b00a6..000000000000
--- a/ui/components/component-library/button-base/button-base.js
+++ /dev/null
@@ -1,203 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import classnames from 'classnames';
-
-import Box from '../../ui/box';
-import { IconName, Icon, IconSize } from '../icon';
-import { Text } from '..';
-
-import {
- AlignItems,
- Display,
- JustifyContent,
- TextColor,
- TextVariant,
- BorderRadius,
- BackgroundColor,
- IconColor,
-} from '../../../helpers/constants/design-system';
-import { BUTTON_BASE_SIZES } from './button-base.constants';
-
-export const ButtonBase = ({
- as = 'button',
- block,
- children,
- className,
- href,
- ellipsis = false,
- externalLink,
- size = BUTTON_BASE_SIZES.MD,
- startIconName,
- startIconProps,
- endIconName,
- endIconProps,
- loading,
- disabled,
- iconLoadingProps,
- textProps,
- color = TextColor.textDefault,
- ...props
-}) => {
- const Tag = href ? 'a' : as;
- if (Tag === 'a' && externalLink) {
- props.target = '_blank';
- props.rel = 'noopener noreferrer';
- }
- return (
-
- {startIconName && (
-
- )}
- {/*
- * If children is a string and doesn't need truncation or loading
- * prevent html bloat by rendering just the string
- * otherwise render with wrapper to allow truncation or loading
- */}
- {typeof children === 'string' && !ellipsis && !loading ? (
- children
- ) : (
-
- {children}
-
- )}
- {endIconName && (
-
- )}
- {loading && (
-
- )}
-
- );
-};
-
-ButtonBase.propTypes = {
- /**
- * The polymorphic `as` prop allows you to change the root HTML element of the Button component between `button` and `a` tag
- */
- as: PropTypes.string,
- /**
- * Boolean prop to quickly activate box prop display block
- */
- block: PropTypes.bool,
- /**
- * Additional props to pass to the Text component that wraps the button children
- */
- buttonTextProps: PropTypes.object,
- /**
- * The children to be rendered inside the ButtonBase
- */
- children: PropTypes.node,
- /**
- * An additional className to apply to the ButtonBase.
- */
- className: PropTypes.string,
- /**
- * Boolean to disable button
- */
- disabled: PropTypes.bool,
- /**
- * When an `href` prop is passed, ButtonBase will automatically change the root element to be an `a` (anchor) tag
- */
- href: PropTypes.string,
- /**
- * Used for long strings that can be cut off...
- */
- ellipsis: PropTypes.bool,
- /**
- * Boolean indicating if the link targets external content, it will cause the link to open in a new tab
- */
- externalLink: PropTypes.bool,
- /**
- * Add icon to start (left side) of button text passing icon name
- * The name of the icon to display. Should be one of IconName
- */
- startIconName: PropTypes.oneOf(Object.values(IconName)),
- /**
- * iconProps accepts all the props from Icon
- */
- startIconProps: PropTypes.object,
- /**
- * Add icon to end (right side) of button text passing icon name
- * The name of the icon to display. Should be one of IconName
- */
- endIconName: PropTypes.oneOf(Object.values(IconName)),
- /**
- * iconProps accepts all the props from Icon
- */
- endIconProps: PropTypes.object,
- /**
- * iconLoadingProps accepts all the props from Icon
- */
- iconLoadingProps: PropTypes.object,
- /**
- * Boolean to show loading spinner in button
- */
- loading: PropTypes.bool,
- /**
- * The size of the ButtonBase.
- * Possible values could be 'Size.SM'(32px), 'Size.MD'(40px), 'Size.LG'(48px),
- */
- size: PropTypes.oneOfType([
- PropTypes.shape(BUTTON_BASE_SIZES),
- PropTypes.string,
- ]),
- /**
- * textProps accepts all the props from Icon
- */
- textProps: PropTypes.object,
- /**
- * ButtonBase accepts all the props from Box
- */
- ...Box.propTypes,
-};
diff --git a/ui/components/component-library/button-base/button-base.stories.js b/ui/components/component-library/button-base/button-base.stories.tsx
similarity index 65%
rename from ui/components/component-library/button-base/button-base.stories.js
rename to ui/components/component-library/button-base/button-base.stories.tsx
index 6be335435142..8beed57f7b99 100644
--- a/ui/components/component-library/button-base/button-base.stories.js
+++ b/ui/components/component-library/button-base/button-base.stories.tsx
@@ -1,15 +1,16 @@
import React from 'react';
+import { StoryFn, Meta } from '@storybook/react';
import {
AlignItems,
- Color,
- DISPLAY,
- FLEX_DIRECTION,
- Size,
+ BackgroundColor,
+ Display,
+ FlexDirection,
+ TextColor,
} from '../../../helpers/constants/design-system';
import Box from '../../ui/box/box';
-import { TextDirection, IconName } from '..';
+import { TextDirection, IconName, ValidTag } from '..';
-import { BUTTON_BASE_SIZES } from './button-base.constants';
+import { ButtonBaseSize } from './button-base.types';
import { ButtonBase } from './button-base';
import README from './README.mdx';
@@ -70,7 +71,7 @@ export default {
},
size: {
control: 'select',
- options: Object.values(BUTTON_BASE_SIZES),
+ options: Object.values(ButtonBaseSize),
},
marginTop: {
options: marginSizeControlOptions,
@@ -96,27 +97,29 @@ export default {
args: {
children: 'Button Base',
},
-};
+} as Meta;
-export const DefaultStory = (args) => ;
+export const DefaultStory: StoryFn = (args) => (
+
+);
DefaultStory.storyName = 'Default';
-export const SizeStory = (args) => (
+export const SizeStory: StoryFn = (args) => (
<>
-
+
Button SM
-
+
Button MD
-
+
Button LG
@@ -125,7 +128,7 @@ export const SizeStory = (args) => (
SizeStory.storyName = 'Size';
-export const Block = (args) => (
+export const Block: StoryFn = (args) => (
<>
Default Button
@@ -136,22 +139,24 @@ export const Block = (args) => (
>
);
-export const As = (args) => (
-
+export const As: StoryFn = (args) => (
+
Button Element
-
+
Anchor Element
);
-export const Href = (args) => Anchor Element;
+export const Href: StoryFn = (args) => (
+ Anchor Element
+);
Href.args = {
href: '/metamask',
};
-export const ExternalLink = (args) => (
+export const ExternalLink: StoryFn = (args) => (
Anchor element with external link
);
@@ -160,7 +165,7 @@ ExternalLink.args = {
externalLink: true,
};
-export const Disabled = (args) => (
+export const Disabled: StoryFn = (args) => (
Disabled Button
);
@@ -168,7 +173,7 @@ Disabled.args = {
disabled: true,
};
-export const Loading = (args) => (
+export const Loading: StoryFn = (args) => (
Loading Button
);
@@ -176,20 +181,20 @@ Loading.args = {
loading: true,
};
-export const StartIconName = (args) => (
+export const StartIconName: StoryFn = (args) => (
Button
);
-export const EndIconName = (args) => (
+export const EndIconName: StoryFn = (args) => (
Button
);
-export const Rtl = (args) => (
-
+export const Rtl: StoryFn = (args) => (
+
(
);
-export const Ellipsis = (args) => (
-
+export const Ellipsis: StoryFn = (args) => (
+
Example without ellipsis
-
+
Example with ellipsis
diff --git a/ui/components/component-library/button-base/button-base.test.js b/ui/components/component-library/button-base/button-base.test.tsx
similarity index 77%
rename from ui/components/component-library/button-base/button-base.test.js
rename to ui/components/component-library/button-base/button-base.test.tsx
index 6e3a3bf43f8b..1b2d1c03dcaa 100644
--- a/ui/components/component-library/button-base/button-base.test.js
+++ b/ui/components/component-library/button-base/button-base.test.tsx
@@ -1,8 +1,8 @@
/* eslint-disable jest/require-top-level-describe */
import { render } from '@testing-library/react';
import React from 'react';
-import { IconName } from '..';
-import { BUTTON_BASE_SIZES } from './button-base.constants';
+import { IconName, ValidTag } from '..';
+import { ButtonBaseSize } from './button-base.types';
import { ButtonBase } from './button-base';
describe('ButtonBase', () => {
@@ -18,7 +18,7 @@ describe('ButtonBase', () => {
it('should render anchor element correctly', () => {
const { getByTestId, container } = render(
- ,
+ ,
);
expect(getByTestId('button-base')).toHaveClass('mm-button-base');
const anchor = container.getElementsByTagName('a').length;
@@ -51,17 +51,8 @@ describe('ButtonBase', () => {
expect(getByTestId('button-base')).toHaveAttribute(
'href',
'https://www.test.com/',
- 'target',
- '_blank',
- 'rel',
- 'noopener noreferrer',
- );
- expect(getByTestId('button-base')).toHaveAttribute(
- 'target',
- '_blank',
- 'rel',
- 'noopener noreferrer',
);
+ expect(getByTestId('button-base')).toHaveAttribute('target', '_blank');
expect(getByTestId('button-base')).toHaveAttribute(
'rel',
'noopener noreferrer',
@@ -79,28 +70,19 @@ describe('ButtonBase', () => {
it('should render with different size classes', () => {
const { getByTestId } = render(
<>
-
-
-
+
+
+
>,
);
- expect(getByTestId(BUTTON_BASE_SIZES.SM)).toHaveClass(
- `mm-button-base--size-${BUTTON_BASE_SIZES.SM}`,
+ expect(getByTestId(ButtonBaseSize.Sm)).toHaveClass(
+ `mm-button-base--size-${ButtonBaseSize.Sm}`,
);
- expect(getByTestId(BUTTON_BASE_SIZES.MD)).toHaveClass(
- `mm-button-base--size-${BUTTON_BASE_SIZES.MD}`,
+ expect(getByTestId(ButtonBaseSize.Md)).toHaveClass(
+ `mm-button-base--size-${ButtonBaseSize.Md}`,
);
- expect(getByTestId(BUTTON_BASE_SIZES.LG)).toHaveClass(
- `mm-button-base--size-${BUTTON_BASE_SIZES.LG}`,
+ expect(getByTestId(ButtonBaseSize.Lg)).toHaveClass(
+ `mm-button-base--size-${ButtonBaseSize.Lg}`,
);
});
diff --git a/ui/components/component-library/button-base/button-base.tsx b/ui/components/component-library/button-base/button-base.tsx
new file mode 100644
index 000000000000..8c1282a69111
--- /dev/null
+++ b/ui/components/component-library/button-base/button-base.tsx
@@ -0,0 +1,130 @@
+import React from 'react';
+import classnames from 'classnames';
+import { IconName, Icon, IconSize, Text } from '..';
+import {
+ AlignItems,
+ Display,
+ JustifyContent,
+ TextColor,
+ TextVariant,
+ BorderRadius,
+ BackgroundColor,
+ IconColor,
+} from '../../../helpers/constants/design-system';
+import type { PolymorphicRef } from '../box';
+import {
+ ButtonBaseProps,
+ ButtonBaseSize,
+ ButtonBaseComponent,
+} from './button-base.types';
+
+export const ButtonBase: ButtonBaseComponent = React.forwardRef(
+ (
+ {
+ as = 'button',
+ block,
+ children,
+ className = '',
+ href,
+ ellipsis = false,
+ externalLink,
+ size = ButtonBaseSize.Md,
+ startIconName,
+ startIconProps,
+ endIconName,
+ endIconProps,
+ loading,
+ disabled,
+ iconLoadingProps,
+ textProps,
+ color = TextColor.textDefault,
+ iconColor = IconColor.iconDefault,
+ ...props
+ }: ButtonBaseProps,
+ ref?: PolymorphicRef,
+ ) => {
+ const Tag = href ? 'a' : as;
+ if (Tag === 'a' && externalLink) {
+ props.target = '_blank';
+ props.rel = 'noopener noreferrer';
+ }
+
+ return (
+
+ {startIconName && (
+
+ )}
+ {/*
+ * If children is a string and doesn't need truncation or loading
+ * prevent html bloat by rendering just the string
+ * otherwise render with wrapper to allow truncation or loading
+ */}
+ {typeof children === 'string' && !ellipsis && !loading ? (
+ children
+ ) : (
+
+ {children}
+
+ )}
+ {endIconName && (
+
+ )}
+ {loading && (
+
+ )}
+
+ );
+ },
+);
diff --git a/ui/components/component-library/button-base/button-base.types.ts b/ui/components/component-library/button-base/button-base.types.ts
new file mode 100644
index 000000000000..5ca12677b2fc
--- /dev/null
+++ b/ui/components/component-library/button-base/button-base.types.ts
@@ -0,0 +1,105 @@
+import { ReactNode } from 'react';
+import type {
+ StyleUtilityProps,
+ PolymorphicComponentPropWithRef,
+} from '../box';
+import { IconColor } from '../../../helpers/constants/design-system';
+import { TextDirection, TextProps } from '../text';
+import { IconName, IconProps } from '../icon';
+
+export enum ButtonBaseSize {
+ Sm = 'sm',
+ Md = 'md',
+ Lg = 'lg',
+}
+
+export interface ButtonBaseStyleUtilityProps extends StyleUtilityProps {
+ /**
+ * The polymorphic `as` prop allows you to change the root HTML element of the Button component between `button` and `a` tag
+ *
+ */
+ as?: 'button' | 'a';
+ /**
+ * Boolean prop to quickly activate box prop display block
+ */
+ block?: boolean;
+ /**
+ * The children to be rendered inside the ButtonBase
+ */
+ children?: ReactNode;
+ /**
+ * Boolean to disable button
+ */
+ disabled?: boolean;
+ /**
+ * When an `href` prop is passed, ButtonBase will automatically change the root element to be an `a` (anchor) tag
+ */
+ href?: string;
+ /**
+ * Used for long strings that can be cut off...
+ */
+ ellipsis?: boolean;
+ /**
+ * Boolean indicating if the link targets external content, it will cause the link to open in a new tab
+ */
+ externalLink?: boolean;
+ /**
+ * Add icon to start (left side) of button text passing icon name
+ * The name of the icon to display. Should be one of IconName
+ */
+ startIconName?: IconName;
+ /**
+ * iconProps accepts all the props from Icon
+ */
+ startIconProps?: IconProps;
+ /**
+ * Add icon to end (right side) of button text passing icon name
+ * The name of the icon to display. Should be one of IconName
+ */
+ endIconName?: IconName;
+ /**
+ * iconProps accepts all the props from Icon
+ */
+ endIconProps?: IconProps;
+ /**
+ * iconLoadingProps accepts all the props from Icon
+ */
+ iconLoadingProps?: IconProps;
+ /**
+ * Boolean to show loading spinner in button
+ */
+ loading?: boolean;
+ /**
+ * The size of the ButtonBase.
+ * Possible values could be 'Size.SM'(32px), 'Size.MD'(40px), 'Size.LG'(48px),
+ */
+ size?: ButtonBaseSize;
+ /**
+ * textProps are additional props to pass to the Text component that wraps the button children
+ */
+ textProps?: TextProps<'span'>;
+ /**
+ * Specifies where to display the linked URL.
+ */
+ target?: string;
+ /**
+ * Specifies the relationship between the current document and
+ * the linked URL.
+ */
+ rel?: string;
+ /**
+ * Sets the color of the button icon.
+ */
+ iconColor?: IconColor;
+ /**
+ * Direction of the text content within the button ("ltr" or "rtl").
+ */
+ textDirection?: TextDirection;
+}
+
+export type ButtonBaseProps =
+ PolymorphicComponentPropWithRef;
+
+export type ButtonBaseComponent = (
+ props: ButtonBaseProps,
+) => React.ReactElement | null;
diff --git a/ui/components/component-library/button-base/index.js b/ui/components/component-library/button-base/index.js
deleted file mode 100644
index 3d030b89b39c..000000000000
--- a/ui/components/component-library/button-base/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-export { ButtonBase } from './button-base';
-export { BUTTON_BASE_SIZES } from './button-base.constants';
diff --git a/ui/components/component-library/button-base/index.ts b/ui/components/component-library/button-base/index.ts
new file mode 100644
index 000000000000..44d2ed54d1ff
--- /dev/null
+++ b/ui/components/component-library/button-base/index.ts
@@ -0,0 +1,3 @@
+export { ButtonBase } from './button-base';
+export { ButtonBaseSize } from './button-base.types';
+export type { ButtonBaseProps } from './button-base.types';
diff --git a/ui/components/component-library/index.js b/ui/components/component-library/index.js
index 564643063982..588707bdab46 100644
--- a/ui/components/component-library/index.js
+++ b/ui/components/component-library/index.js
@@ -16,7 +16,7 @@ export {
} from './badge-wrapper';
export { Box } from './box';
export { Button, BUTTON_VARIANT, BUTTON_SIZES } from './button';
-export { ButtonBase, BUTTON_BASE_SIZES } from './button-base';
+export { ButtonBase, ButtonBaseSize } from './button-base';
export { ButtonIcon, ButtonIconSize } from './button-icon';
export { ButtonLink, BUTTON_LINK_SIZES } from './button-link';
export { ButtonPrimary, BUTTON_PRIMARY_SIZES } from './button-primary';
diff --git a/ui/components/component-library/text/text.types.ts b/ui/components/component-library/text/text.types.ts
index 2afc5a71436b..ca221003fed3 100644
--- a/ui/components/component-library/text/text.types.ts
+++ b/ui/components/component-library/text/text.types.ts
@@ -3,7 +3,6 @@ import {
FontWeight,
FontStyle,
TextVariant,
- TextAlign,
TextTransform,
OverflowWrap,
} from '../../../helpers/constants/design-system';
@@ -52,6 +51,8 @@ export enum ValidTag {
Label = 'label',
Input = 'input',
Header = 'header',
+ A = 'a',
+ Button = 'button',
}
export type ValidTagType =
@@ -72,7 +73,9 @@ export type ValidTagType =
| 'ul'
| 'label'
| 'input'
- | 'header';
+ | 'header'
+ | 'a'
+ | 'button';
export interface TextStyleUtilityProps extends StyleUtilityProps {
/**
@@ -117,11 +120,6 @@ export interface TextStyleUtilityProps extends StyleUtilityProps {
* ./ui/helpers/constants/design-system.js
*/
textTransform?: TextTransform;
- /**
- * The text-align of the Text component. Should use the TextAlign enum from
- * ./ui/helpers/constants/design-system.js
- */
- textAlign?: TextAlign;
/**
* Change the dir (direction) global attribute of text to support the direction a language is written
* Possible values: `LEFT_TO_RIGHT` (default), `RIGHT_TO_LEFT`, `AUTO` (user agent decides)
diff --git a/ui/helpers/constants/design-system.ts b/ui/helpers/constants/design-system.ts
index 89530360a6f1..602a79e2d9c7 100644
--- a/ui/helpers/constants/design-system.ts
+++ b/ui/helpers/constants/design-system.ts
@@ -164,6 +164,7 @@ export enum IconColor {
lineaMainnetInverse = 'linea-mainnet-inverse',
goerliInverse = 'goerli-inverse',
sepoliaInverse = 'sepolia-inverse',
+ transparent = 'transparent',
}
export enum TypographyVariant {