-
-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement core base HTML atom
Button
It consists of the previously implemented styles and variants and represents a `<button>` (1). Next to this is can also wrap the base HTML element atom `A` (2) to handle internal and external links. References: (1) https://developer.mozilla.org/de/docs/Web/HTML/Element/button (2) #70 Associated epic: GH-63 GH-110
- Loading branch information
1 parent
8c45192
commit ff64b75
Showing
5 changed files
with
976 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]> | ||
* Copyright (C) 2018-present Sven Greb <[email protected]> | ||
* | ||
* Project: Nord Docs | ||
* Repository: https://github.com/arcticicestudio/nord-docs | ||
* License: MIT | ||
*/ | ||
|
||
import React from "react"; | ||
import PropTypes from "prop-types"; | ||
import styled from "styled-components"; | ||
|
||
import { A } from "atoms/core/HTMLElements"; | ||
|
||
import styles from "./styles"; | ||
|
||
const BaseButton = styled.button` | ||
${styles}; | ||
`; | ||
|
||
/** | ||
* A component that represents the `<button>` HTML element with multiple variants and additional props to toggle more | ||
* styles. | ||
* It also wraps the `A` component to render a link through the `href` and `to` props. | ||
* | ||
* @author Arctic Ice Studio <[email protected]> | ||
* @author Sven Greb <[email protected]> | ||
* @since 0.6.0 | ||
* @see https://developer.mozilla.org/de/docs/Web/HTML/Element/button | ||
*/ | ||
const Button = ({ children, dashed, disabled, ghost, href, onClick, outlined, quiet, to, variant }) => { | ||
if (href) { | ||
return ( | ||
<BaseButton | ||
as={A} | ||
dashed={dashed} | ||
disabled={disabled} | ||
ghost={ghost} | ||
href={href} | ||
outlined={outlined} | ||
quiet={quiet} | ||
variant={variant} | ||
> | ||
{children} | ||
</BaseButton> | ||
); | ||
} | ||
if (to) { | ||
return ( | ||
<BaseButton | ||
as={A} | ||
dashed={dashed} | ||
disabled={disabled} | ||
ghost={ghost} | ||
outlined={outlined} | ||
quiet={quiet} | ||
to={to} | ||
variant={variant} | ||
> | ||
{children} | ||
</BaseButton> | ||
); | ||
} | ||
return ( | ||
<BaseButton | ||
dashed={dashed} | ||
disabled={disabled} | ||
ghost={ghost} | ||
onClick={onClick} | ||
outlined={outlined} | ||
quiet={quiet} | ||
variant={variant} | ||
> | ||
{children} | ||
</BaseButton> | ||
); | ||
}; | ||
|
||
Button.propTypes = { | ||
children: PropTypes.node.isRequired, | ||
dashed: PropTypes.bool, | ||
disabled: PropTypes.bool, | ||
ghost: PropTypes.bool, | ||
href: PropTypes.string, | ||
onClick: PropTypes.func, | ||
outlined: PropTypes.bool, | ||
quiet: PropTypes.bool, | ||
to: PropTypes.string, | ||
variant: PropTypes.oneOf(["primary", "secondary", "simple", "subtle"]) | ||
}; | ||
|
||
Button.defaultProps = { | ||
dashed: false, | ||
disabled: false, | ||
ghost: false, | ||
href: "", | ||
onClick: () => {}, | ||
outlined: false, | ||
quiet: false, | ||
to: "", | ||
variant: "simple" | ||
}; | ||
|
||
export default Button; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/* | ||
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]> | ||
* Copyright (C) 2018-present Sven Greb <[email protected]> | ||
* | ||
* Project: Nord Docs | ||
* Repository: https://github.com/arcticicestudio/nord-docs | ||
* License: MIT | ||
*/ | ||
|
||
export { default } from "./Button"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
/* | ||
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]> | ||
* Copyright (C) 2018-present Sven Greb <[email protected]> | ||
* | ||
* Project: Nord Docs | ||
* Repository: https://github.com/arcticicestudio/nord-docs | ||
* License: MIT | ||
*/ | ||
|
||
import React from "react"; | ||
import { waitForElement } from "react-testing-library"; | ||
|
||
import { renderWithTheme } from "nord-docs-test-utils"; | ||
import Button from "atoms/core/Button"; | ||
import { ROUTE_DOCS } from "config/routes/mappings"; | ||
import { metadataNordDocs } from "data/project"; | ||
|
||
const handleOnClickMock = jest.fn(); | ||
|
||
const assertBaseButtonStyles = element => { | ||
const { color } = getComputedStyle(element); | ||
|
||
expect(element).toHaveStyleRule("display", "inline-flex"); | ||
expect(element).toHaveStyleRule("align-items", "center"); | ||
expect(element).toHaveStyleRule("text-align", "center"); | ||
expect(element).toHaveStyleRule("vertical-align", "middle"); | ||
expect(element).toHaveStyleRule("white-space", "nowrap"); | ||
expect(element).toHaveStyleRule("border-radius", expect.stringContaining("em")); | ||
expect(element).toHaveStyleRule("padding", expect.stringContaining("em")); | ||
expect(element).toHaveStyleRule("user-select", "none"); | ||
expect(element).toHaveStyleRule("cursor", "pointer", { | ||
modifier: ":hover:not(:disabled)" | ||
}); | ||
expect(element).toHaveStyleRule("outline", "none", { | ||
modifier: ":active" | ||
}); | ||
expect(element).toHaveStyleRule("outline", "none", { | ||
modifier: ":focus" | ||
}); | ||
expect(element).toHaveStyleRule("cursor", "not-allowed", { | ||
modifier: ":disabled" | ||
}); | ||
expect(element).toHaveStyleRule("color", expect.not.stringMatching(color), { | ||
modifier: ":disabled" | ||
}); | ||
}; | ||
|
||
const assertBackgroundColorStyle = element => { | ||
const { backgroundColor } = getComputedStyle(element); | ||
|
||
expect(element).toHaveStyleRule("background-color", expect.not.stringMatching(backgroundColor), { | ||
modifier: ":active:not(:disabled)" | ||
}); | ||
expect(element).toHaveStyleRule("background-color", expect.not.stringMatching(backgroundColor), { | ||
modifier: ":hover:not(:disabled)" | ||
}); | ||
}; | ||
|
||
describe("theme styles", () => { | ||
test("matches the snapshot with 'primary' variant", () => { | ||
const { container } = renderWithTheme( | ||
<Button onClick={handleOnClickMock} variant="primary"> | ||
Nord | ||
</Button> | ||
); | ||
|
||
assertBaseButtonStyles(container.firstChild); | ||
assertBackgroundColorStyle(container.firstChild); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
|
||
test("matches the snapshot with 'secondary' variant", () => { | ||
const { container } = renderWithTheme( | ||
<Button onClick={handleOnClickMock} variant="secondary"> | ||
Nord | ||
</Button> | ||
); | ||
|
||
assertBaseButtonStyles(container.firstChild); | ||
assertBackgroundColorStyle(container.firstChild); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
|
||
test("matches the snapshot with 'simple' variant", () => { | ||
const { container } = renderWithTheme( | ||
<Button onClick={handleOnClickMock} variant="simple"> | ||
Nord | ||
</Button> | ||
); | ||
|
||
assertBaseButtonStyles(container.firstChild); | ||
assertBackgroundColorStyle(container.firstChild); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
|
||
test("matches the snapshot with 'subtle' variant", () => { | ||
const { container } = renderWithTheme( | ||
<Button onClick={handleOnClickMock} variant="subtle"> | ||
Nord | ||
</Button> | ||
); | ||
|
||
assertBaseButtonStyles(container.firstChild); | ||
assertBackgroundColorStyle(container.firstChild); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
|
||
test("has borders with 'outlined' prop", () => { | ||
const { container } = renderWithTheme( | ||
<Button onClick={handleOnClickMock} outlined> | ||
Nord | ||
</Button> | ||
); | ||
|
||
assertBaseButtonStyles(container.firstChild); | ||
assertBackgroundColorStyle(container.firstChild); | ||
expect(container.firstChild).toHaveStyleRule("border", expect.stringContaining("solid")); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
|
||
test("has borders with 'dashed' and 'outlined' props", () => { | ||
const { container } = renderWithTheme( | ||
<Button dashed onClick={handleOnClickMock} outlined> | ||
Nord | ||
</Button> | ||
); | ||
|
||
assertBaseButtonStyles(container.firstChild); | ||
assertBackgroundColorStyle(container.firstChild); | ||
expect(container.firstChild).toHaveStyleRule("border", expect.stringContaining("dashed")); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
}); | ||
|
||
describe("logical behavior", () => { | ||
test("renders inernal URLs with `to` prop", () => { | ||
const { container } = renderWithTheme(<Button to={ROUTE_DOCS}>Nord</Button>); | ||
expect(container.firstChild).toHaveAttribute("href", ROUTE_DOCS); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
|
||
test("renders inernal URLs with `href` prop", () => { | ||
const { container } = renderWithTheme(<Button href={ROUTE_DOCS}>Nord</Button>); | ||
expect(container.firstChild).toHaveAttribute("href", ROUTE_DOCS); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
|
||
test("renders external URLs with `href` prop", () => { | ||
const { container } = renderWithTheme(<Button href={metadataNordDocs.homepage}>Nord</Button>); | ||
expect(container.firstChild).toHaveAttribute("href", metadataNordDocs.homepage); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
|
||
test("renders external URLs with `to` prop", () => { | ||
const { container } = renderWithTheme(<Button to={metadataNordDocs.homepage}>Nord</Button>); | ||
expect(container.firstChild).toHaveAttribute("href", metadataNordDocs.homepage); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
}); | ||
|
||
describe("rendering", () => { | ||
test("renders with text", async () => { | ||
const { getByText } = renderWithTheme(<Button onClick={handleOnClickMock}>Nord</Button>); | ||
|
||
await waitForElement(() => getByText(/Nord/i)); | ||
}); | ||
}); |
Oops, something went wrong.