Skip to content

Commit

Permalink
Merge pull request #14 from code0-tech/button
Browse files Browse the repository at this point in the history
Button and ButtonGroup Component in all variants
  • Loading branch information
nicosammito authored Dec 16, 2023
2 parents f767bc1 + e6f9266 commit b56b4b5
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/components/alert/Alert.style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
}

.alert__content {
border-top: 1px solid borderColor($color);
border-top: 1px solid borderColor();
}

.alert__dismissible {
Expand Down
25 changes: 25 additions & 0 deletions src/components/button-group/ButtonGroup.style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@import "../../styles/helpers";

.button-group {
@include box(false, $primary);

border: none;
width: fit-content;
margin-bottom: 1rem;
display: flex;

> * {
margin-bottom: 0 !important;
border-radius: 0 !important;

&:first-child {
border-bottom-left-radius: .5rem !important;
border-top-left-radius: .5rem !important;
}

&:last-child {
border-bottom-right-radius: .5rem !important;
border-top-right-radius: .5rem !important;
}
}
}
18 changes: 18 additions & 0 deletions src/components/button-group/ButtonGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, {ReactElement} from "react";
import {ButtonType} from "../button/Button";
import "./ButtonGroup.style.scss"

export interface ButtonGroupType {
children: ReactElement<ButtonType>[]
}

const ButtonGroup: React.FC<ButtonGroupType> = (props) => {

const {children} = props

return <div className={"button-group"}>
{children}
</div>
}

export default ButtonGroup;
91 changes: 91 additions & 0 deletions src/components/button/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import Button from "./Button";
import {StoryObj} from "@storybook/react";
import React from "react";
import ButtonGroup from "../button-group/ButtonGroup";
import {IconAbc} from "@tabler/icons-react";

const meta = {
title: "Button",
component: Button,
argTypes: {
icon: {
type: "boolean",
default: true
},
disabled: {
type: "boolean",
default: false
},
variant: {table: {disable: true}}
}
}

type ButtonStory = StoryObj<{ icon: boolean, disabled: boolean}>;
type ButtonGroupStory = StoryObj<typeof ButtonGroup>;

export default meta

export const Buttons: ButtonStory = {
render: (args) => {

const {icon, disabled} = args

return <>
{
["primary", "secondary", "info", "success", "warning", "error"].map(value => {
// @ts-ignore
return <Button disabled={disabled} variant={value}>
{icon ? <Button.Icon><IconAbc/></Button.Icon> : null}
{value}
</Button>
})
}
</>
},
args: {
icon: true,
disabled: false
}
}

export const ButtonGroups: ButtonGroupStory = {
render: () => {
return <>
<ButtonGroup>
{
["primary", "secondary", "info", "success", "warning", "error"].map((value, index) => {
// @ts-ignore
return <Button variant={value}>
{(index % 2) == 0 ? <Button.Icon><IconAbc/></Button.Icon> : null}
{value}
</Button>
})
}
</ButtonGroup>
<ButtonGroup>
{
["primary", "primary", "primary", "primary"].map((value, index) => {
// @ts-ignore
return <Button variant={value}>
{(index % 2) == 0 ? <Button.Icon><IconAbc/></Button.Icon> : null}
{value}
</Button>
})
}
</ButtonGroup>

<ButtonGroup>
{
["secondary", "secondary", "secondary", "secondary"].map((value, index) => {
// @ts-ignore
return <Button disabled={(index % 2) == 0} variant={value}>
{(index % 2) == 0 ? <Button.Icon><IconAbc/></Button.Icon> : null}
{value}
</Button>
})
}
</ButtonGroup>

</>
}
}
51 changes: 51 additions & 0 deletions src/components/button/Button.style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
@import "../../styles/helpers";

.button {
@include box(true);

padding: .5rem;
cursor: pointer;
display: flex;
align-items: center;
margin-bottom: 1rem;
width: fit-content;

&--disabled {
opacity: 50%;
pointer-events: none;
}

&__icon + &__content {
margin-left: .5rem;
display: inline-block;
position: relative;
}

&__icon {
@include box(false);

display: inline-flex;
justify-content: center;
align-items: center;
width: 1rem;
height: 1rem;
padding: .25rem;
cursor: pointer;

> * {
width: 1rem;
height: 1rem;
}
}
}

@each $name, $color in $variants {
.button--#{$name} {
@include box(true, $color);

.button__icon {
@include box(false, $color);
}

}
}
61 changes: 61 additions & 0 deletions src/components/button/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import "./Button.style.scss"
import React, {
AnchorHTMLAttributes,
DetailedHTMLProps,
ReactNode
} from "react";

export interface ButtonType extends DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement> {
children: ReactNode | ReactNode[]
//defaults to primary
variant?: "primary" | "secondary" | "info" | "success" | "warning" | "error",
//defaults to false
active?: boolean
//defaults to false
disabled?: boolean
}

export interface ButtonIconType {
children: ReactNode
}

const Button: React.FC<ButtonType> = (props) => {

const {children, variant = "primary", active = false, disabled = false, ...args} = props
const icon = getChild(children, ButtonIcon)
const content = getContent(children, ButtonIcon)


return <a {...args} className={`button button--${variant} ${active ? "button--active" : ""} ${disabled ? "button--disabled" : ""}`}>
{icon}
{content ? <span className={"button__content"}>{content}</span> : null}
</a>
}

const ButtonIcon: React.FC<ButtonIconType> = ({children}) => {
return <span className={"button__icon"}>
{children}
</span>
}


const getChild = (children: ReactNode | ReactNode[], child: React.FC<any>): ReactNode | null => {
return React.Children.toArray(children).find((childT) => {
if (!React.isValidElement(childT)) return false;
return childT.type == child;
})
}

const getContent = (children: ReactNode | ReactNode[], ...child: React.FC<any>[]): ReactNode[] | null => {

const array = React.Children.toArray(children).filter((childT) => {
if (!React.isValidElement(childT)) return true;
return !child.find(value => value == childT.type);
})

return array.length == 0 ? null : array
}

export default Object.assign(Button, {
Icon: ButtonIcon
});
4 changes: 2 additions & 2 deletions src/styles/_helpers.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
color: rgba($white, .5);
font-family: Ubuntu, sans-serif;
font-size: $secondaryFontSize;
border: 1px solid borderColor($color);
border: 1px solid borderColor();
position: relative;

@if ($active == true) {
@include hoverAndActiveContent {
border: 1px solid borderColor($color, true);
border: 1px solid borderColor($white, true);
}

@include activeContent {
Expand Down

0 comments on commit b56b4b5

Please sign in to comment.