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(' + ) + + 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()