diff --git a/docs/pages/components/button.mdx b/docs/pages/components/button.mdx
index 734f8c1485..566c1f9630 100644
--- a/docs/pages/components/button.mdx
+++ b/docs/pages/components/button.mdx
@@ -28,6 +28,7 @@ Built with [Ariakit](https://ariakit.org/components/button) for a better accessi
+
```
### States
diff --git a/packages/Button/package.json b/packages/Button/package.json
index e1df356117..7eed8a676d 100644
--- a/packages/Button/package.json
+++ b/packages/Button/package.json
@@ -51,7 +51,8 @@
"@welcome-ui/box": "^5.16.2",
"@welcome-ui/loader": "^5.17.0",
"@welcome-ui/system": "^5.16.2",
- "@welcome-ui/utils": "^5.16.2"
+ "@welcome-ui/utils": "^5.16.2",
+ "@welcome-ui/core": "^5.16.2"
},
"devDependencies": {
"@welcome-ui/icons": "^5.16.2",
diff --git a/packages/Button/src/index.tsx b/packages/Button/src/index.tsx
index a2788085a2..54c77c9af2 100644
--- a/packages/Button/src/index.tsx
+++ b/packages/Button/src/index.tsx
@@ -2,10 +2,12 @@ import React from 'react'
import { CreateWuiProps, forwardRef } from '@welcome-ui/system'
import { Box } from '@welcome-ui/box'
import { Loader } from '@welcome-ui/loader'
+import type { WuiTheme } from '@welcome-ui/core'
import * as S from './styles'
-export type Shape = 'circle' | 'square'
+export type ShapeValues = 'circle' | 'square' | 'default'
+export type Shape = ShapeValues | Record
export type Size = 'xxs' | 'xs' | 'sm' | 'md' | 'lg'
export type Variant =
| 'primary'
diff --git a/packages/Button/src/styles.ts b/packages/Button/src/styles.ts
index 2bdb247d58..2d4b452c32 100644
--- a/packages/Button/src/styles.ts
+++ b/packages/Button/src/styles.ts
@@ -2,20 +2,57 @@ import styled, { css, system, th } from '@xstyled/styled-components'
import { Button as AriakitButton } from '@ariakit/react'
import { shouldForwardProp } from '@welcome-ui/system'
import { hideFocusRingsDataAttribute } from '@welcome-ui/utils'
+import type { WuiTheme } from '@welcome-ui/core'
import { ButtonOptions } from './index'
-const shapeStyles = (size: ButtonOptions['size'], shape: ButtonOptions['shape'] = 'square') => css`
- width: ${th(`buttons.sizes.${size}.height`)};
- padding: 0;
- ${shape === 'circle' &&
- css`
- border-radius: ${th(`buttons.sizes.${size}.height`)};
- `};
-`
+const shapeStyles = (
+ size: ButtonOptions['size'],
+ shape: ButtonOptions['shape'],
+ theme: WuiTheme
+) => {
+ if (!shape) return
+ const styles = {
+ circle: css`
+ width: ${theme.buttons.sizes[size].height};
+ padding: 0;
+ border-radius: ${theme.buttons.sizes[size].height};
+ `,
+ // square and circle styles must override each other for mediaqueries to be able
+ // to work as expected
+ square: css`
+ width: ${theme.buttons.sizes[size].height};
+ padding: 0;
+ border-radius: 0;
+ `,
+ default: css`
+ width: auto;
+ padding: ${theme.buttons.sizes[size].padding};
+ border-radius: 0;
+ `,
+ }
+
+ if (typeof shape === 'string') {
+ return styles[shape as keyof typeof styles]
+ }
+
+ return Object.keys(shape).map((breakpoint: keyof WuiTheme['screens']) => {
+ const screenWidth = theme.screens[breakpoint]
+ if (breakpoint === '_') {
+ return styles[shape[breakpoint] as keyof typeof styles]
+ }
+ if (screenWidth) {
+ return css`
+ @media (width >= ${screenWidth}px) {
+ ${styles[shape[breakpoint] as keyof typeof styles]};
+ }
+ `
+ }
+ })
+}
export const Button = styled(AriakitButton).withConfig({ shouldForwardProp })(
- ({ disabled, shape, size = 'md', variant }) => css`
+ ({ disabled, shape, size = 'md', theme, variant }: ButtonOptions & { theme: WuiTheme }) => css`
${th(`buttons.${variant}`)};
position: relative;
display: inline-flex;
@@ -33,7 +70,7 @@ export const Button = styled(AriakitButton).withConfig({ shouldForwardProp }) svg.wui-icon,
@@ -56,21 +93,23 @@ export const Button = styled(AriakitButton).withConfig({ shouldForwardProp })', () => {
expect(button).toHaveStyleRule('height', theme.buttons.sizes.sm.height)
})
+ it('should look like a circle', () => {
+ const theme = createTheme()
+
+ render(
+
+ )
+ const button = screen.getByTestId('button')
+
+ expect(button).toHaveStyleRule('width', theme.buttons.sizes.sm.height)
+ expect(button).toHaveStyleRule('padding', '0')
+ expect(button).toHaveStyleRule('border-radius', theme.buttons.sizes.sm.height)
+ })
+
+ it('should look like the default button', () => {
+ const theme = createTheme()
+
+ render(
+ // Disabling type check since people wui user can be wrong on the value if not using TS
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+
+ )
+ const button = screen.getByTestId('button')
+
+ expect(button).toHaveStyleRule('width', 'auto')
+ expect(button).toHaveStyleRule('padding', theme.buttons.sizes.sm.padding)
+ expect(button).toHaveStyleRule('border-radius', theme.buttons.primary.borderRadius)
+ })
+
it('should have correct size', () => {
const theme = createTheme()
@@ -138,6 +171,96 @@ describe('', () => {
expect(button).toHaveAttribute('rel', 'noopener noreferrer') // added by target="_blank" on Link
})
+ it('should render width the shape prop being an object', () => {
+ const theme = createTheme()
+
+ render(
+
+ )
+
+ const button = screen.getByTestId('button')
+
+ expect(button).toHaveStyle({
+ height: theme.buttons.sizes.sm.height,
+ })
+ })
+
+ it('should render width the shape prop set as circle for md breakpoint', () => {
+ const theme = createTheme()
+ render(
+
+ )
+
+ const button = screen.getByTestId('button')
+
+ expect(button).toHaveStyleRule('width', theme.buttons.sizes.sm.height, {
+ media: `(width >= ${theme.screens.md}px)`,
+ })
+ expect(button).toHaveStyleRule('padding', '0', {
+ media: `(width >= ${theme.screens.md}px)`,
+ })
+ expect(button).toHaveStyleRule('border-radius', theme.buttons.sizes.sm.height, {
+ media: `(width >= ${theme.screens.md}px)`,
+ })
+ })
+
+ it('should render width the shape prop being and object and set as circle (using _)', () => {
+ const theme = createTheme()
+ render(
+
+ )
+
+ const button = screen.getByTestId('button')
+
+ expect(button).toHaveStyleRule('width', theme.buttons.sizes.sm.height)
+ expect(button).toHaveStyleRule('padding', '0')
+ expect(button).toHaveStyleRule('border-radius', theme.buttons.sizes.sm.height)
+ })
+
+ it('should render width the shape prop set as circle for _, then default for md and square for lg breakpoints', () => {
+ const theme = createTheme()
+ render(
+
+ )
+
+ const button = screen.getByTestId('button')
+
+ // breakpoint '_'
+ expect(button).toHaveStyleRule('width', theme.buttons.sizes.md.height)
+ expect(button).toHaveStyleRule('padding', '0')
+ expect(button).toHaveStyleRule('border-radius', theme.buttons.sizes.md.height)
+
+ // breakpoint 'md'
+ expect(button).toHaveStyleRule('width', 'auto', {
+ media: `(width >= ${theme.screens.md}px)`,
+ })
+ expect(button).toHaveStyleRule('padding', theme.buttons.sizes.md.padding, {
+ media: `(width >= ${theme.screens.md}px)`,
+ })
+ expect(button).toHaveStyleRule('border-radius', '0', {
+ media: `(width >= ${theme.screens.md}px)`,
+ })
+
+ // breakpoint 'lg'
+ expect(button).toHaveStyleRule('width', theme.buttons.sizes.md.height, {
+ media: `(width >= ${theme.screens.lg}px)`,
+ })
+ expect(button).toHaveStyleRule('padding', '0', {
+ media: `(width >= ${theme.screens.lg}px)`,
+ })
+ expect(button).toHaveStyleRule('border-radius', '0', {
+ media: `(width >= ${theme.screens.lg}px)`,
+ })
+ })
+
it('should have correct Icon size with Icon and text', () => {
const theme = createTheme()