Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@devseed-ui/button

Package Overview
Dependencies
Maintainers
3
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@devseed-ui/button - npm Package Compare versions

Comparing version 3.1.0 to 4.0.0

dist/index.cjs.js

7

CHANGELOG.md
# @devseed-ui/button
## 4.0.0
`@devseed-ui/*` Now uses a fixed versioning system.
The global changelog is at the root of the repo.
----
## 3.1.0

@@ -4,0 +11,0 @@

19

package.json
{
"name": "@devseed-ui/button",
"version": "3.1.0",
"version": "4.0.0",
"description": "devseed UI Kit Button",
"browser": "./dist/index.web.js",
"main": "./dist/index.node.js",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
"types": "./src/index.d.ts",
"sideEffects": false,
"scripts": {
"build": "yarn webpack --config ../../webpack.config.js",
"watch": "yarn webpack --watch --config ../../webpack.config.js"
"build": "yarn rollup -c ../../rollup.config.js",
"watch": "yarn rollup -c ../../rollup.config.js -w"
},

@@ -28,5 +30,6 @@ "license": "MIT",

"dependencies": {
"@devseed-ui/theme-provider": "^3.0.0",
"@devseed-ui/collecticons": "3.2.0"
}
"@devseed-ui/collecticons": "^4.0.0",
"@devseed-ui/theme-provider": "^4.0.0"
},
"gitHead": "6d2abc5d6d83c14f401f0c29fc01d9a6888bdf8f"
}
# @devseed-ui/button
```hint|neutral
This component **may** require [collecticons](/collecticons) to be included if you're using the `useIcon` prop.
You'll see strange characters (example �) in place of icons if collecticons is missing.
```
#### Variation
Buttons come in different variations:
- primary-raised-light
- primary-raised-semidark
- primary-raised-dark
- primary-plain
- danger-raised-light
- danger-raised-dark
- danger-plain
- success-raised-light
- success-raised-dark
- success-plain
- achromic-plain
- achromic-glass
- base-raised-light
- base-raised-semidark
- base-raised-dark
- base-plain
```react
<DevseedUiThemeProvider>
<style>{`
.line {
margin-bottom: 1rem;
}
.line > * {
margin-right: 0.5rem;
}
.achromic {
background: #443F3F;
padding: 0.5rem;
}
`}</style>
<p className="line">
<Button variation="base-plain">base-plain</Button>
<Button variation="primary-plain">primary-plain</Button>
<Button variation="success-plain">success-plain</Button>
</p>
<p className="line">
<Button variation="primary-raised-light">primary-raised-light</Button>
<Button variation="primary-raised-semidark">primary-raised-semidark</Button>
<Button variation="primary-raised-dark">primary-raised-dark</Button>
</p>
<p className="line">
<Button variation="danger-raised-light">danger-raised-light</Button>
<Button variation="danger-raised-dark">danger-raised-dark</Button>
</p>
<p className="line">
<Button variation="success-raised-light">success-raised-light</Button>
<Button variation="success-raised-dark">success-raised-dark</Button>
</p>
<p className="line achromic">
<Button variation="achromic-plain">achromic-plain</Button>
<Button variation="achromic-glass">achromic-glass</Button>
</p>
<p className="line">
<Button variation="base-raised-light">base-raised-light</Button>
<Button variation="base-raised-semidark">base-raised-semidark</Button>
<Button variation="base-raised-dark">base-raised-dark</Button>
</p>
</DevseedUiThemeProvider>
```
#### Size
Button supports three sizes – large for emphasized actions, medium as default, and small as alternative to medium.
```react
<DevseedUiThemeProvider>
<Button
variation="base-raised-light"
size="small"
className="button-class"
title="sample button"
onClick={() => {}}
>
Click Me
</Button>
<Button
variation="base-raised-light"
size="medium"
className="button-class"
title="sample button"
onClick={() => {}}
>
Click Me
</Button>
<Button
variation="base-raised-light"
size="large"
className="button-class"
title="sample button"
onClick={() => {}}
>
Click Me
</Button>
</DevseedUiThemeProvider>
```
## API Documentation
```table
rows:
- Prop name: "variation"
Type: "One of: primary-raised-light | primary-raised-semidark | primary-raised-dark | primary-plain | danger-raised-light | danger-raised-dark | danger-plain | success-raised-light | success-raised-dark | success-plain | achromic-plain | achromic-glass | base-raised-light | base-raised-semidark | base-raised-dark | base-plain"
Description: "Sets the style variant of the button"
Default value: "base-plain"
- Prop name: "size"
Type: "oneOf ['small', 'medium', 'large', 'xlarge']"
Description: "Sets the size of the button"
Default value: "medium"
- Prop name: "radius"
Type: "oneOf ['ellipsoid','square', 'rounded']"
Description: "The value for the radius"
Default value: "rounded"
- Prop name: "box"
Type: "oneOf ['block','semi-fluid', 'null']"
Description: "The value for the box."
Default value: "null"
- Prop name: "active"
Type: "bool"
Description: "Whether the button is in an active state."
Default value: "false"
- Prop name: "hideText"
Type: "bool"
Description: "Whether the button text should be hidden"
Default value: "false"
- Prop name: "disabled"
Type: "bool"
Description: "Whether the button should be disabled."
Default value: "false"
- Prop name: "visuallyDisabled"
Type: "bool"
Description: "Whether the button should be visually disabled. A visually disabled button looks disabled but retains the mouse events. This is useful to trigger tooltips on hover."
Default value: "false"
- Prop name: "useIcon"
Type: "oneOf [array, string]"
Description: "The value for the icon. Has to be the name of a collecticon. If an array is used instead of a string, the first position is the name of the icon, and the second the position ('before' | 'after')."
Default value: "null"
- Prop name: "onClick"
Type: "func"
Description: "Click event handler"
Default value: "f => f"
```
Buttons and button groups.

@@ -1,33 +0,111 @@

import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import styled, { css } from 'styled-components';
import { rgba } from 'polished';
import { themeVal, visuallyHidden } from '@devseed-ui/theme-provider';
import { themeVal, glsp, disabled } from '@devseed-ui/theme-provider';
import React from 'react';
import { useButtonGroup } from './context';
import {
renderIcon,
renderDisabledState,
renderButtonVariation,
renderButtonRadius,
renderButtonSize,
renderButtonBox,
renderHideText,
} from './styled';
export const createButtonStyles = (props) => {
return css`
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
gap: ${glsp(0.25)};
user-select: none;
line-height: inherit;
border: ${themeVal('button.shape.border')} solid transparent;
background: transparent;
font-family: ${themeVal('button.type.family')};
font-weight: ${themeVal('button.type.weight')};
font-style: ${themeVal('button.type.style')};
font-variation-settings: ${themeVal('button.type.settings')};
text-transform: ${themeVal('button.type.case')};
outline: 0 solid transparent;
text-decoration: none;
transition: background-color 0.24s ease-in-out 0s,
outline-width 0.16s ease-in-out 0s;
// This empty element is needed so we can use the styled component property
// "forwardedAs" and replace it while maintaining the component structure.
// Because of this, buttons SHOULD NOT be used with "as".
const El = styled.button``;
&:focus-visible {
outline-width: 0.25rem;
}
/* eslint-disable-next-line react/display-name */
const BaseButton = React.forwardRef(({ children, ...rest }, ref) => {
const elType = rest.type || (!rest.as ? 'button' : undefined);
return (
<El ref={ref} {...rest} type={elType}>
{!!children && <span>{children}</span>}
</El>
);
});
&:focus:not(:focus-visible) {
outline: 0;
}
BaseButton.propTypes = {
/* Disabled state based on prop. */
${renderDisabledState(props)}
&[disabled] {
${disabled()}
}
/* Size. */
${renderButtonSize(props)}
/* Variation. */
${renderButtonVariation(props)}
/* Content Fit */
${renderButtonFitting(props)}
/* Radius */
&,
&::after {
${renderButtonRadius(props)}
}
&::after {
position: absolute;
z-index: 10;
top: -${themeVal('button.shape.border')};
right: -${themeVal('button.shape.border')};
bottom: -${themeVal('button.shape.border')};
left: -${themeVal('button.shape.border')};
content: '';
background: transparent;
pointer-events: none;
}
`;
};
// Styled component of a button
const StyledButton = styled.button.attrs((props) => {
return {
'aria-pressed': String(!!props.active),
type: props.type || (!props.as ? 'button' : undefined)
};
})`
${createButtonStyles}
`;
/* eslint-disable react/display-name, react/prop-types */
// Wrap the styled button in a high order component to pass the context
// provider. This component needs to also become a styled-component to take
// advantage of the styled component functionalities.
// To be sure that this component maintains its structure you MUST use the
// styled-component's `forwardedAs` property when replacing what's being
// rendered, instead of using `as`.
export const Button = styled(
React.forwardRef((props, ref) => {
const { size, variation, radius } = useButtonGroup();
return (
<StyledButton
size={size}
variation={variation}
radius={radius}
{...props}
ref={ref}
/>
);
})
)`
/* styled-component */
`;
/* eslint-enable */
Button.propTypes = {
children: PropTypes.node,

@@ -37,105 +115,255 @@ variation: PropTypes.string,

radius: PropTypes.string,
box: PropTypes.string,
fitting: PropTypes.string,
active: PropTypes.bool,
hideText: PropTypes.bool,
disabled: PropTypes.bool,
visuallyDisabled: PropTypes.bool,
useIcon: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
disabled: PropTypes.bool
};
/**
* Renders a Button element with a span inside it.
* Renders the button disabled stated.
*
* @param {string} variation Select the button variation to render. One of:
* "primary-raised-light" | "primary-raised-semidark" |
* "primary-raised-dark" | "primary-plain" | "danger-raised-light" |
* "danger-raised-dark" | "danger-plain" | "success-raised-light" |
* "success-raised-dark" | "success-plain" | "achromic-plain" |
* "achromic-glass" | "base-raised-light" | "base-raised-semidark" |
* "base-raised-dark" | "base-plain"
* @param {string} size The value for the box. One of "small" | "medium" |
* "large" | "xlarge"
* @param {string} radius The value for the radius. One of "ellipsoid" |
* "square" | "rounded"
* @param {string} box The value for the box. One of "block" | "semi-fluid" |
* "null"
* @param {boolean} active Whether the button is in an active state.
* @param {boolean} hideText Whether the button text should be hidden.
* @param {boolean} disabled Whether the button should be disabled.
* @param {boolean} visuallyDisabled Whether the button should be visually
* disabled. A visually disabled button looks disabled but retains the
* mouse events. This is useful to trigger tooltips on hover.
* @param {string|array} useIcon The value for the icon. Has to be the name of a
* collecticon. If an array is used instead of a string, the first
* position is the name of the icon, and the second the position
* ("before" | "after").
* @param {object} props The element props
* @param {bool} props.disabled Whether the button should be disabled.
*/
const Button = styled(BaseButton)`
cursor: pointer;
user-select: none;
display: inline-block;
text-align: center;
white-space: nowrap;
vertical-align: middle;
line-height: 1.5rem;
font-size: 1rem;
padding: 0.25rem 0.75rem;
min-width: 2rem;
background: none;
text-shadow: none;
border: 0;
font-family: ${themeVal('type.button.family')};
font-weight: ${themeVal('type.button.weight')};
font-style: ${themeVal('type.button.style')};
font-variation-settings: ${themeVal('type.button.settings')};
text-transform: ${themeVal('type.button.case')};
function renderDisabledState(props) {
return props.disabled
? disabled()
: css`
cursor: pointer;
`;
}
/* States */
/**
* Renders the button size based on the props.
*
* @param {object} props The element props
* @param {string} props.size The value for the box. One of
* "small" | "medium" | "large" | "xlarge"
*/
function renderButtonSize(props) {
const { size = 'medium' } = props;
&,
&:focus {
/* This causes usability problems. Needs fixing. */
outline: none;
switch (size) {
case 'small':
return css`
min-width: 1.5rem;
height: 1.5rem;
font-size: 0.875rem;
padding: 0 0.5rem;
`;
case 'medium':
return css`
min-width: 2rem;
height: 2rem;
font-size: 1rem;
padding: 0 0.75rem;
`;
case 'large':
return css`
min-width: 2.5rem;
height: 2.5rem;
font-size: 1rem;
padding: 0 1rem;
`;
case 'xlarge':
return css`
min-width: 3rem;
height: 3rem;
font-size: 1rem;
padding: 0 1.25rem;
`;
default:
throw new Error(
`Invalid button size (${size}). Must be one of [small, medium, large, xlarge].`
);
}
}
&:hover {
opacity: 1;
function renderButtonVariation(props) {
const [color, type] = props.variation?.split('-') || ['base', 'text'];
// Function to get the color from the theme.
const tColor = (c) => themeVal(['color', c]);
if (!['text', 'fill', 'outline'].includes(type)) {
throw new Error(
`Invalid button type (${type}). Must be one of [text, fill, outline].`
);
}
/* Icon handling */
${props => renderIcon(props)}
if (color === 'achromic') {
if (type === 'fill') {
throw new Error(`achromic-fill buttons do not exist.`);
}
/* Checkbox/radio handling */
> input[type=checkbox],
> input[type=radio] {
${visuallyHidden()}
switch (type) {
case 'text':
return css`
color: #fff;
&:hover {
background-color: ${rgba('#fff', 0.04)};
}
/* Print & when prop is passed */
${({ active }) => active && '&,'}
&:active,
&.active {
background-color: ${rgba('#fff', 0.08)};
}
&:focus-visible {
outline-color: ${rgba('#fff', 0.16)};
}
`;
case 'outline':
return css`
border-color: #fff;
color: #fff;
&:hover {
background-color: ${rgba('#fff', 0.04)};
}
/* Print & when prop is passed */
${({ active }) => active && '&,'}
&:active,
&.active {
background-color: ${rgba('#fff', 0.04)};
}
&:focus-visible {
outline-color: ${rgba('#fff', 0.16)};
}
`;
}
}
/* Animation */
transition: background-color 0.24s ease 0s;
switch (type) {
case 'text':
return css`
color: ${tColor(color)};
/* Variations */
${props => renderButtonVariation(props)}
&:hover {
background-color: ${tColor(`${color}-50a`)};
}
/* Size */
${props => renderButtonSize(props)}
/* Print & when prop is passed */
${({ active }) => active && '&,'}
&:active,
&.active {
background-color: ${tColor(`${color}-100a`)};
}
/* Radius */
${props => renderButtonRadius(props)}
&:focus-visible {
outline-color: ${tColor(`${color}-200a`)};
}
`;
case 'outline':
return css`
border-color: ${tColor(color)};
color: ${tColor(color)};
/* Box */
${props => renderButtonBox(props)}
&:hover {
background-color: ${tColor(`${color}-50a`)};
}
/* Hide Text */
${props => renderHideText(props)}
/* Print & when prop is passed */
${({ active }) => active && '&,'}
&:active,
&.active {
background-color: ${tColor(`${color}-100a`)};
}
/* Disabled state */
${props => renderDisabledState(props)}
`;
&:focus-visible {
outline-color: ${tColor(`${color}-200a`)};
}
`;
case 'fill':
return css`
border-color: ${themeVal('color.base-200a')};
background-color: ${tColor(color)};
color: #fff;
Button.defaultProps = {
variation: 'base-plain',
size: 'medium',
};
&:hover {
background-color: ${tColor(`${color}-600`)};
}
export default Button;
/* Print & when prop is passed */
${({ active }) => active && '&,'}
&:active,
&.active {
background-color: ${tColor(`${color}-700`)};
}
&:focus-visible {
outline-color: ${tColor(`${color}-200a`)};
}
&::after {
box-shadow: ${themeVal('boxShadow.elevationB')};
}
`;
}
}
/**
* Renders the border radius based on the props.
*
* @param {object} props The element props
* @param {string} props.radius The value for the radius. One of
* "ellipsoid" | "square" | "rounded"
*/
function renderButtonRadius(props) {
const { radius = 'rounded' } = props;
switch (radius) {
case 'rounded':
return css`
border-radius: ${themeVal('shape.rounded')};
`;
case 'ellipsoid':
return css`
border-radius: ${themeVal('shape.ellipsoid')};
`;
case 'square':
return css`
border-radius: 0;
`;
default:
throw new Error(
`Invalid button radius (${radius}). Must be one of [rounded, ellipsoid, square].`
);
}
}
/**
* Renders the content fit based on the props.
*
* @param {object} props The element props
* @param {string} props.fitting The value for the content fit. One of
* "skinny" | "regular" | "relaxed" | "baggy"
*/
function renderButtonFitting(props) {
const { fitting = 'regular' } = props;
switch (fitting) {
case 'skinny':
return css`
padding-left: 0;
padding-right: 0;
`;
case 'regular':
return '';
case 'relaxed':
return css`
min-width: 12rem;
`;
case 'baggy':
return css`
width: 100%;
`;
default:
throw new Error(
`Invalid button fitting (${fitting}). Must be one of [skinny, regular, relaxed, baggy].`
);
}
}

@@ -11,3 +11,3 @@ import React from 'react';

describe('packages/Button', () => {
describe('<Button />', () => {
const onClick = jest.fn();

@@ -21,3 +21,3 @@ const className = 'test-button-class';

{child}
</Button>,
</Button>
);

@@ -27,25 +27,25 @@

test(`renders "${className}" in the button's classList`, () => {
it(`renders "${className}" in the button's classList`, () => {
expect(renderedButton.classList.contains(className)).toBe(true);
});
test(`renders "${child}" as the button's textContent`, () => {
it(`renders "${child}" as the button's textContent`, () => {
expect(renderedButton.textContent).toBe(child);
});
test(`renders "${title}" as the button title`, () => {
it(`renders "${title}" as the button title`, () => {
expect(renderedButton.title).toBe(title);
});
test(`renders inside of a button tag by default`, () => {
it(`renders inside of a button tag by default`, () => {
expect(renderedButton.tagName.toLowerCase()).toBe('button');
});
test(`renders a button with the "button" type by default`, () => {
it(`renders a button with the "button" type by default`, () => {
expect(renderedButton.type).toBe('button');
});
test(`renders a button with the given type when one is set`, () => {
it(`renders a button with the given type when one is set`, () => {
const submitButton = renderWithTheme(
<Button type="submit">My submit button</Button>,
<Button type='submit'>My submit button</Button>
).container.firstChild;

@@ -55,5 +55,5 @@ expect(submitButton.type).toBe('submit');

test(`renders component inside of a React Element/HTML tag based on as prop`, () => {
it(`renders component inside of a React Element/HTML tag based on forwardedAs prop`, () => {
const { container } = renderWithTheme(
<Button forwardedAs="div">Click me!</Button>,
<Button forwardedAs='div'>Click me!</Button>
);

@@ -64,7 +64,7 @@ const buttonComponent = container.firstChild;

test(`renders component inside of a React Element/HTML tag based on as prop, even when "href" is set`, () => {
it(`renders component inside of a React Element/HTML tag based on forwardedAs prop, even when "href" is set`, () => {
const { container } = renderWithTheme(
<Button forwardedAs="div" href="http://developmentseed.org">
<Button forwardedAs='div' href='http://developmentseed.org'>
Click me!
</Button>,
</Button>
);

@@ -76,2 +76,12 @@ const buttonComponent = container.firstChild;

it(`renders a active button`, () => {
const rendered = renderWithTheme(<Button active>Btn</Button>);
expect(rendered.asFragment()).toMatchSnapshot();
});
it(`renders a disabled button`, () => {
const rendered = renderWithTheme(<Button disabled>Btn</Button>);
expect(rendered.asFragment()).toMatchSnapshot();
});
it('renders SSR compatible <Button />', () => {

@@ -82,6 +92,53 @@ const renderOnServer = () =>

<Button>Some button</Button>
</DevseedUiThemeProvider>,
</DevseedUiThemeProvider>
);
expect(renderOnServer).not.toThrow();
});
const btnVariations = [
'base-fill',
'base-text',
'base-outline',
'primary-fill',
'primary-text',
'primary-outline',
'achromic-text',
'achromic-outline'
];
describe('button variations', () => {
it.each(btnVariations)(`should render: %s`, (variation) => {
const rendered = renderWithTheme(
<Button variation={variation}>Btn</Button>
);
expect(rendered.asFragment()).toMatchSnapshot();
});
});
const btnSize = ['small', 'medium', 'large', 'xlarge'];
describe('button size', () => {
it.each(btnSize)(`should render: %s`, (size) => {
const rendered = renderWithTheme(<Button size={size}>Btn</Button>);
expect(rendered.asFragment()).toMatchSnapshot();
});
});
const btnRadius = ['rounded', 'ellipsoid', 'square'];
describe('button radius', () => {
it.each(btnRadius)(`should render: %s`, (radius) => {
const rendered = renderWithTheme(<Button radius={radius}>Btn</Button>);
expect(rendered.asFragment()).toMatchSnapshot();
});
});
const btnFitting = ['skinny', 'regular', 'relaxed', 'baggy'];
describe('button fitting', () => {
it.each(btnFitting)(`should render: %s`, (fitting) => {
const rendered = renderWithTheme(<Button fitting={fitting}>Btn</Button>);
expect(rendered.asFragment()).toMatchSnapshot();
});
});
});

@@ -1,8 +0,10 @@

import PropTypes from 'prop-types';
import React from 'react';
import T from 'prop-types';
import styled, { css } from 'styled-components';
import Button from './Button';
import { themeVal } from '@devseed-ui/theme-provider';
import { Button } from './Button';
import { ButtonGroupProvider } from './context';
/**

@@ -15,70 +17,159 @@ * Renders the button group orientation based on the props.

*/
function renderOrientation({ orientation }) {
if (orientation === 'horizontal') {
return css`
flex-flow: row nowrap;
> ${Button}:first-child:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
clip-path: inset(-100% 0 -100% -100%);
}
> ${Button}:last-child:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
clip-path: inset(-100% -100% -100% 0);
}
> ${Button}:not(:first-child):not(:last-child) {
border-radius: 0;
clip-path: inset(-100% 0);
}
> ${Button} + ${Button} {
margin-left: -${themeVal('layout.border')};
}
`;
}
function renderOrientation(props) {
const { orientation = 'horizontal' } = props;
if (orientation === 'vertical') {
return css`
flex-flow: column;
> ${Button}:first-child:not(:last-child) {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
clip-path: inset(-100% -100% 0 -100%);
}
> ${Button}:last-child:not(:first-child) {
border-top-left-radius: 0;
border-top-right-radius: 0;
clip-path: inset(0 -100% -100% -100%);
}
> ${Button}:not(:first-child):not(:last-child) {
border-radius: 0;
clip-path: inset(0 -100%);
}
> ${Button} + ${Button} {
margin-top: -${themeVal('layout.border')};
}
`;
switch (orientation) {
case 'horizontal':
return css`
flex-flow: row nowrap;
> ${Button}:first-child:not(:last-child) {
&,
&::after {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&::after {
clip-path: inset(-100% 0 -100% -100%);
}
}
> ${Button}:last-child:not(:first-child) {
&,
&::after {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
&::after {
clip-path: inset(-100% -100% -100% 0);
}
}
> ${Button}:not(:first-child):not(:last-child) {
&,
&::after {
border-radius: 0;
}
&::after {
clip-path: inset(-100% 0);
}
}
> ${Button} + ${Button} {
margin-left: -${themeVal('button.shape.border')};
}
`;
case 'vertical':
return css`
flex-flow: column;
> ${Button}:first-child:not(:last-child) {
&,
&::after {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
&::after {
clip-path: inset(-100% -100% 0 -100%);
}
}
> ${Button}:last-child:not(:first-child) {
&,
&::after {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
&::after {
clip-path: inset(0 -100% -100% -100%);
}
}
> ${Button}:not(:first-child):not(:last-child) {
&,
&::after {
border-radius: 0;
}
&::after {
clip-path: inset(0 -100%);
}
}
> ${Button} + ${Button} {
margin-top: -${themeVal('button.shape.border')};
}
`;
default:
throw new Error(
`Invalid button group orientation (${orientation}). Must be one of [horizontal, vertical].`
);
}
}
const ButtonGroup = styled.div`
position: relative;
display: inline-flex;
> ${Button} {
display: block;
export const createButtonGroupStyles = (props) => {
return css`
position: relative;
margin: 0;
z-index: 2;
}
display: inline-flex;
/* Group orientation */
${props => renderOrientation(props)}
> ${Button} {
position: relative;
flex: 1 1 auto;
&:focus {
z-index: 1;
}
}
/* Group orientation */
${renderOrientation(props)}
`;
};
// Styled component of the button group.
const StyledButtonGroup = styled.div`
${createButtonGroupStyles}
`;
/* eslint-disable react/display-name, react/prop-types */
// Wrap the styled button in a high order component to pass the context
// provider. This component needs to also become a styled-component to take
// advantage of the styled component functionalities.
// To be sure that this component maintains its structure you MUST use the
// styled-component's `forwardedAs` property when replacing what's being
// rendered, instead of using `as`.
export const ButtonGroup = styled(
React.forwardRef((props, ref) => {
const {
className,
children,
size,
variation,
radius,
role = 'group',
...rest
} = props;
return (
<ButtonGroupProvider size={size} variation={variation} radius={radius}>
<StyledButtonGroup
className={className}
role={role}
ref={ref}
{...rest}
>
{children}
</StyledButtonGroup>
</ButtonGroupProvider>
);
})
)`
/* styled-component */
`;
/* eslint-enable */
ButtonGroup.propTypes = {
children: PropTypes.node,
orientation: PropTypes.string,
children: T.node,
className: T.string,
size: T.string,
variation: T.string,
radius: T.string,
role: T.string
};
export default ButtonGroup;

@@ -1,9 +0,4 @@

import Button from './Button';
import {
buttonVariation,
buttonVariationHoverCss,
buttonVariationBaseCss,
buttonVariationActiveCss,
} from './styled';
import ButtonGroup from './ButtonGroup';
import { Button, createButtonStyles } from './Button';
import { ButtonGroup, createButtonGroupStyles } from './ButtonGroup';
import { ButtonGroupProvider, useButtonGroup } from './context';

@@ -13,6 +8,6 @@ export {

Button,
buttonVariation,
buttonVariationBaseCss,
buttonVariationHoverCss,
buttonVariationActiveCss,
createButtonStyles,
createButtonGroupStyles,
ButtonGroupProvider,
useButtonGroup
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc