├── _redirects
├── __mocks__
└── fileMock.js
├── src
├── components
│ ├── Typography
│ │ ├── Text
│ │ │ ├── index.js
│ │ │ └── styles.module.css
│ │ ├── P.jsx
│ │ ├── H1.jsx
│ │ ├── H2.jsx
│ │ ├── H3.jsx
│ │ ├── H4.jsx
│ │ ├── H5.jsx
│ │ ├── H6.jsx
│ │ ├── tests
│ │ │ ├── __snapshots__
│ │ │ │ └── typography.test.js.snap
│ │ │ └── typography.test.js
│ │ └── styles.module.css
│ ├── DarkLightToggle
│ │ ├── styles.module.css
│ │ └── tests
│ │ │ └── darkLightToggle.test.js
│ ├── NumberInput
│ │ ├── styles.module.css
│ │ ├── tests
│ │ │ ├── __snapshots__
│ │ │ │ └── numberInput.test.js.snap
│ │ │ └── numberInput.test.js
│ │ └── NumberInput.jsx
│ ├── Link
│ │ ├── Link.test.js
│ │ ├── test
│ │ │ ├── __snapshots__
│ │ │ │ └── Link.test.js.snap
│ │ │ └── Link.test.js
│ │ ├── styles.module.css
│ │ └── Link.jsx
│ ├── Table
│ │ ├── Table.test.js
│ │ ├── TableHeader.jsx
│ │ ├── TableBody.jsx
│ │ ├── styles.module.css
│ │ ├── tests
│ │ │ ├── __snapshots__
│ │ │ │ └── table.test.js.snap
│ │ │ └── table.test.js
│ │ └── Table.jsx
│ ├── Message
│ │ ├── Message.test.js
│ │ ├── test
│ │ │ ├── message.test.js
│ │ │ └── __snapshots__
│ │ │ │ └── message.test.js.snap
│ │ ├── Message.jsx
│ │ └── styles.module.css
│ ├── Checkbox
│ │ ├── Checkbox.test.js
│ │ ├── tests
│ │ │ ├── checkbox.test.js
│ │ │ └── __snapshots__
│ │ │ │ └── checkbox.test.js.snap
│ │ ├── Checkbox.jsx
│ │ └── styles.module.css
│ ├── StatusBar
│ │ ├── StatusBar.test.js
│ │ └── test
│ │ │ └── statusBar.test.js
│ ├── TextHighlighted
│ │ ├── styles.module.css
│ │ ├── tests
│ │ │ ├── __snapshots__
│ │ │ │ └── texthighlighted.test.js.snap
│ │ │ └── texthighlighted.test.js
│ │ └── TextHighlighted.jsx
│ ├── Description
│ │ ├── styles.module.css
│ │ └── Description.jsx
│ ├── Card
│ │ ├── tests
│ │ │ ├── __snapshots__
│ │ │ │ └── card.test.js.snap
│ │ │ └── card.test.js
│ │ ├── styles.module.css
│ │ └── Card.jsx
│ ├── CopyableText
│ │ ├── helpers.js
│ │ ├── styles.module.css
│ │ ├── tests
│ │ │ ├── copyableText.test.js
│ │ │ └── __snapshots__
│ │ │ │ └── copyableText.test.js.snap
│ │ └── CopyableText.jsx
│ ├── Button
│ │ ├── tests
│ │ │ ├── __snapshots__
│ │ │ │ └── button.test.js.snap
│ │ │ └── button.test.js
│ │ ├── Button.jsx
│ │ └── styles.module.css
│ ├── Spinner
│ │ ├── test
│ │ │ ├── __snapshots__
│ │ │ │ └── spinner.test.js.snap
│ │ │ └── spinner.test.js
│ │ ├── Spinner.jsx
│ │ └── styles.module.css
│ ├── Tooltip
│ │ ├── test
│ │ │ ├── __snapshots__
│ │ │ │ └── tooltip.test.js.snap
│ │ │ └── tooltip.test.js
│ │ ├── styles.module.css
│ │ └── Tooltip.jsx
│ ├── StatusTag
│ │ ├── negative.svg
│ │ ├── negative_blue.svg
│ │ ├── test
│ │ │ ├── __snapshots__
│ │ │ │ └── statusTag.test.js.snap
│ │ │ └── statusTag.test.js
│ │ ├── waiting.svg
│ │ ├── green_check.svg
│ │ ├── time_yellow.svg
│ │ ├── styles.module.css
│ │ ├── negative_circle.svg
│ │ └── StatusTag.jsx
│ ├── Toggle
│ │ ├── tests
│ │ │ ├── __snapshots__
│ │ │ │ └── toggle.test.js.snap
│ │ │ └── toggle.test.js
│ │ ├── Toggle.jsx
│ │ └── styles.module.css
│ ├── Layout
│ │ ├── StaticContainer.jsx
│ │ ├── Row.jsx
│ │ ├── Sidebar.jsx
│ │ ├── Header.jsx
│ │ ├── TopBanner.jsx
│ │ ├── PageDetails.jsx
│ │ ├── Main.jsx
│ │ ├── Column.jsx
│ │ └── Container.jsx
│ ├── TextInput
│ │ └── test
│ │ │ ├── __snapshots__
│ │ │ └── textInput.test.js.snap
│ │ │ └── textInput.test.js
│ ├── Badge
│ │ ├── tests
│ │ │ ├── __snapshots__
│ │ │ │ └── badge.test.js.snap
│ │ │ └── badge.test.js
│ │ ├── styles.module.css
│ │ └── Badge.jsx
│ ├── Select
│ │ ├── styles.module.css
│ │ └── test
│ │ │ ├── select.test.js
│ │ │ └── __snapshots__
│ │ │ └── select.test.js.snap
│ ├── Modal
│ │ ├── tests
│ │ │ └── modal.test.js
│ │ ├── ModalWrapper.jsx
│ │ └── styles.module.css
│ ├── Dropdown
│ │ ├── tests
│ │ │ ├── __snapshots__
│ │ │ │ └── dropdown.test.js.snap
│ │ │ └── dropdown.test.js
│ │ ├── DropdownItem.jsx
│ │ └── styles.module.css
│ ├── TextArea
│ │ ├── test
│ │ │ ├── textArea.test.js
│ │ │ └── __snapshots__
│ │ │ │ └── textArea.test.js.snap
│ │ ├── styles.module.css
│ │ └── TextArea.jsx
│ ├── Slider
│ │ ├── helpers.js
│ │ ├── tests
│ │ │ ├── slider.test.js
│ │ │ └── __snapshots__
│ │ │ │ └── slider.test.js.snap
│ │ ├── styles.module.css
│ │ └── SliderHandle.jsx
│ ├── Paginator
│ │ └── styles.module.css
│ ├── Datepicker
│ │ ├── DatepickerPad.module.css
│ │ └── test
│ │ │ ├── __snapshots__
│ │ │ └── datepicker.test.js.snap
│ │ │ └── datepicker.test.js
│ ├── ButtonIcon
│ │ └── styles.module.css
│ ├── BoxTextInput
│ │ ├── styles.module.css
│ │ └── tests
│ │ │ ├── boxTextInput.test.js
│ │ │ └── __snapshots__
│ │ │ └── boxTextInput.test.js.snap
│ ├── Tabs
│ │ ├── tests
│ │ │ ├── __snapshots__
│ │ │ │ └── tabs.test.js.snap
│ │ │ └── tabs.test.js
│ │ └── styles.module.css
│ └── RadioButtonGroup
│ │ ├── tests
│ │ └── radiobuttongroup.test.js
│ │ └── styles.module.css
├── assets
│ └── fonts
│ │ └── source-sans-pro
│ │ ├── SourceSansPro-Light.ttf
│ │ ├── SourceSansPro-Regular.ttf
│ │ └── SourceSansPro-SemiBold.ttf
├── stories
│ ├── buttonicon.module.css
│ ├── page.css
│ ├── Description.stories.jsx
│ ├── Badge.stories.jsx
│ ├── Checkbox.stories.jsx
│ ├── Spinner.stories.jsx
│ ├── NumberInput.stories.jsx
│ ├── Toggle.stories.jsx
│ ├── DarkLightToggle.stories.jsx
│ ├── TextArea.stories.jsx
│ ├── Icon.stories.jsx
│ ├── Paginator.stories.jsx
│ ├── Dropdown.stories.jsx
│ ├── CopyableText.stories.jsx
│ ├── DatePickerV2.stories.jsx
│ ├── Card.stories.jsx
│ ├── Link.stories.jsx
│ ├── Message.stories.jsx
│ ├── DatePicker.stories.jsx
│ ├── Tooltip.stories.jsx
│ ├── StatusTag.stories.jsx
│ ├── assets
│ │ ├── direction.svg
│ │ ├── flow.svg
│ │ ├── code-brackets.svg
│ │ ├── comments.svg
│ │ ├── repo.svg
│ │ ├── plugin.svg
│ │ └── stackalt.svg
│ ├── BoxTextInput.stories.jsx
│ ├── Button.stories.jsx
│ ├── TextInput.stories.jsx
│ ├── RadioButton.stories.jsx
│ ├── Select.stories.jsx
│ ├── ButtonIcon.stories.jsx
│ ├── Introduction.stories.mdx
│ ├── StatusBar.stories.jsx
│ └── Typography.stories.jsx
├── theme
│ ├── index.js
│ ├── constants.js
│ ├── defaultTheme.js
│ ├── helpers.js
│ └── ThemeProvider.js
├── hooks
│ ├── usePrevious.js
│ ├── index.js
│ ├── useMountEffect.js
│ ├── useClickOutside.js
│ ├── useLockBodyScrollOnTrue.js
│ ├── useKeyPress.js
│ ├── useHover.js
│ └── useTruncate.js
├── css
│ └── exports.css
├── utils.js
└── utils.test.js
├── .eslintrc
├── .prettierrc
├── postcss.config.js
├── babel.config.js
├── .gitignore
├── jest.setup.js
├── .storybook
├── themeDecorator.js
├── main.js
├── preview.js
└── providerFn.js
├── .github
└── workflows
│ ├── deploy.yml
│ └── nodejs.yml
├── .stylelintrc
├── rollup.config.js
└── jest.config.js
/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/__mocks__/fileMock.js:
--------------------------------------------------------------------------------
1 | export default "";
2 |
--------------------------------------------------------------------------------
/src/components/Typography/Text/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Text.jsx";
2 |
--------------------------------------------------------------------------------
/src/components/DarkLightToggle/styles.module.css:
--------------------------------------------------------------------------------
1 | .darkLightToggle {
2 | cursor: pointer;
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["react-app", "react-app/jest", "prettier"],
3 | "rules": {
4 | "eqeqeq": 0
5 | }
6 | }
--------------------------------------------------------------------------------
/src/assets/fonts/source-sans-pro/SourceSansPro-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/pi-ui/HEAD/src/assets/fonts/source-sans-pro/SourceSansPro-Light.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/source-sans-pro/SourceSansPro-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/pi-ui/HEAD/src/assets/fonts/source-sans-pro/SourceSansPro-Regular.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/source-sans-pro/SourceSansPro-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/pi-ui/HEAD/src/assets/fonts/source-sans-pro/SourceSansPro-SemiBold.ttf
--------------------------------------------------------------------------------
/src/components/NumberInput/styles.module.css:
--------------------------------------------------------------------------------
1 | .numberInputWrapper {
2 | width: 4rem;
3 | display: inline-block;
4 | }
5 |
6 | .numberInput {
7 | text-align: center;
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/Link/Link.test.js:
--------------------------------------------------------------------------------
1 | import Link from "./Link.jsx";
2 |
3 | describe("Link Component", () => {
4 | it("is truthy", () => {
5 | expect(Link).toBeTruthy();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/src/components/Table/Table.test.js:
--------------------------------------------------------------------------------
1 | import Table from "./Table.jsx";
2 |
3 | describe("Table Component", () => {
4 | it("is truthy", () => {
5 | expect(Table).toBeTruthy();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/src/components/Message/Message.test.js:
--------------------------------------------------------------------------------
1 | import Message from "./Message.jsx";
2 |
3 | describe("Message Component", () => {
4 | it("is truthy", () => {
5 | expect(Message).toBeTruthy();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/src/stories/buttonicon.module.css:
--------------------------------------------------------------------------------
1 | .blueButtonIcon {
2 | background: var(--color-primary) !important;
3 | }
4 |
5 | .darkBlueButtonIcon {
6 | background: var(--paginator-button-color) !important;
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/Checkbox/Checkbox.test.js:
--------------------------------------------------------------------------------
1 | import Checkbox from "./Checkbox.jsx";
2 |
3 | describe("Checkbox Component", () => {
4 | it("is truthy", () => {
5 | expect(Checkbox).toBeTruthy();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/src/components/StatusBar/StatusBar.test.js:
--------------------------------------------------------------------------------
1 | import StatusBar from "./StatusBar.jsx";
2 |
3 | describe("StatusBar Component", () => {
4 | it("is truthy", () => {
5 | expect(StatusBar).toBeTruthy();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/src/components/TextHighlighted/styles.module.css:
--------------------------------------------------------------------------------
1 | .contentWrapper {
2 | padding: 0.3rem 1rem;
3 | word-break: break-all;
4 | background-color: var(--copyable-text-background-color);
5 | border-radius: 0.4rem;
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/Description/styles.module.css:
--------------------------------------------------------------------------------
1 | .description {
2 | margin-left: 2.5rem;
3 | margin-top: 0.5rem;
4 | color: var(--text-secondary-color);
5 | font-size: var(--font-size-small);
6 | font-style: italic;
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/Link/test/__snapshots__/Link.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Link component Matches the snapshot 1`] = `
4 |
7 | test
8 |
9 | `;
10 |
--------------------------------------------------------------------------------
/src/stories/page.css:
--------------------------------------------------------------------------------
1 | body.sb-show-main {
2 | background: transparent !important;
3 | }
4 |
5 | #story--design-system-components-tabs--slide-animation {
6 | position: relative;
7 | overflow-x: hidden;
8 | height: 5rem;
9 | }
10 |
--------------------------------------------------------------------------------
/src/theme/index.js:
--------------------------------------------------------------------------------
1 | export { default as defaultDarkTheme } from "./darkTheme";
2 | export { default as defaultLightTheme } from "./lightTheme";
3 | export * from "./ThemeProvider";
4 | export * from "./helpers";
5 | export * from "./constants";
6 |
--------------------------------------------------------------------------------
/src/components/Card/tests/__snapshots__/card.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Card component Matches the snapshot 1`] = `
4 |
7 | test
8 |
9 | `;
10 |
--------------------------------------------------------------------------------
/src/theme/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | * The following constants are used throughout the application to determine
3 | * if the current active theme is default light or dark theme
4 | */
5 | export const DEFAULT_LIGHT_THEME_NAME = "light";
6 | export const DEFAULT_DARK_THEME_NAME = "dark";
7 |
--------------------------------------------------------------------------------
/src/hooks/usePrevious.js:
--------------------------------------------------------------------------------
1 | import { useRef, useEffect } from "react";
2 |
3 | function usePrevious(value) {
4 | const ref = useRef();
5 | useEffect(() => {
6 | ref.current = value;
7 | }, [value]);
8 | return ref.current;
9 | }
10 |
11 | export default usePrevious;
12 |
--------------------------------------------------------------------------------
/src/components/CopyableText/helpers.js:
--------------------------------------------------------------------------------
1 | export const copyToClipboard = (str) => {
2 | const el = document.createElement("textarea");
3 | el.value = str;
4 | document.body.appendChild(el);
5 | el.select();
6 | document.execCommand("copy");
7 | document.body.removeChild(el);
8 | };
9 |
--------------------------------------------------------------------------------
/src/theme/defaultTheme.js:
--------------------------------------------------------------------------------
1 | import lightTheme from "./lightTheme";
2 |
3 | function applyDefaultTheme() {
4 | Object.keys(lightTheme).forEach((key) => {
5 | document.documentElement.style.setProperty(`--${key}`, lightTheme[key]);
6 | });
7 | }
8 |
9 | applyDefaultTheme();
10 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": true,
6 | "singleQuote": false,
7 | "trailingComma": "es5",
8 | "bracketSpacing": true,
9 | "bracketSameLine": true,
10 | "arrowParens": "always",
11 | "endOfLine": "auto"
12 | }
13 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | const postcssPresetEnv = require("postcss-preset-env");
2 |
3 | module.exports = {
4 | plugins: [
5 | postcssPresetEnv({
6 | importFrom: "src/css/exports.css",
7 | features: {
8 | "nesting-rules": true,
9 | },
10 | }),
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/src/components/Button/tests/__snapshots__/button.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Button component Matches the snapshot 1`] = `
4 |
11 | `;
12 |
--------------------------------------------------------------------------------
/src/components/CopyableText/styles.module.css:
--------------------------------------------------------------------------------
1 | .copyableWrapper {
2 | display: flex;
3 | align-items: center;
4 | flex-wrap: nowrap;
5 | max-width: 100%;
6 | }
7 |
8 | .copyToClipboard {
9 | width: 1.4rem;
10 | height: 1.4rem;
11 | cursor: pointer;
12 | margin-left: 5px;
13 | user-select: none;
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/Spinner/test/__snapshots__/spinner.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Spinner component Matches the snapshot 1`] = `
4 |
13 | `;
14 |
--------------------------------------------------------------------------------
/src/components/Link/test/Link.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Link from "../Link";
3 | import { create } from "react-test-renderer";
4 |
5 | describe("Link component", () => {
6 | test("Matches the snapshot", () => {
7 | const link = create(test);
8 | expect(link.toJSON()).toMatchSnapshot();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/src/components/TextHighlighted/tests/__snapshots__/texthighlighted.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`TextHighlighted component Matches snapshot 1`] = `
4 |
8 | test
9 |
10 | `;
11 |
--------------------------------------------------------------------------------
/src/components/Tooltip/test/__snapshots__/tooltip.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Tooltip component Matches the snapshot 1`] = `
4 |
8 |
11 | content
12 |
13 |
14 | `;
15 |
--------------------------------------------------------------------------------
/src/components/Card/tests/card.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Card from "../Card";
3 | import { create } from "react-test-renderer";
4 |
5 | describe("Card component", () => {
6 | test("Matches the snapshot", () => {
7 | const card = create(test);
8 | expect(card.toJSON()).toMatchSnapshot();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/src/components/StatusTag/negative.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/StatusTag/negative_blue.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Spinner/test/spinner.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Spinner from "../Spinner";
3 | import { create } from "react-test-renderer";
4 |
5 | describe("Spinner component", () => {
6 | test("Matches the snapshot", () => {
7 | const spinner = create();
8 | expect(spinner.toJSON()).toMatchSnapshot();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/src/components/Tooltip/test/tooltip.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Tooltip from "../Tooltip";
3 | import { create } from "react-test-renderer";
4 |
5 | describe("Tooltip component", () => {
6 | test("Matches the snapshot", () => {
7 | const tooltip = create();
8 | expect(tooltip.toJSON()).toMatchSnapshot();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/src/components/Toggle/tests/__snapshots__/toggle.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Toggle Component Matches snapshot 1`] = `
4 |
16 | `;
17 |
--------------------------------------------------------------------------------
/src/components/StatusTag/test/__snapshots__/statusTag.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`StatusTag component Matches the snapshot 1`] = `
4 |
7 |
11 |
12 | grayNegative
13 |
14 |
15 | `;
16 |
--------------------------------------------------------------------------------
/src/components/StatusTag/test/statusTag.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import StatusTag from "../StatusTag";
3 | import { create } from "react-test-renderer";
4 |
5 | describe("StatusTag component", () => {
6 | test("Matches the snapshot", () => {
7 | const statusTag = create();
8 | expect(statusTag.toJSON()).toMatchSnapshot();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/src/components/StatusTag/waiting.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Checkbox/tests/checkbox.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Checkbox from "../Checkbox";
3 | import { create } from "react-test-renderer";
4 |
5 | describe("Checkbox component", () => {
6 | test("Matches the snapshot", () => {
7 | const checkbox = create();
8 | expect(checkbox.toJSON()).toMatchSnapshot();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/src/components/Layout/StaticContainer.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { classNames } from "../../utils";
3 | import styles from "./styles.module.css";
4 |
5 | const StaticContainer = ({ children, className, style }) => (
6 |
7 | {children}
8 |
9 | );
10 |
11 | export default StaticContainer;
12 |
--------------------------------------------------------------------------------
/src/components/StatusTag/green_check.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Checkbox/tests/__snapshots__/checkbox.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Checkbox component Matches the snapshot 1`] = `
4 |
19 | `;
20 |
--------------------------------------------------------------------------------
/src/components/TextInput/test/__snapshots__/textInput.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`TextInput component Matches the snapshot 1`] = `
4 |
18 | `;
19 |
--------------------------------------------------------------------------------
/src/components/Badge/tests/__snapshots__/badge.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Badge component Matches the snapshot 1`] = `
4 |
7 |
14 |
17 | test
18 |
19 | `;
20 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ["@babel/preset-env", "@babel/preset-react"],
3 | plugins: [
4 | ["@babel/plugin-proposal-class-properties", { "loose": true }],
5 | ["@babel/plugin-proposal-private-methods", { "loose": true }],
6 | ["@babel/plugin-proposal-private-property-in-object", { "loose": true }]
7 | ],
8 | env: {
9 | test: {
10 | plugins: ["@babel/plugin-transform-runtime"],
11 | },
12 | },
13 | };
14 |
--------------------------------------------------------------------------------
/src/hooks/index.js:
--------------------------------------------------------------------------------
1 | export { default as useTruncate } from "./useTruncate";
2 | export { default as useMediaQuery } from "./useMediaQuery";
3 | export { default as useHover } from "./useHover";
4 | export { default as useClickOutside } from "./useClickOutside";
5 | export { default as useLockBodyScrollOnTrue } from "./useLockBodyScrollOnTrue";
6 | export { default as useMountEffect } from "./useMountEffect";
7 | export { default as usePrevious } from "./usePrevious";
8 |
--------------------------------------------------------------------------------
/src/stories/Description.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Description } from "..";
3 | import "./page.css";
4 |
5 | const DescriptionObj = {
6 | title: "Design System/Components/Description",
7 | component: Description,
8 | };
9 |
10 | export default DescriptionObj;
11 |
12 | const Template = ({ ...args }) => ;
13 |
14 | export const Basic = Template.bind({});
15 | Basic.args = {
16 | body: "Test Description",
17 | };
18 |
--------------------------------------------------------------------------------
/src/stories/Badge.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Badge } from "..";
3 | import "./page.css";
4 |
5 | const BadgeObj = {
6 | title: "Design System/Components/Badge",
7 | component: Badge,
8 | };
9 |
10 | export default BadgeObj;
11 |
12 | const Template = ({ children, ...args }) => {children};
13 |
14 | export const Basic = Template.bind({});
15 | Basic.args = {
16 | children: "Test Badge",
17 | show: true,
18 | };
19 |
--------------------------------------------------------------------------------
/src/components/NumberInput/tests/__snapshots__/numberInput.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`NumberInput component Matches the snapshot 1`] = `
4 |
19 | `;
20 |
--------------------------------------------------------------------------------
/src/stories/Checkbox.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Checkbox } from "..";
3 | import "./page.css";
4 |
5 | const CheckboxObj = {
6 | title: "Design System/Components/Checkbox",
7 | component: Checkbox,
8 | };
9 |
10 | export default CheckboxObj;
11 |
12 | const Template = ({ children, ...args }) => ;
13 |
14 | export const Basic = Template.bind({});
15 | Basic.args = {
16 | id: "test-checkbox",
17 | label: "test",
18 | description: "yoyo!",
19 | };
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # IDEs
5 | .idea
6 |
7 | # dependencies
8 | node_modules
9 |
10 | # builds
11 | build
12 | dist
13 | .rpt2_cache
14 |
15 | # misc
16 | .DS_Store
17 | .env
18 | .env.local
19 | .env.development.local
20 | .env.test.local
21 | .env.production.local
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | #storybook
28 | storybook-static
29 |
30 | #docz
31 | .docz
32 |
33 | #bundle stats
34 | stats.html
--------------------------------------------------------------------------------
/src/components/Link/styles.module.css:
--------------------------------------------------------------------------------
1 | .link {
2 | color: var(--link-color);
3 | font-weight: var(--font-weight-regular);
4 | }
5 |
6 | .gray {
7 | color: var(--color-gray);
8 | }
9 |
10 | .dark {
11 | color: var(--color-primary-dark);
12 | }
13 |
14 | .link:hover {
15 | text-decoration: underline;
16 | }
17 |
18 | .noHover:hover {
19 | text-decoration: none;
20 | }
21 |
22 | .truncate {
23 | display: block;
24 | white-space: nowrap;
25 | overflow: hidden;
26 | text-overflow: ellipsis;
27 | }
28 |
--------------------------------------------------------------------------------
/src/stories/Spinner.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Spinner } from "..";
3 | import "./page.css";
4 |
5 | const SpinnerObj = {
6 | title: "Design System/Components/Spinner",
7 | component: Spinner,
8 | };
9 |
10 | export default SpinnerObj;
11 |
12 | const Template = ({ children, ...args }) => ;
13 |
14 | export const Basic = Template.bind({});
15 | Basic.args = {};
16 |
17 | export const Inverted = Template.bind({});
18 | Inverted.args = {
19 | invert: true,
20 | };
21 |
--------------------------------------------------------------------------------
/jest.setup.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | /**
4 | * This code silences the warnings about the forwardRef update inside a test. TODO: improve tests and get rid of the warnings
5 | */
6 | const consoleError = console.error;
7 | beforeAll(() => {
8 | jest.spyOn(console, "error").mockImplementation((...args) => {
9 | if (
10 | !args[0].includes(
11 | "Warning: An update to %s inside a test was not wrapped in act"
12 | )
13 | ) {
14 | consoleError(...args);
15 | }
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/components/Spinner/Spinner.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styles from "./styles.module.css";
3 | import PropTypes from "prop-types";
4 |
5 | const Spinner = ({ invert, width, height }) => {
6 | return (
7 |
11 | );
12 | };
13 |
14 | Spinner.propTypes = {
15 | width: PropTypes.string,
16 | height: PropTypes.string,
17 | invert: PropTypes.bool,
18 | };
19 |
20 | export default Spinner;
21 |
--------------------------------------------------------------------------------
/src/components/Card/styles.module.css:
--------------------------------------------------------------------------------
1 | .card {
2 | background: var(--card-background);
3 | position: relative;
4 | }
5 |
6 | .padding-small {
7 | padding: 2rem 4rem;
8 | }
9 |
10 | .padding-medium {
11 | padding: 4rem 6rem;
12 | }
13 |
14 | .padding-large {
15 | padding: 6rem 9rem;
16 | }
17 |
18 | .marker {
19 | border-left: 1.2rem solid;
20 | border-top: 1.2rem solid;
21 | border-right: 1.2rem solid transparent;
22 | border-bottom: 1.2rem solid transparent;
23 | position: absolute;
24 | top: 0;
25 | left: 0;
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/Select/styles.module.css:
--------------------------------------------------------------------------------
1 | .arrowAnchor {
2 | position: relative;
3 | height: 100%;
4 | width: 2rem;
5 | }
6 |
7 | .arrowAnchor:after {
8 | content: "";
9 | width: 0;
10 | height: 0;
11 |
12 | border: 0.5rem solid transparent;
13 | border-color: var(--select-anchor-color) transparent transparent transparent;
14 | position: absolute;
15 | top: -0.3rem;
16 | right: 0.3rem;
17 | }
18 |
19 | .arrowAnchorOpen:after {
20 | top: -0.9rem;
21 | border-color: transparent transparent var(--color-primary) transparent;
22 | }
23 |
--------------------------------------------------------------------------------
/src/theme/helpers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Returns the final value of a given theme key
3 | * @param {Object} theme
4 | * @param {String} key
5 | */
6 | export const getThemeProperty = (theme, key) => {
7 | if (!theme[key]) {
8 | return new Error("Please, provide a valid color key!");
9 | }
10 | const value = theme[key];
11 | const regex = /^var\(--(.*)\)/gm;
12 | const match = regex.exec(value);
13 |
14 | const isVariable = match && match.length > 0;
15 | if (isVariable) {
16 | return getThemeProperty(theme, match[1]);
17 | }
18 |
19 | return value;
20 | };
21 |
--------------------------------------------------------------------------------
/src/components/Layout/Row.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | function Row({ className, style, children, ...props }) {
7 | return (
8 |
9 | {children}
10 |
11 | );
12 | }
13 |
14 | Row.propTypes = {
15 | children: PropTypes.node.isRequired,
16 | style: PropTypes.object,
17 | className: PropTypes.string,
18 | };
19 |
20 | export default Row;
21 |
--------------------------------------------------------------------------------
/src/components/Description/Description.jsx:
--------------------------------------------------------------------------------
1 | import { PropTypes } from "prop-types";
2 | import React from "react";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const Description = ({ body, style, className }) =>
7 | body ? (
8 |
9 | {body}
10 |
11 | ) : null;
12 |
13 | Description.propTypes = {
14 | body: PropTypes.node,
15 | style: PropTypes.object,
16 | className: PropTypes.string,
17 | };
18 |
19 | export default Description;
20 |
--------------------------------------------------------------------------------
/.storybook/themeDecorator.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | ThemeProvider,
4 | DEFAULT_DARK_THEME_NAME,
5 | DEFAULT_LIGHT_THEME_NAME,
6 | defaultDarkTheme,
7 | defaultLightTheme,
8 | } from "../src/theme";
9 |
10 | const themes = {
11 | [DEFAULT_DARK_THEME_NAME]: defaultDarkTheme,
12 | [DEFAULT_LIGHT_THEME_NAME]: defaultLightTheme,
13 | };
14 |
15 | const ThemeDecorator = (storyFn) => (
16 |
17 | {storyFn()}
18 |
19 | );
20 |
21 | export default ThemeDecorator;
22 |
--------------------------------------------------------------------------------
/src/components/Modal/tests/modal.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { render, fireEvent, screen } from "@testing-library/react";
3 | import Modal from "../Modal";
4 |
5 | describe("Modal Component", () => {
6 | test("Open and close", () => {
7 | const mockOnClose = jest.fn();
8 | render(
9 |
10 | test
11 |
12 | );
13 | expect(screen.queryByText(/test/i)).toBeTruthy();
14 |
15 | fireEvent.click(screen.getByTestId("close"));
16 |
17 | expect(mockOnClose).toBeCalled();
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/src/css/exports.css:
--------------------------------------------------------------------------------
1 | @custom-media --xs-viewport screen and (max-width: 560px);
2 | @custom-media --sm-viewport screen and (max-width: 768px);
3 | @custom-media --md-viewport screen and (max-width: 1000px);
4 | @custom-media --bg-viewport screen and (max-width: 1180px);
5 | @custom-media --lg-viewport screen and (max-width: 1400px);
6 | @custom-media --xl-viewport screen and (min-width: 1401px);
7 | @custom-media --bigger-than-xs-viewport screen and (min-width: 561px);
8 | @custom-media --bigger-than-sm-viewport screen and (min-width: 769px);
9 | @custom-media --bigger-than-md-viewport screen and (min-width: 1001px);
10 |
--------------------------------------------------------------------------------
/src/components/Layout/Sidebar.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const Sidebar = ({ children, style, className, ...props }) => {
7 | return (
8 |
12 | {children}
13 |
14 | );
15 | };
16 |
17 | Sidebar.propTypes = {
18 | children: PropTypes.node,
19 | style: PropTypes.object,
20 | className: PropTypes.string,
21 | };
22 |
23 | export default Sidebar;
24 |
--------------------------------------------------------------------------------
/src/stories/NumberInput.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { NumberInput } from "..";
3 | import "./page.css";
4 |
5 | const NumberInputObj = {
6 | title: "Design System/Components/NumberInput",
7 | component: NumberInput,
8 | };
9 |
10 | export default NumberInputObj;
11 |
12 | const Template = ({ children, ...args }) => ;
13 |
14 | export const Basic = Template.bind({});
15 | Basic.args = {
16 | id: "test-number-input",
17 | };
18 | export const Error = Template.bind({});
19 | Error.args = {
20 | id: "test-number-input2",
21 | error: "Value must be lass than 20",
22 | };
23 |
--------------------------------------------------------------------------------
/src/components/Dropdown/tests/__snapshots__/dropdown.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Dropdown component Matches snapshot 1`] = `
4 |
12 |
17 |
20 | test
21 |
22 |
26 |
27 |
28 | `;
29 |
--------------------------------------------------------------------------------
/src/components/Layout/Header.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const Header = ({ children, style, className, ...props }) => {
7 | return (
8 |
12 | {children}
13 |
14 | );
15 | };
16 |
17 | Header.propTypes = {
18 | children: PropTypes.node.isRequired,
19 | style: PropTypes.object,
20 | className: PropTypes.string,
21 | };
22 |
23 | export default Header;
24 |
--------------------------------------------------------------------------------
/src/components/Layout/TopBanner.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const TopBanner = ({ children, style, className, ...props }) => {
7 | return (
8 |
12 | {children}
13 |
14 | );
15 | };
16 |
17 | TopBanner.propTypes = {
18 | children: PropTypes.node,
19 | style: PropTypes.object,
20 | className: PropTypes.string,
21 | };
22 |
23 | export default TopBanner;
24 |
--------------------------------------------------------------------------------
/src/components/Layout/PageDetails.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const PageDetails = ({ children, style, className, ...props }) => {
7 | return (
8 |
12 | {children}
13 |
14 | );
15 | };
16 |
17 | PageDetails.propTypes = {
18 | children: PropTypes.node,
19 | style: PropTypes.object,
20 | className: PropTypes.string,
21 | };
22 |
23 | export default PageDetails;
24 |
--------------------------------------------------------------------------------
/src/stories/Toggle.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Toggle } from "..";
3 | import "./page.css";
4 |
5 | const ToggleObj = {
6 | title: "Design System/Components/Toggle",
7 | component: Toggle,
8 | };
9 |
10 | export default ToggleObj;
11 |
12 | const Template = ({ children, ...args }) => ;
13 |
14 | export const Basic = Template.bind({});
15 | Basic.args = {
16 | toggled: false,
17 | };
18 |
19 | export const Toggled = Template.bind({});
20 | Toggled.args = {
21 | toggled: true,
22 | };
23 |
24 | export const Disabled = Template.bind({});
25 | Disabled.args = {
26 | disabled: true,
27 | };
28 |
--------------------------------------------------------------------------------
/src/components/NumberInput/NumberInput.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import TextInput from "../TextInput/TextInput.jsx";
4 | import styles from "./styles.module.css";
5 |
6 | const NumberInput = ({ ...props }) => {
7 | return (
8 |
15 | );
16 | };
17 |
18 | NumberInput.propTypes = {
19 | min: PropTypes.number,
20 | max: PropTypes.number,
21 | style: PropTypes.object,
22 | };
23 |
24 | export default NumberInput;
25 |
--------------------------------------------------------------------------------
/src/components/Layout/Main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const Main = ({ children, style, className, fill, ...props }) => {
7 | return (
8 |
12 | {children}
13 |
14 | );
15 | };
16 |
17 | Main.propTypes = {
18 | children: PropTypes.node,
19 | style: PropTypes.object,
20 | fill: PropTypes.bool,
21 | className: PropTypes.string,
22 | };
23 |
24 | export default Main;
25 |
--------------------------------------------------------------------------------
/src/components/Message/test/message.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Message from "../Message";
3 | import { create } from "react-test-renderer";
4 | import {
5 | defaultLightTheme,
6 | ThemeProvider,
7 | DEFAULT_LIGHT_THEME_NAME,
8 | } from "../../../theme";
9 |
10 | describe("Message component", () => {
11 | test("Matches the snapshot", () => {
12 | const message = create(
13 |
16 | test
17 |
18 | );
19 | expect(message.toJSON()).toMatchSnapshot();
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/components/StatusTag/time_yellow.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/components/TextArea/test/textArea.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import TextArea from "../TextArea";
3 | import { create } from "react-test-renderer";
4 | import {
5 | defaultLightTheme,
6 | ThemeProvider,
7 | DEFAULT_LIGHT_THEME_NAME,
8 | } from "../../../theme";
9 |
10 | describe("TextArea component", () => {
11 | test("Matches the snapshot", () => {
12 | const textArea = create(
13 |
16 |
17 |
18 | );
19 | expect(textArea.toJSON()).toMatchSnapshot();
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/components/Spinner/styles.module.css:
--------------------------------------------------------------------------------
1 | .spinner {
2 | display: inline-block;
3 | width: 1.8rem;
4 | height: 1.8rem;
5 | border: 0.2rem solid rgba(255, 255, 255, 0.3);
6 | border-radius: 50%;
7 | border-top-color: var(--color-white);
8 | animation: spin 1s ease-in-out infinite;
9 | -webkit-animation: spin 1s ease-in-out infinite;
10 | }
11 |
12 | .spinnerInvert {
13 | composes: spinner;
14 | border: 0.2rem solid rgba(9, 20, 64, 0.3);
15 | border-top-color: var(--color-primary-dark);
16 | }
17 |
18 | @keyframes spin {
19 | to {
20 | -webkit-transform: rotate(360deg);
21 | }
22 | }
23 | @-webkit-keyframes spin {
24 | to {
25 | -webkit-transform: rotate(360deg);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/TextInput/test/textInput.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import TextInput from "../TextInput";
3 | import { create } from "react-test-renderer";
4 | import {
5 | defaultLightTheme,
6 | ThemeProvider,
7 | DEFAULT_LIGHT_THEME_NAME,
8 | } from "../../../theme";
9 |
10 | describe("TextInput component", () => {
11 | test("Matches the snapshot", () => {
12 | const textInput = create(
13 |
16 |
17 |
18 | );
19 | expect(textInput.toJSON()).toMatchSnapshot();
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/stories/DarkLightToggle.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { DarkLightToggle } from "..";
3 | import "./page.css";
4 |
5 | const DarkLightToggleObj = {
6 | title: "Design System/Components/DarkLightToggle",
7 | component: DarkLightToggle,
8 | };
9 |
10 | export default DarkLightToggleObj;
11 |
12 | const Template = ({ children, ...args }) => (
13 | {children}
14 | );
15 |
16 | export const Basic = Template.bind({});
17 | Basic.args = {
18 | children: "Test DarkLightToggle",
19 | toggled: false,
20 | };
21 |
22 | export const Toggled = Template.bind({});
23 | Toggled.args = {
24 | children: "Test DarkLightToggle",
25 | toggled: true,
26 | };
27 |
--------------------------------------------------------------------------------
/src/components/NumberInput/tests/numberInput.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import NumberInput from "../NumberInput";
3 | import { create } from "react-test-renderer";
4 | import {
5 | defaultLightTheme,
6 | ThemeProvider,
7 | DEFAULT_LIGHT_THEME_NAME,
8 | } from "../../../theme";
9 |
10 | describe("NumberInput component", () => {
11 | test("Matches the snapshot", () => {
12 | const numberInput = create(
13 |
16 |
17 |
18 | );
19 | expect(numberInput.toJSON()).toMatchSnapshot();
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/stories/TextArea.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { TextArea } from "..";
3 | import "./page.css";
4 |
5 | const TextAreaObj = {
6 | title: "Design System/Components/TextArea",
7 | component: TextArea,
8 | };
9 |
10 | export default TextAreaObj;
11 |
12 | const Template = ({ children, ...args }) => ;
13 |
14 | export const Basic = Template.bind({});
15 | Basic.args = {
16 | id: "test-statement",
17 | placeholder: "Statement",
18 | name: "test-statement",
19 | error: "",
20 | };
21 |
22 | export const Error = Template.bind({});
23 | Error.args = {
24 | id: "test-statement2",
25 | placeholder: "Statement",
26 | name: "test-statement2",
27 | error: "Invalid statement",
28 | };
29 |
--------------------------------------------------------------------------------
/src/components/CopyableText/tests/copyableText.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import CopyableText from "../CopyableText";
3 | import { create } from "react-test-renderer";
4 | import {
5 | defaultLightTheme,
6 | ThemeProvider,
7 | DEFAULT_LIGHT_THEME_NAME,
8 | } from "../../../theme";
9 |
10 | describe("CopyableText component", () => {
11 | test("Matches snapshot", () => {
12 | const copyableText = create(
13 |
16 | test
17 |
18 | );
19 | expect(copyableText.toJSON()).toMatchSnapshot();
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy
2 | on:
3 | push:
4 | branches:
5 | - master
6 |
7 | jobs:
8 | build-and-deploy:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@master
13 |
14 | - name: Setup node
15 | uses: actions/setup-node@v2
16 | with:
17 | node-version: "14"
18 |
19 | - name: Install and Build
20 | run: |
21 | yarn
22 | yarn build-storybook
23 |
24 | - name: Deploy
25 | uses: JamesIves/github-pages-deploy-action@3.7.1
26 | with:
27 | BRANCH: gh-pages
28 | FOLDER: storybook-static
29 | ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
30 | CLEAN: true
31 |
--------------------------------------------------------------------------------
/src/stories/Icon.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Icon } from "..";
3 | import "./page.css";
4 |
5 | const IconObj = {
6 | title: "Design System/Components/Icon",
7 | component: Icon,
8 | };
9 |
10 | export default IconObj;
11 |
12 | const Template = ({ children, ...args }) => ;
13 |
14 | export const Type = Template.bind({});
15 | Type.args = {
16 | type: "search",
17 | };
18 | export const Small = Template.bind({});
19 | Small.args = {
20 | type: "github",
21 | size: "sm",
22 | };
23 | export const Medium = Template.bind({});
24 | Medium.args = {
25 | type: "github",
26 | size: "md",
27 | };
28 | export const Large = Template.bind({});
29 | Large.args = {
30 | type: "github",
31 | size: "lg",
32 | };
33 |
--------------------------------------------------------------------------------
/src/components/TextHighlighted/tests/texthighlighted.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import TextHighlighted from "../TextHighlighted";
3 | import { create } from "react-test-renderer";
4 | import {
5 | defaultLightTheme,
6 | ThemeProvider,
7 | DEFAULT_LIGHT_THEME_NAME,
8 | } from "../../../theme";
9 |
10 | describe("TextHighlighted component", () => {
11 | test("Matches snapshot", () => {
12 | const texthighlighted = create(
13 |
16 | test
17 |
18 | );
19 | expect(texthighlighted.toJSON()).toMatchSnapshot();
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/hooks/useMountEffect.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 |
3 | /**
4 | * This is copied from Decrediton
5 | *
6 | * useMountEffect is a custom hook to run an effect on mount
7 | * only once, it accepts an effect/function and executes it
8 | * only after succesful mount (empty array as second argument
9 | * in useEffect call)
10 | *
11 | * Note: eslint react-hooks/exhaustive-deps is disabled here due to this being
12 | * specifically to run a function on the very first mount, independently of fun
13 | * changing, thus `fun` is not supposed to actually be a dependency.
14 | *
15 | * @param {*} fun - effect to run on mount
16 | */
17 | const useMountEffect = (fun) => useEffect(fun, []); // eslint-disable-line react-hooks/exhaustive-deps
18 |
19 | export default useMountEffect;
20 |
--------------------------------------------------------------------------------
/src/stories/Paginator.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Paginator } from "..";
3 | import "./page.css";
4 |
5 | const PaginatorObj = {
6 | title: "Design System/Components/Paginator",
7 | component: Paginator,
8 | };
9 |
10 | export default PaginatorObj;
11 |
12 | const Template = ({ children, ...args }) => ;
13 |
14 | export const Basic = Template.bind({});
15 | Basic.args = {
16 | pageCount: 20,
17 | };
18 |
19 | export const CustomPaginationGap = Template.bind({});
20 | CustomPaginationGap.args = {
21 | pageCount: 20,
22 | paginationGap: 0,
23 | };
24 |
25 | export const CustomMarginPagesDisplayed = Template.bind({});
26 | CustomMarginPagesDisplayed.args = {
27 | pageCount: 20,
28 | paginationGap: 0,
29 | marginPagesDisplayed: 5,
30 | };
31 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx)"],
3 | addons: [
4 | "@storybook/addon-links",
5 | "@storybook/addon-essentials",
6 | "@storybook/addon-interactions",
7 | "@react-theming/storybook-addon",
8 | {
9 | name: "@storybook/addon-postcss",
10 | options: {
11 | styleLoaderOptions: {},
12 | cssLoaderOptions: {
13 | modules: true,
14 | sourceMap: true,
15 | importLoaders: 1,
16 | },
17 | postcssLoaderOptions: {
18 | sourceMap: true,
19 | implementation: require("postcss"),
20 | },
21 | },
22 | },
23 | ],
24 | framework: "@storybook/react",
25 | core: {
26 | builder: "webpack5",
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/src/stories/Dropdown.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Dropdown, DropdownItem } from "..";
3 | import "./page.css";
4 |
5 | const DropdownObj = {
6 | title: "Design System/Components/Dropdown",
7 | component: Dropdown,
8 | };
9 |
10 | export default DropdownObj;
11 |
12 | const Template = ({ children, ...args }) => (
13 | {children}
14 | );
15 |
16 | export const BasicOpened = Template.bind({});
17 | BasicOpened.args = {
18 | children: test dropdown item,
19 | show: true,
20 | title: "Test dropdown",
21 | };
22 |
23 | export const BasicClosed = Template.bind({});
24 | BasicClosed.args = {
25 | children: test dropdown item,
26 | show: false,
27 | title: "Test dropdown",
28 | };
29 |
--------------------------------------------------------------------------------
/src/hooks/useClickOutside.js:
--------------------------------------------------------------------------------
1 | import { useRef, useCallback, useEffect } from "react";
2 |
3 | export default function useClickOutside(onClickHandler) {
4 | const ref = useRef();
5 | const handleClick = useCallback(
6 | (event) => {
7 | if (ref.current && !ref.current.contains(event.target)) {
8 | onClickHandler();
9 | }
10 | },
11 | [onClickHandler]
12 | );
13 |
14 | useEffect(() => {
15 | // Bind the event listener
16 | document.addEventListener("mousedown", handleClick);
17 | return () => {
18 | // Unbind the event listener on clean up
19 | document.removeEventListener("mousedown", handleClick);
20 | };
21 | });
22 |
23 | const callbackRef = useCallback((node) => {
24 | ref.current = node;
25 | }, []);
26 |
27 | return [callbackRef];
28 | }
29 |
--------------------------------------------------------------------------------
/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import { addDecorator } from "@storybook/react";
2 | import { withThemes } from "@react-theming/storybook-addon";
3 | import {
4 | DEFAULT_DARK_THEME_NAME,
5 | DEFAULT_LIGHT_THEME_NAME,
6 | defaultDarkTheme,
7 | defaultLightTheme,
8 | } from "../src/theme";
9 | import providerFn from "./providerFn";
10 |
11 | const themes = [
12 | { [DEFAULT_LIGHT_THEME_NAME]: defaultLightTheme },
13 | { [DEFAULT_DARK_THEME_NAME]: defaultDarkTheme },
14 | ];
15 |
16 | addDecorator(
17 | withThemes(null, themes, {
18 | providerFn,
19 | })
20 | );
21 |
22 | export const parameters = {
23 | backgrounds: { disable: true },
24 | actions: { argTypesRegex: "^on[A-Z].*" },
25 | controls: {
26 | matchers: {
27 | color: /(background|color)$/i,
28 | date: /Date$/,
29 | },
30 | },
31 | };
32 |
--------------------------------------------------------------------------------
/src/stories/CopyableText.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { CopyableText } from "..";
3 | import "./page.css";
4 |
5 | const CopyableTextObj = {
6 | title: "Design System/Components/CopyableText",
7 | component: CopyableText,
8 | };
9 |
10 | export default CopyableTextObj;
11 |
12 | const Template = ({ children, ...args }) => (
13 | {children}
14 | );
15 |
16 | export const Basic = Template.bind({});
17 | Basic.args = {
18 | children: "Test CopyableText",
19 | id: "copyable-test",
20 | hoverText: "",
21 | };
22 |
23 | export const Truncate = Template.bind({});
24 | Truncate.args = {
25 | children: "Test CopyableText truncateeeeeeeeeeeeeee",
26 | textStyle: { width: "200px" },
27 | truncate: true,
28 | hoverText: "",
29 | id: "copyable-truncate-test",
30 | };
31 |
--------------------------------------------------------------------------------
/src/stories/DatePickerV2.stories.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { DatePickerV2 } from "..";
3 | import "./page.css";
4 |
5 | const DatePickerV2Obj = {
6 | title: "Design System/Components/DatePickerV2",
7 | component: DatePickerV2,
8 | };
9 |
10 | export default DatePickerV2Obj;
11 |
12 | const ControlledTemplate = ({ children, ...args }) => {
13 | const [value, setValue] = useState();
14 |
15 | return ;
16 | };
17 |
18 | export const FullyControlled = ControlledTemplate.bind({});
19 | FullyControlled.args = {};
20 |
21 | const args = {
22 | minTimestamp: Date.now(),
23 | // One Year From now
24 | maxTimestamp: Date.now() + 31536000000,
25 | };
26 |
27 | export const FullyControlledMinMax = ControlledTemplate.bind({});
28 | FullyControlledMinMax.args = args;
29 |
--------------------------------------------------------------------------------
/src/components/Toggle/Toggle.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { classNames } from "../../utils";
4 |
5 | import styles from "./styles.module.css";
6 |
7 | const Toggle = ({ onToggle, toggled, disabled, className }) => {
8 | return (
9 |
18 | );
19 | };
20 |
21 | Toggle.propTypes = {
22 | onToggle: PropTypes.func,
23 | toggled: PropTypes.bool.isRequired,
24 | disabled: PropTypes.bool,
25 | className: PropTypes.string,
26 | };
27 |
28 | export default Toggle;
29 |
--------------------------------------------------------------------------------
/src/components/Dropdown/DropdownItem.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 | import React from "react";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const DropdownItem = ({
7 | handleClose,
8 | onClick,
9 | className,
10 | children,
11 | ...props
12 | }) => {
13 | const handleClick = () => {
14 | handleClose && handleClose();
15 | onClick && onClick();
16 | };
17 | return (
18 |
22 | {children}
23 |
24 | );
25 | };
26 |
27 | DropdownItem.propTypes = {
28 | className: PropTypes.string,
29 | children: PropTypes.node,
30 | handleClose: PropTypes.func,
31 | onClick: PropTypes.func,
32 | style: PropTypes.object,
33 | };
34 |
35 | export default DropdownItem;
36 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | name: Build and Test
2 | on: [push, pull_request]
3 | permissions:
4 | contents: read
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | strategy:
10 | matrix:
11 | node-version: [18.x, 19.x]
12 | fail-fast: false
13 |
14 | steps:
15 | - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 #v3.1.0
16 | - name: Use nodejs ${{ matrix.node-version }}
17 | uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 #v3.5.1
18 | with:
19 | node-version: ${{ matrix.node-version }}
20 | - name: yarn
21 | run: yarn --network-concurrency 1
22 | - name: tests
23 | run: |
24 | yarn test
25 | env:
26 | CI: true
27 | - name: build
28 | run: yarn build
29 | - name: storybook build
30 | run: yarn build-storybook
31 |
--------------------------------------------------------------------------------
/src/stories/Card.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Card } from "..";
3 | import "./page.css";
4 |
5 | const CardObj = {
6 | title: "Design System/Components/Card",
7 | component: Card,
8 | };
9 |
10 | export default CardObj;
11 |
12 | const Template = ({ children, ...args }) => {children};
13 |
14 | export const Basic = Template.bind({});
15 | Basic.args = {
16 | children: "I am a card",
17 | };
18 |
19 | export const WithPadding = () => (
20 | <>
21 | Card with small padding
22 |
23 | Card with medium padding
24 |
25 | Card with large padding
26 | >
27 | );
28 |
29 | export const WithMarker = Template.bind({});
30 | WithMarker.args = {
31 | children: "Card with a marker",
32 | paddingSize: "small",
33 | marker: true,
34 | };
35 |
--------------------------------------------------------------------------------
/src/components/Slider/helpers.js:
--------------------------------------------------------------------------------
1 | export const DIMENSIONS_MAP = {
2 | x: "width",
3 | y: "height",
4 | };
5 |
6 | export const POSITIONS_MAP = {
7 | x: "left",
8 | y: "top",
9 | };
10 |
11 | export const POSITIONS_MAP_CAPITALIZED = {
12 | x: "Left",
13 | y: "Top",
14 | };
15 |
16 | export function getClientPosition(e) {
17 | return e.touches?.length
18 | ? {
19 | x: e.touches[0].clientX,
20 | y: e.touches[0].clientY,
21 | }
22 | : {
23 | x: e.clientX,
24 | y: e.clientY,
25 | };
26 | }
27 |
28 | export function addEventListeners(handleDrag, handleDragEnd) {
29 | document.addEventListener("mousemove", handleDrag);
30 | document.addEventListener("touchmove", handleDrag, { passive: false });
31 | document.addEventListener("mouseup", handleDragEnd);
32 | document.addEventListener("touchend", handleDragEnd);
33 | document.addEventListener("touchcancel", handleDragEnd);
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/TextHighlighted/TextHighlighted.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 | import React from "react";
3 | import Text from "../Typography/Text/Text.jsx";
4 | import { classNames, idPropTypeCheckForTruncatedComponents } from "../../utils";
5 | import styles from "./styles.module.css";
6 |
7 | const TextHighlighted = ({ id, truncate, className, style, children }) => (
8 |
16 | {children}
17 |
18 | );
19 |
20 | TextHighlighted.propTypes = {
21 | id: idPropTypeCheckForTruncatedComponents,
22 | truncate: PropTypes.bool,
23 | className: PropTypes.string,
24 | style: PropTypes.object,
25 | children: PropTypes.node,
26 | };
27 |
28 | TextHighlighted.defaultProps = {
29 | truncate: true,
30 | };
31 |
32 | export default TextHighlighted;
33 |
--------------------------------------------------------------------------------
/src/components/Button/tests/button.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Button from "../Button";
3 | import { create } from "react-test-renderer";
4 | import { fireEvent, render } from "@testing-library/react";
5 |
6 | describe("Button component", () => {
7 | test("Matches the snapshot", () => {
8 | const button = create();
9 | expect(button.toJSON()).toMatchSnapshot();
10 | });
11 |
12 | test("Calls onSubmit() prop on submit button click", () => {
13 | const mockedOnClick = jest.fn();
14 |
15 | const { getByText, queryByText } = render(
16 |
17 | );
18 |
19 | // button rendered with right children
20 | expect(queryByText("test")).toBeTruthy();
21 |
22 | // click button
23 | fireEvent.click(getByText("test"));
24 |
25 | // expect click cb to be called
26 | expect(mockedOnClick).toHaveBeenCalledTimes(1);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/components/Toggle/styles.module.css:
--------------------------------------------------------------------------------
1 | .toggle {
2 | display: inline-block;
3 | vertical-align: middle;
4 | }
5 |
6 | .toggle > div {
7 | display: block;
8 | width: 5rem;
9 | height: 2rem;
10 | border-radius: 0.3rem;
11 | transition: all 200ms ease-in-out;
12 | background-color: var(--toggle-bar-color);
13 | float: left;
14 | }
15 |
16 | .toggle > div:hover:not([disabled]) {
17 | cursor: pointer;
18 | width: 4.7rem;
19 | margin-right: 0.3rem;
20 | }
21 |
22 | .toggle > div.toggled:hover:not([disabled]) {
23 | padding-right: 0.3rem;
24 | }
25 |
26 | .toggle > div.notToggled:hover:not([disabled]) {
27 | padding-left: 0.3rem;
28 | }
29 |
30 | .knob {
31 | width: 2.5rem;
32 | height: 100%;
33 | border-radius: 0.3rem;
34 | }
35 |
36 | .toggled .knob {
37 | float: right;
38 | background-color: var(--toggle-toggled-knob-color);
39 | }
40 |
41 | .notToggled .knob {
42 | background-color: var(--toggle-knob-color);
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/Slider/tests/slider.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { create } from "react-test-renderer";
3 | import Slider from "../Slider";
4 |
5 | describe("Slider component", () => {
6 | test("Matches snapshot", () => {
7 | let value0 = 0;
8 | let value1 = 40;
9 | const mockHandleChange0 = jest.fn((newValue) => {
10 | value0 = newValue;
11 | });
12 | const mockHandleChange1 = jest.fn((newValue) => {
13 | value1 = newValue;
14 | });
15 | const slider = create(
16 |
32 | );
33 | expect(slider.toJSON()).toMatchSnapshot();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/components/TextArea/test/__snapshots__/textArea.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`TextArea component Matches the snapshot 1`] = `
4 |
7 |
13 |
31 |
34 |
35 | `;
36 |
--------------------------------------------------------------------------------
/src/stories/Link.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "..";
3 | import "./page.css";
4 |
5 | const LinkObj = {
6 | title: "Design System/Components/Link",
7 | component: Link,
8 | };
9 |
10 | export default LinkObj;
11 |
12 | const Template = ({ children, ...args }) => {children};
13 |
14 | export const Basic = Template.bind({});
15 | Basic.args = {
16 | children: "Test Link",
17 | href: "#",
18 | };
19 |
20 | export const Gray = Template.bind({});
21 | Gray.args = {
22 | children: "Test Link",
23 | href: "#",
24 | gray: true,
25 | };
26 | export const Truncated = Template.bind({});
27 | Truncated.args = {
28 | children: "Test Big Big Big Big Big Truncated Link",
29 | style: {
30 | width: "200px",
31 | },
32 | href: "#",
33 | truncate: true,
34 | };
35 | export const CustomChild = Template.bind({});
36 | CustomChild.args = {
37 | customComponent: (props) => Test me Custom
,
38 | href: "#",
39 | };
40 |
--------------------------------------------------------------------------------
/src/components/Message/test/__snapshots__/message.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Message component Matches the snapshot 1`] = `
4 |
7 |
23 |
26 | test
27 |
28 |
29 | `;
30 |
--------------------------------------------------------------------------------
/src/stories/Message.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Message } from "..";
3 | import "./page.css";
4 |
5 | const MessageObj = {
6 | title: "Design System/Components/Message",
7 | component: Message,
8 | };
9 |
10 | export default MessageObj;
11 |
12 | const Template = ({ children, ...args }) => (
13 | {children}
14 | );
15 |
16 | export const Info = Template.bind({});
17 | Info.args = {
18 | children: "Test Message",
19 | kind: "info",
20 | };
21 | export const Warning = Template.bind({});
22 | Warning.args = {
23 | children: "Test Message",
24 | kind: "warning",
25 | };
26 | export const Error = Template.bind({});
27 | Error.args = {
28 | children: "Test Message",
29 | kind: "error",
30 | };
31 | export const Success = Template.bind({});
32 | Success.args = {
33 | children: "Test Message",
34 | kind: "success",
35 | };
36 | export const Blocked = Template.bind({});
37 | Blocked.args = {
38 | children: "Test Message",
39 | kind: "blocked",
40 | };
41 |
--------------------------------------------------------------------------------
/src/components/StatusBar/test/statusBar.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import StatusBar from "../StatusBar";
3 | import { create } from "react-test-renderer";
4 | import {
5 | defaultLightTheme,
6 | ThemeProvider,
7 | DEFAULT_LIGHT_THEME_NAME,
8 | } from "../../../theme";
9 |
10 | describe("StatusBar component", () => {
11 | test("Matches the snapshot", () => {
12 | const statusBar = create(
13 |
16 |
30 |
31 | );
32 | expect(statusBar.toJSON()).toMatchSnapshot();
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/.storybook/providerFn.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import {
3 | useTheme,
4 | ThemeProvider,
5 | DEFAULT_DARK_THEME_NAME,
6 | DEFAULT_LIGHT_THEME_NAME,
7 | defaultDarkTheme,
8 | defaultLightTheme,
9 | } from "../src/theme";
10 |
11 | const themes = {
12 | [DEFAULT_DARK_THEME_NAME]: defaultDarkTheme,
13 | [DEFAULT_LIGHT_THEME_NAME]: defaultLightTheme,
14 | };
15 |
16 | function ThemeConsumer({ theme, children }) {
17 | const { setThemeName, themeName } = useTheme();
18 | const themeKey = Object.keys(theme)[0];
19 | useEffect(() => {
20 | if (themeName !== themeKey) {
21 | setThemeName(themeKey);
22 | }
23 | }, [themeKey, setThemeName, themeName]);
24 |
25 | return children;
26 | }
27 |
28 | function providerFn({ theme, children }) {
29 | return (
30 |
31 | {children}
32 |
33 | );
34 | }
35 |
36 | export default providerFn;
37 |
--------------------------------------------------------------------------------
/src/components/Badge/styles.module.css:
--------------------------------------------------------------------------------
1 | .badgeWrapper {
2 | position: absolute;
3 | display: inline-flex;
4 | align-items: center;
5 | padding: 2rem 3rem 2rem 0;
6 | z-index: 100;
7 | border-radius: 5px;
8 | height: 5rem;
9 | -webkit-box-shadow: 0px 3px 6px 2px rgba(0, 0, 0, 0.16);
10 | -moz-box-shadow: 0px 3px 6px 2px rgba(0, 0, 0, 0.16);
11 | box-shadow: 0px 3px 6px 2px rgba(0, 0, 0, 0.16);
12 | visibility: hidden;
13 | opacity: 0;
14 | }
15 |
16 | .badgeWrapperVisible {
17 | composes: badgeWrapper;
18 | visibility: visible;
19 | opacity: 1;
20 | }
21 |
22 | .badgeClose {
23 | background: none;
24 | border: none;
25 | cursor: pointer;
26 | position: absolute;
27 | top: 0;
28 | right: 1rem;
29 | font-size: var(--font-size-xxlarge);
30 | color: var(--text-color-light);
31 | }
32 |
33 | .badgeClose:hover {
34 | background: none;
35 | color: var(--color-primary);
36 | }
37 |
38 | .badgeIcon {
39 | display: inline-flex;
40 | align-items: center;
41 | padding: 0 1rem 0 2rem;
42 | }
43 |
--------------------------------------------------------------------------------
/src/stories/DatePicker.stories.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { DatePicker } from "..";
3 | import "./page.css";
4 |
5 | const DatePickerObj = {
6 | title: "Design System/Components/DatePicker",
7 | component: DatePicker,
8 | };
9 |
10 | export default DatePickerObj;
11 |
12 | const ControlledTemplate = ({ children, ...args }) => {
13 | const [value, setValue] = useState({ year: 2019, month: 11, day: 15 });
14 | const onChange = (year, month, day) => {
15 | if (!!year && !!month && !!day) {
16 | setValue({ year, month, day });
17 | }
18 | };
19 |
20 | return ;
21 | };
22 |
23 | export const FullyControlled = ControlledTemplate.bind({});
24 | FullyControlled.args = {};
25 |
26 | const years = {
27 | min: { year: 2018, month: 1, day: 25 },
28 | max: { year: 2020, month: 2, day: 4 },
29 | };
30 |
31 | export const FullyControlledMinMax = ControlledTemplate.bind({});
32 | FullyControlledMinMax.args = {
33 | years,
34 | };
35 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * classNames is an utility to return a concatenated string without falsy values
3 | * @param {String} args
4 | * @returns {String}
5 | */
6 | export const classNames = (...args) =>
7 | args.filter(Boolean).filter(isString).join(" ");
8 |
9 | export const isString = (arg) => typeof arg == "string";
10 | export const isObject = (arg) =>
11 | arg && !Array.isArray(arg) && typeof arg == "object";
12 |
13 | /**
14 | * Returns the prop type checking for the id of components exposing an API
15 | * for truncating the text
16 | * @param {Object} props
17 | * @param {string} propName
18 | * @param {string} componentName
19 | */
20 | export const idPropTypeCheckForTruncatedComponents = (
21 | props,
22 | propName,
23 | componentName
24 | ) => {
25 | if (
26 | props.truncate === true &&
27 | (!props[propName] || typeof props[propName] !== "string")
28 | ) {
29 | return new Error(
30 | `Please provide a valid ID for ${componentName} when truncate is active`
31 | );
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "stylelint-config-recommended",
3 | "rules": {
4 | "block-no-empty": null,
5 | "no-empty-source": null,
6 | "unit-disallowed-list": [
7 | "cm",
8 | "mm",
9 | "in",
10 | "pt",
11 | "pc",
12 | "ch",
13 | "vmin",
14 | "vmax",
15 | "ex"
16 | ],
17 | "max-empty-lines": 2,
18 | "indentation": 2,
19 | "max-nesting-depth": 3,
20 | "font-family-no-missing-generic-family-keyword": null,
21 | "rule-empty-line-before": [
22 | "always",
23 | {
24 | "ignore": ["after-comment", "first-nested"],
25 | "severity": "warning"
26 | }
27 | ],
28 | "at-rule-no-unknown": null,
29 | "property-no-unknown": [
30 | true,
31 | {
32 | "ignoreProperties": ["composes"]
33 | }
34 | ],
35 | "selector-pseudo-class-no-unknown": [
36 | true,
37 | {
38 | "ignorePseudoClasses": ["global"]
39 | }
40 | ]
41 | },
42 | "ignoreFiles": "**/*.js"
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/Table/TableHeader.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const TableHeader = ({
7 | headers,
8 | headerCellClassName,
9 | rowClassName,
10 | className,
11 | }) => {
12 | return (
13 |
14 |
15 | {headers.map((header, idx) => (
16 | |
20 | {header}
21 | |
22 | ))}
23 |
24 |
25 | );
26 | };
27 |
28 | TableHeader.propTypes = {
29 | headers: PropTypes.arrayOf(PropTypes.node).isRequired,
30 | headerCellClassName: PropTypes.string,
31 | rowClassName: PropTypes.string,
32 | className: PropTypes.string,
33 | };
34 |
35 | export default TableHeader;
36 |
--------------------------------------------------------------------------------
/src/components/Badge/Badge.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const Badge = ({ children, icon, show, onClose, ...props }) => {
7 | const onCloseClick = (e) => {
8 | e && e.preventDefault();
9 | onClose();
10 | };
11 | return (
12 |
17 |
23 |
{icon}
24 | {children}
25 |
26 | );
27 | };
28 |
29 | Badge.propTypes = {
30 | children: PropTypes.node.isRequired,
31 | show: PropTypes.bool.isRequired,
32 | onClose: PropTypes.func.isRequired,
33 | style: PropTypes.object,
34 | icon: PropTypes.node,
35 | };
36 |
37 | export default Badge;
38 |
--------------------------------------------------------------------------------
/src/components/Typography/P.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { useTruncate } from "../../hooks";
5 | import { classNames, idPropTypeCheckForTruncatedComponents } from "../../utils";
6 |
7 | const P = ({
8 | id,
9 | children,
10 | className,
11 | style,
12 | truncate,
13 | linesBeforeTruncate,
14 | ...props
15 | }) => {
16 | useTruncate(id, truncate, linesBeforeTruncate);
17 | return (
18 |
23 | {children}
24 |
25 | );
26 | };
27 |
28 | P.propTypes = {
29 | children: PropTypes.node.isRequired,
30 | className: PropTypes.string,
31 | style: PropTypes.object,
32 | truncate: PropTypes.bool,
33 | linesBeforeTruncate: PropTypes.number,
34 | id: idPropTypeCheckForTruncatedComponents,
35 | };
36 |
37 | P.defaultProps = {
38 | truncate: false,
39 | linesBeforeTruncate: 1,
40 | };
41 | export default P;
42 |
--------------------------------------------------------------------------------
/src/components/Badge/tests/badge.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Badge from "../Badge";
3 | import { create } from "react-test-renderer";
4 | import { fireEvent, render } from "@testing-library/react";
5 |
6 | describe("Badge component", () => {
7 | test("Matches the snapshot", () => {
8 | const badge = create(
9 |
10 | test
11 |
12 | );
13 | expect(badge.toJSON()).toMatchSnapshot();
14 | });
15 |
16 | test("Calls onClose() prop on close icon click", () => {
17 | const mockedOnClose = jest.fn();
18 |
19 | const { getByTestId, queryByTestId } = render(
20 |
21 | test
22 |
23 | );
24 |
25 | // close button rendered
26 | expect(queryByTestId("close-button")).toBeTruthy();
27 |
28 | // click close button
29 | fireEvent.click(getByTestId("close-button"));
30 |
31 | // expect onClose to be called
32 | expect(mockedOnClose).toHaveBeenCalledTimes(1);
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/src/components/Typography/H1.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames, idPropTypeCheckForTruncatedComponents } from "../../utils";
5 | import { useTruncate } from "../../hooks";
6 |
7 | const H1 = ({
8 | id,
9 | children,
10 | className,
11 | style,
12 | truncate,
13 | linesBeforeTruncate,
14 | ...props
15 | }) => {
16 | useTruncate(id, truncate, linesBeforeTruncate);
17 | return (
18 |
23 | {children}
24 |
25 | );
26 | };
27 |
28 | H1.propTypes = {
29 | children: PropTypes.node.isRequired,
30 | className: PropTypes.string,
31 | style: PropTypes.object,
32 | truncate: PropTypes.bool,
33 | linesBeforeTruncate: PropTypes.number,
34 | id: idPropTypeCheckForTruncatedComponents,
35 | };
36 |
37 | H1.defaultProps = {
38 | truncate: false,
39 | linesBeforeTruncate: 1,
40 | };
41 |
42 | export default H1;
43 |
--------------------------------------------------------------------------------
/src/components/Typography/H2.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { useTruncate } from "../../hooks";
5 | import { classNames, idPropTypeCheckForTruncatedComponents } from "../../utils";
6 |
7 | const H2 = ({
8 | id,
9 | children,
10 | className,
11 | style,
12 | truncate,
13 | linesBeforeTruncate,
14 | ...props
15 | }) => {
16 | useTruncate(id, truncate, linesBeforeTruncate);
17 | return (
18 |
23 | {children}
24 |
25 | );
26 | };
27 |
28 | H2.propTypes = {
29 | children: PropTypes.node.isRequired,
30 | className: PropTypes.string,
31 | style: PropTypes.object,
32 | truncate: PropTypes.bool,
33 | linesBeforeTruncate: PropTypes.number,
34 | id: idPropTypeCheckForTruncatedComponents,
35 | };
36 |
37 | H2.defaultProps = {
38 | truncate: false,
39 | linesBeforeTruncate: 1,
40 | };
41 |
42 | export default H2;
43 |
--------------------------------------------------------------------------------
/src/components/Typography/H3.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { useTruncate } from "../../hooks";
5 | import { classNames, idPropTypeCheckForTruncatedComponents } from "../../utils";
6 |
7 | const H3 = ({
8 | id,
9 | children,
10 | className,
11 | style,
12 | truncate,
13 | linesBeforeTruncate,
14 | ...props
15 | }) => {
16 | useTruncate(id, truncate, linesBeforeTruncate);
17 | return (
18 |
23 | {children}
24 |
25 | );
26 | };
27 |
28 | H3.propTypes = {
29 | children: PropTypes.node.isRequired,
30 | className: PropTypes.string,
31 | style: PropTypes.object,
32 | truncate: PropTypes.bool,
33 | linesBeforeTruncate: PropTypes.number,
34 | id: idPropTypeCheckForTruncatedComponents,
35 | };
36 |
37 | H3.defaultProps = {
38 | truncate: false,
39 | linesBeforeTruncate: 1,
40 | };
41 |
42 | export default H3;
43 |
--------------------------------------------------------------------------------
/src/components/Typography/H4.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { useTruncate } from "../../hooks";
5 | import { classNames, idPropTypeCheckForTruncatedComponents } from "../../utils";
6 |
7 | const H4 = ({
8 | id,
9 | children,
10 | className,
11 | style,
12 | truncate,
13 | linesBeforeTruncate,
14 | ...props
15 | }) => {
16 | useTruncate(id, truncate, linesBeforeTruncate);
17 | return (
18 |
23 | {children}
24 |
25 | );
26 | };
27 |
28 | H4.propTypes = {
29 | children: PropTypes.node.isRequired,
30 | className: PropTypes.string,
31 | style: PropTypes.object,
32 | truncate: PropTypes.bool,
33 | linesBeforeTruncate: PropTypes.number,
34 | id: idPropTypeCheckForTruncatedComponents,
35 | };
36 |
37 | H4.defaultProps = {
38 | truncate: false,
39 | linesBeforeTruncate: 1,
40 | };
41 |
42 | export default H4;
43 |
--------------------------------------------------------------------------------
/src/components/Typography/H5.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { useTruncate } from "../../hooks";
5 | import { classNames, idPropTypeCheckForTruncatedComponents } from "../../utils";
6 |
7 | const H5 = ({
8 | id,
9 | children,
10 | className,
11 | style,
12 | truncate,
13 | linesBeforeTruncate,
14 | ...props
15 | }) => {
16 | useTruncate(id, truncate, linesBeforeTruncate);
17 | return (
18 |
23 | {children}
24 |
25 | );
26 | };
27 |
28 | H5.propTypes = {
29 | children: PropTypes.node.isRequired,
30 | className: PropTypes.string,
31 | style: PropTypes.object,
32 | truncate: PropTypes.bool,
33 | linesBeforeTruncate: PropTypes.number,
34 | id: idPropTypeCheckForTruncatedComponents,
35 | };
36 |
37 | H5.defaultProps = {
38 | truncate: false,
39 | linesBeforeTruncate: 1,
40 | };
41 |
42 | export default H5;
43 |
--------------------------------------------------------------------------------
/src/components/Typography/H6.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { useTruncate } from "../../hooks";
5 | import { classNames, idPropTypeCheckForTruncatedComponents } from "../../utils";
6 |
7 | const H6 = ({
8 | id,
9 | children,
10 | className,
11 | style,
12 | truncate,
13 | linesBeforeTruncate,
14 | ...props
15 | }) => {
16 | useTruncate(id, truncate, linesBeforeTruncate);
17 | return (
18 |
23 | {children}
24 |
25 | );
26 | };
27 |
28 | H6.propTypes = {
29 | children: PropTypes.node.isRequired,
30 | className: PropTypes.string,
31 | style: PropTypes.object,
32 | truncate: PropTypes.bool,
33 | linesBeforeTruncate: PropTypes.number,
34 | id: idPropTypeCheckForTruncatedComponents,
35 | };
36 |
37 | H6.defaultProps = {
38 | truncate: false,
39 | linesBeforeTruncate: 1,
40 | };
41 |
42 | export default H6;
43 |
--------------------------------------------------------------------------------
/src/components/Modal/ModalWrapper.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const ModalWrapper = ({
7 | children,
8 | style,
9 | show,
10 | onClose,
11 | disableClose,
12 | className,
13 | ...props
14 | }) => {
15 | const handleClickOverlay = (e) => {
16 | e.stopPropagation();
17 | if (!disableClose) {
18 | e.target.id === "modal-wrapper" && onClose();
19 | }
20 | };
21 | return (
22 |
30 | {children}
31 |
32 | );
33 | };
34 |
35 | ModalWrapper.propTypes = {
36 | children: PropTypes.element,
37 | style: PropTypes.object,
38 | show: PropTypes.bool.isRequired,
39 | className: PropTypes.string,
40 | onClose: PropTypes.func.isRequired,
41 | disableClose: PropTypes.bool,
42 | };
43 |
44 | export default ModalWrapper;
45 |
--------------------------------------------------------------------------------
/src/components/Checkbox/Checkbox.jsx:
--------------------------------------------------------------------------------
1 | import { PropTypes } from "prop-types";
2 | import React from "react";
3 | import { classNames } from "../../utils";
4 | import styles from "./styles.module.css";
5 | import Description from "../Description/Description.jsx";
6 |
7 | const Checkbox = ({
8 | label,
9 | className,
10 | checked,
11 | style,
12 | name,
13 | id,
14 | description,
15 | ...props
16 | }) => (
17 | <>
18 |
30 |
31 | >
32 | );
33 |
34 | Checkbox.propTypes = {
35 | label: PropTypes.node,
36 | description: PropTypes.node,
37 | className: PropTypes.string,
38 | style: PropTypes.object,
39 | checked: PropTypes.bool,
40 | name: PropTypes.string,
41 | id: PropTypes.string.isRequired,
42 | };
43 |
44 | export default Checkbox;
45 |
--------------------------------------------------------------------------------
/src/hooks/useLockBodyScrollOnTrue.js:
--------------------------------------------------------------------------------
1 | import { useLayoutEffect, useState, useCallback } from "react";
2 |
3 | export default function useLockBodyScrollOnTrue(value) {
4 | const [originalStyle, setOriginalStyle] = useState(null);
5 |
6 | const reEnableScrolling = useCallback(() => {
7 | if (originalStyle) {
8 | document.body.style.overflow = originalStyle;
9 | setOriginalStyle(null);
10 | }
11 | }, [originalStyle, setOriginalStyle]);
12 |
13 | useLayoutEffect(() => {
14 | // value changed to true: need to prevent the scrolling
15 | if (value && !originalStyle) {
16 | // Get original body overflow
17 | const originalStyle = window.getComputedStyle(document.body).overflow;
18 | setOriginalStyle(originalStyle);
19 | // Prevent scrolling on value set to true
20 | document.body.style.overflow = "hidden";
21 | } else if (!value) {
22 | // value changed to false, we need to set the scroll
23 | // style back to its original style
24 | reEnableScrolling();
25 | }
26 | return () => reEnableScrolling();
27 | }, [value, reEnableScrolling, originalStyle, setOriginalStyle]);
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/Card/Card.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 | import React from "react";
3 | import { classNames } from "../../utils";
4 | import styles from "./styles.module.css";
5 |
6 | const Card = ({
7 | children,
8 | className,
9 | style,
10 | paddingSize,
11 | marker,
12 | markerColor,
13 | ...props
14 | }) => (
15 |
23 | {marker && (
24 |
31 | )}
32 | {children}
33 |
34 | );
35 |
36 | Card.propTypes = {
37 | children: PropTypes.node.isRequired,
38 | className: PropTypes.string,
39 | paddingSize: PropTypes.oneOf(["small", "medium", "large"]),
40 | marker: PropTypes.bool,
41 | markerColor: PropTypes.string,
42 | style: PropTypes.object,
43 | };
44 |
45 | Card.defaultProps = {
46 | markerColor: "var(--color-yellow)",
47 | };
48 |
49 | export default Card;
50 |
--------------------------------------------------------------------------------
/src/components/Paginator/styles.module.css:
--------------------------------------------------------------------------------
1 | .paginator {
2 | display: flex;
3 | user-select: none;
4 | }
5 |
6 | .paginator button {
7 | border: none;
8 | outline: none;
9 | background: none;
10 | height: 2.4rem;
11 | min-width: 2.4rem;
12 | display: flex;
13 | margin-right: 0.5rem;
14 | align-items: center;
15 | justify-content: center;
16 | cursor: pointer;
17 | border-radius: 0.3rem;
18 | font-size: 1.6rem;
19 | font-weight: 400;
20 | line-height: 0.2rem;
21 | letter-spacing: 0em;
22 | color: var(--paginator-button-color);
23 | }
24 |
25 | .paginator button:disabled {
26 | cursor: default;
27 | }
28 |
29 | .paginator button:hover:not([disabled]) {
30 | opacity: 0.7;
31 | }
32 |
33 | .paginator button.selected {
34 | background-color: var(--paginator-selected-background);
35 | color: var(--paginator-selected-color);
36 | }
37 |
38 | .paginator button.brake {
39 | color: var(--paginator-brake-color);
40 | }
41 |
42 | .arrowButton.previous {
43 | margin-right: 1.5rem;
44 | }
45 |
46 | .arrowButton.next {
47 | margin-left: 0.1rem;
48 | margin-right: 0;
49 | }
50 |
51 | .arrowButton.previous .icon {
52 | transform: rotate(180deg);
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/Select/test/select.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Select from "../Select";
3 | import { create } from "react-test-renderer";
4 | import { render } from "@testing-library/react";
5 | import selectEvent from "react-select-event";
6 |
7 | describe("Select component", () => {
8 | const options = [
9 | {
10 | value: "top",
11 | label: "Top",
12 | },
13 | ];
14 | test("Matches the snapshot", () => {
15 | const select = create();
16 | expect(select.toJSON()).toMatchSnapshot();
17 | });
18 |
19 | test("Call on change function on option click", async () => {
20 | let selected;
21 | const mockHandleChange = jest.fn((option) => {
22 | selected = option.value;
23 | });
24 | const { getByLabelText } = render(
25 | <>
26 |
27 |
28 | >
29 | );
30 |
31 | // select top option
32 | await selectEvent.select(getByLabelText("Sort"), ["Top"]);
33 | expect(mockHandleChange).toBeCalled();
34 | expect(selected).toBe("top");
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/src/components/Slider/styles.module.css:
--------------------------------------------------------------------------------
1 | .thumb {
2 | position: relative;
3 | display: block;
4 | content: "";
5 | width: 1.8rem;
6 | height: 1.8rem;
7 | background-color: var(--color-white);
8 | border-radius: 50%;
9 | box-shadow: 0 0.1rem 0.1rem rgba(0, 0, 0, 0.5);
10 | user-select: none;
11 | cursor: pointer;
12 | box-sizing: border-box;
13 | }
14 |
15 | .track {
16 | position: relative;
17 | display: inline-block;
18 | background-color: var(--btn-disabled-background-color);
19 | border-radius: 0.5rem;
20 | user-select: none;
21 | box-sizing: border-box;
22 | }
23 |
24 | .x.track {
25 | width: 20rem;
26 | height: 1rem;
27 | }
28 |
29 | .y.track {
30 | width: 1rem;
31 | height: 20rem;
32 | }
33 |
34 | .active {
35 | position: absolute;
36 | background-color: var(--btn-background-color);
37 | border-radius: 0.5rem;
38 | user-select: none;
39 | box-sizing: border-box;
40 | }
41 |
42 | .x.active {
43 | top: 0;
44 | height: 100%;
45 | }
46 |
47 | .y.active {
48 | left: 0;
49 | width: 100%;
50 | }
51 |
52 | .disabled {
53 | opacity: 0.5;
54 | }
55 |
56 | .handle {
57 | z-index: 1;
58 | position: absolute;
59 | transform: translate(-50%, -50%);
60 | }
61 |
--------------------------------------------------------------------------------
/src/stories/Tooltip.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Tooltip } from "..";
3 | import "./page.css";
4 |
5 | const TooltipObj = {
6 | title: "Design System/Components/Tooltip",
7 | component: Tooltip,
8 | };
9 |
10 | export default TooltipObj;
11 |
12 | // TODO: remove margins and implement multiple stories in a single canva with MDX
13 | const Template = ({ children, ...args }) => (
14 | {children}
15 | );
16 |
17 | export const Top = Template.bind({});
18 | Top.args = {
19 | children: "Test Tooltip",
20 | content: "tooltip top",
21 | placement: "top",
22 | style: {
23 | marginTop: "5rem",
24 | },
25 | };
26 |
27 | export const Left = Template.bind({});
28 | Left.args = {
29 | children: "Test Tooltip",
30 | content: "tooltip left",
31 | placement: "left",
32 | style: {
33 | marginLeft: "10rem",
34 | },
35 | };
36 |
37 | export const Bottom = Template.bind({});
38 | Bottom.args = {
39 | children: "Test Tooltip",
40 | content: "tooltip bottom",
41 | placement: "bottom",
42 | };
43 |
44 | export const Right = Template.bind({});
45 | Right.args = {
46 | children: "Test Tooltip",
47 | content: "tooltip right",
48 | placement: "right",
49 | };
50 |
--------------------------------------------------------------------------------
/src/components/Slider/tests/__snapshots__/slider.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Slider component Matches snapshot 1`] = `
4 |
55 | `;
56 |
--------------------------------------------------------------------------------
/src/components/Toggle/tests/toggle.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Toggle from "../Toggle";
3 | import { create } from "react-test-renderer";
4 | import {
5 | defaultLightTheme,
6 | ThemeProvider,
7 | DEFAULT_LIGHT_THEME_NAME,
8 | } from "../../../theme";
9 | import { render, fireEvent } from "@testing-library/react";
10 |
11 | describe("Toggle Component", () => {
12 | test("Matches snapshot", () => {
13 | const toggle = create(
14 |
17 |
18 |
19 | );
20 | expect(toggle.toJSON()).toMatchSnapshot();
21 | });
22 |
23 | test("Toggles state", () => {
24 | const mockHandleToggle = jest.fn();
25 | const { getByTestId } = render(
26 |
29 |
30 |
31 | );
32 | fireEvent.click(getByTestId("switch"));
33 | expect(mockHandleToggle).toBeCalled();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/components/Datepicker/DatepickerPad.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | font-size: var(--font-size-small);
3 | background-color: var(--card-background);
4 | text-align: center;
5 | justify-items: center;
6 | align-items: center;
7 | }
8 |
9 | .container > * > *:not(label):not(.disabled):not(.selected):hover {
10 | cursor: pointer;
11 | background-color: var(--separator-color);
12 | }
13 |
14 | .row {
15 | width: 100%;
16 | display: flex;
17 | align-items: center;
18 | justify-content: space-between;
19 | }
20 |
21 | .element {
22 | width: 60%;
23 | padding: 1rem;
24 | text-align: center;
25 | color: var(--text-color);
26 | border: 1px solid transparent;
27 | background-color: var(--card-background);
28 | outline: none;
29 | }
30 |
31 | .element:focus-visible {
32 | border: 1px solid var(--color-primary);
33 | }
34 |
35 | .padRow {
36 | width: 100%;
37 | display: grid;
38 | grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
39 | justify-items: center;
40 | }
41 |
42 | .disabled {
43 | cursor: default;
44 | color: var(--btn-disabled-text-color);
45 | }
46 |
47 | .selected {
48 | color: var(--card-background);
49 | background-color: var(--text-input-color);
50 | }
51 |
52 | .label {
53 | min-width: 15rem;
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/Datepicker/test/__snapshots__/datepicker.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`DatePicker component Matches the snapshot 1`] = `
4 |
7 |
11 | 12/2020
12 |
15 |
16 |
46 |
47 | `;
48 |
--------------------------------------------------------------------------------
/src/stories/StatusTag.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { StatusTag } from "..";
3 | import "./page.css";
4 |
5 | const StatusTagObj = {
6 | title: "Design System/Components/StatusTag",
7 | component: StatusTag,
8 | };
9 |
10 | export default StatusTagObj;
11 |
12 | const Template = ({ children, ...args }) => ;
13 |
14 | export const GrayNegative = Template.bind({});
15 | GrayNegative.args = {
16 | type: "grayNegative",
17 | text: "",
18 | };
19 |
20 | export const GreenCheck = Template.bind({});
21 | GreenCheck.args = {
22 | type: "greenCheck",
23 | text: "",
24 | };
25 |
26 | export const OrangeNegativeCircled = Template.bind({});
27 | OrangeNegativeCircled.args = {
28 | type: "orangeNegativeCircled",
29 | text: "",
30 | };
31 |
32 | export const BluePendingWithText = Template.bind({});
33 | BluePendingWithText.args = {
34 | type: "bluePending",
35 | text: "Pending",
36 | };
37 |
38 | export const YellowTimeWithText = Template.bind({});
39 | YellowTimeWithText.args = {
40 | type: "yellowTime",
41 | text: "Waiting for approval",
42 | };
43 |
44 | export const BlackTimeWithText = Template.bind({});
45 | BlackTimeWithText.args = {
46 | type: "blackTime",
47 | text: "Hasn't approved yet",
48 | };
49 |
--------------------------------------------------------------------------------
/src/stories/assets/direction.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Table/TableBody.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const TableBody = ({ data, className, rowClassName, bodyCellClassName }) => {
7 | return (
8 |
9 | {data.map((line, lineIdx) => (
10 |
13 | {Object.keys(line).map((key, keyIdx) => (
14 | |
18 | {typeof line[key] === "string" ? (
19 | {line[key]}
20 | ) : (
21 | line[key]
22 | )}
23 | |
24 | ))}
25 |
26 | ))}
27 |
28 | );
29 | };
30 |
31 | TableBody.propTypes = {
32 | data: PropTypes.array.isRequired,
33 | className: PropTypes.string,
34 | rowClassName: PropTypes.string,
35 | bodyCellClassName: PropTypes.string,
36 | };
37 |
38 | export default TableBody;
39 |
--------------------------------------------------------------------------------
/src/hooks/useKeyPress.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState, useCallback } from "react";
2 |
3 | // Hook grabbed from https://usehooks.com/useKeyPress/
4 | export default function useKeyPress(targetKey) {
5 | // State for keeping track of whether key is pressed
6 | const [keyPressed, setKeyPressed] = useState(false);
7 |
8 | // If pressed key is our target key then set to true
9 | const downHandler = useCallback(
10 | ({ key }) => {
11 | if (key === targetKey) {
12 | setKeyPressed(true);
13 | }
14 | },
15 | [setKeyPressed, targetKey]
16 | );
17 |
18 | // If released key is our target key then set to false
19 | const upHandler = useCallback(
20 | ({ key }) => {
21 | if (key === targetKey) {
22 | setKeyPressed(false);
23 | }
24 | },
25 | [setKeyPressed, targetKey]
26 | );
27 |
28 | // Add event listeners
29 | useEffect(() => {
30 | window.addEventListener("keydown", downHandler);
31 | window.addEventListener("keyup", upHandler);
32 | // Remove event listeners on cleanup
33 | return () => {
34 | window.removeEventListener("keydown", downHandler);
35 | window.removeEventListener("keyup", upHandler);
36 | };
37 | }, [downHandler, upHandler]);
38 |
39 | return keyPressed;
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/Typography/tests/__snapshots__/typography.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Typography Elements H1 Matches Snapshot 1`] = `
4 |
7 | header1
8 |
9 | `;
10 |
11 | exports[`Typography Elements H2 Matches Snapshot 1`] = `
12 |
15 | header1
16 |
17 | `;
18 |
19 | exports[`Typography Elements H3 Matches Snapshot 1`] = `
20 |
23 | header1
24 |
25 | `;
26 |
27 | exports[`Typography Elements H4 Matches Snapshot 1`] = `
28 |
31 | header1
32 |
33 | `;
34 |
35 | exports[`Typography Elements H5 Matches Snapshot 1`] = `
36 |
39 | header1
40 |
41 | `;
42 |
43 | exports[`Typography Elements H6 Matches Snapshot 1`] = `
44 |
47 | header1
48 |
49 | `;
50 |
51 | exports[`Typography Elements P Matches Snapshot 1`] = `
52 |
55 | header1
56 |
57 | `;
58 |
59 | exports[`Typography Elements Text Matches Snapshot 1`] = `
60 |
63 | header1
64 |
65 | `;
66 |
--------------------------------------------------------------------------------
/src/components/Checkbox/styles.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | justify-content: flex-start;
4 | position: relative;
5 | align-items: center;
6 | cursor: pointer;
7 | user-select: none;
8 | }
9 |
10 | .container input {
11 | position: absolute;
12 | opacity: 0;
13 | cursor: pointer;
14 | width: 0;
15 | }
16 |
17 | .checkmark {
18 | margin-right: 0.8rem;
19 | height: 1.4rem;
20 | width: 1.4rem;
21 | border-radius: 0.1rem;
22 | border: 1px solid var(--checkbox-stroke-color);
23 | background-color: transparent;
24 | }
25 |
26 | .checkmark:after {
27 | content: "";
28 | position: absolute;
29 | display: none;
30 | }
31 |
32 | .container .checkmark:after {
33 | left: 5px;
34 | width: 4px;
35 | height: 9px;
36 | border: solid var(--color-white);
37 | border-width: 0 2px 2px 0;
38 | -webkit-transform: rotate(45deg);
39 | -ms-transform: rotate(45deg);
40 | transform: rotate(45deg);
41 | }
42 |
43 | .container:hover input ~ .checkmark {
44 | background-color: var(--checkbox-hovered-background-color);
45 | }
46 |
47 | .container input:checked ~ .checkmark {
48 | background-color: var(--color-primary);
49 | border: 1px solid var(--checkbox-active-stroke-color);
50 | }
51 |
52 | .container input:checked ~ .checkmark:after {
53 | display: block;
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/DarkLightToggle/tests/darkLightToggle.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import DarkLightToggle from "../DarkLightToggle";
3 | import { create } from "react-test-renderer";
4 | import {
5 | defaultLightTheme,
6 | ThemeProvider,
7 | DEFAULT_LIGHT_THEME_NAME,
8 | } from "../../../theme";
9 | import { render, fireEvent } from "@testing-library/react";
10 |
11 | describe("DarkLightToggle Component", () => {
12 | test("Matches snapshot", () => {
13 | const toggle = create(
14 |
17 |
18 |
19 | );
20 | expect(toggle.toJSON()).toMatchSnapshot();
21 | });
22 |
23 | test("Toggles state", () => {
24 | const mockHandleToggle = jest.fn();
25 | const { getByTestId } = render(
26 |
29 |
30 |
31 | );
32 | fireEvent.click(getByTestId("darkLightToggle"));
33 | expect(mockHandleToggle).toBeCalled();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/stories/BoxTextInput.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { BoxTextInput } from "..";
3 | import "./page.css";
4 |
5 | const BoxTextInputObj = {
6 | title: "Design System/Components/BoxTextInput",
7 | component: BoxTextInput,
8 | };
9 |
10 | export default BoxTextInputObj;
11 | const Template = ({ children, ...args }) => ;
12 |
13 | export const Basic = Template.bind({});
14 | Basic.args = {
15 | placeholder: "Censorship Token",
16 | name: "search",
17 | error: "",
18 | };
19 |
20 | export const Error = Template.bind({});
21 | Error.args = {
22 | placeholder: "Censorship Token",
23 | name: "search",
24 | error: "Oops, something went wrong",
25 | };
26 |
27 | export const Rounded = Template.bind({});
28 | Rounded.args = {
29 | placeholder: "Censorship Token",
30 | name: "search",
31 | rounded: true,
32 | error: "",
33 | };
34 |
35 | export const Searchable = Template.bind({});
36 | Searchable.args = {
37 | placeholder: "Censorship Token",
38 | name: "search",
39 | searchInput: true,
40 | error: "",
41 | };
42 |
43 | export const WithInfoTooltip = Template.bind({});
44 | WithInfoTooltip.args = {
45 | placeholder: "Censorship Token",
46 | name: "token",
47 | tooltipInfo: "Record Censorship Token",
48 | tooltipPlacement: "bottom",
49 | };
50 |
--------------------------------------------------------------------------------
/src/components/ButtonIcon/styles.module.css:
--------------------------------------------------------------------------------
1 | .buttonIcon {
2 | border: 0;
3 | display: flex;
4 | justify-content: center;
5 | align-items: center;
6 | border-radius: 0.5rem;
7 | width: 2.4rem;
8 | height: 2.4rem;
9 | padding: 0.3rem;
10 | cursor: pointer;
11 | background: var(--button-icon-background);
12 | box-shadow: 0 0.2rem 0.8rem 0 rgba(0, 0, 0, 0.12);
13 | }
14 |
15 | .buttonIcon:hover {
16 | box-shadow: 0 0.1rem 0.4rem 0 rgba(0, 0, 0, 0.24);
17 | }
18 |
19 | .buttonIcon.text:hover {
20 | width: max-content;
21 | padding-left: 0.4rem;
22 | }
23 |
24 | .buttonIcon:active {
25 | box-shadow: 0 0 0.4rem 0 rgba(0, 0, 0, 0.24);
26 | }
27 |
28 | .disabled {
29 | cursor: not-allowed;
30 | color: var(--btn-disabled-text-color);
31 | background-color: var(--btn-disabled-background-color);
32 | box-shadow: 0 0.2rem 0.8rem 0 rgba(0, 0, 0, 0.12);
33 | }
34 |
35 | .disabled:hover {
36 | box-shadow: 0 0.2rem 0.8rem 0 rgba(0, 0, 0, 0.12);
37 | }
38 |
39 | .disabled:active {
40 | box-shadow: 0 0.2rem 0.8rem 0 rgba(0, 0, 0, 0.12);
41 | }
42 |
43 | .loading {
44 | pointer-events: none;
45 | }
46 |
47 | .buttonIcon > span {
48 | display: none;
49 | margin: 0 1rem 0 0.6rem;
50 | font-size: 1.3rem;
51 | line-height: 1.6rem;
52 | }
53 |
54 | .buttonIcon:hover > span {
55 | display: inline;
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/Slider/SliderHandle.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { POSITIONS_MAP } from "./helpers";
4 | import { classNames } from "../../utils";
5 | import styles from "./styles.module.css";
6 |
7 | const SliderHandle = React.forwardRef(
8 | (
9 | { axis, position, onTouchStart, onMouseDown, className, dataTestId },
10 | ref
11 | ) => {
12 | const style = { top: "50%", left: "50%" };
13 | const positionAtAxis = POSITIONS_MAP[axis];
14 | style[positionAtAxis] = position[positionAtAxis] + "%";
15 |
16 | return (
17 | {
21 | e.stopPropagation();
22 | e.nativeEvent.stopImmediatePropagation();
23 | }}
24 | data-testid={dataTestId}>
25 |
26 |
27 | );
28 | }
29 | );
30 |
31 | SliderHandle.propTypes = {
32 | axis: PropTypes.string.isRequired,
33 | position: PropTypes.object.isRequired,
34 | style: PropTypes.object,
35 | onTouchStart: PropTypes.func.isRequired,
36 | onMouseDown: PropTypes.func.isRequired,
37 | className: PropTypes.string,
38 | dataTestId: PropTypes.string,
39 | };
40 |
41 | export default SliderHandle;
42 |
--------------------------------------------------------------------------------
/src/components/Tooltip/styles.module.css:
--------------------------------------------------------------------------------
1 | .tooltipContent {
2 | border-radius: 0.2rem;
3 | box-shadow: 0.1rem 0.1rem 0.6rem 0.25rem var(--color-shadow-light);
4 | padding: 0.25rem 1.5rem;
5 | user-select: none;
6 | max-width: 30rem;
7 | word-wrap: break-word;
8 | pointer-events: none;
9 | z-index: var(--z-index-big);
10 | background-color: var(--tooltip-background-color);
11 | visibility: hidden;
12 | opacity: 0;
13 | transition: 0.3s all ease;
14 | }
15 |
16 | .showTooltip {
17 | visibility: visible;
18 | opacity: 1;
19 | }
20 |
21 | .tooltip {
22 | position: relative;
23 | display: inline-block;
24 | }
25 |
26 | .top {
27 | position: absolute;
28 | margin: 0.6rem 0;
29 | bottom: 100%;
30 | left: 50%;
31 | transform: translateX(-50%);
32 | }
33 |
34 | .bottom {
35 | position: absolute;
36 | margin: 0.6rem 0;
37 | top: 100%;
38 | left: 50%;
39 | transform: translateX(-50%);
40 | }
41 |
42 | .left {
43 | position: absolute;
44 | margin: 0 0.6rem;
45 | right: 100%;
46 | top: 50%;
47 | transform: translateY(-50%);
48 | }
49 |
50 | .right {
51 | position: absolute;
52 | margin: 0 0.6rem;
53 | left: 100%;
54 | top: 50%;
55 | transform: translateY(-50%);
56 | }
57 |
58 | @media (pointer: fine) {
59 | .tooltip:hover .tooltipContent {
60 | visibility: visible;
61 | opacity: 1;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/components/StatusTag/styles.module.css:
--------------------------------------------------------------------------------
1 | .statusTagWrapper {
2 | padding: 0em 0.4em;
3 | height: 20px;
4 | display: inline-flex;
5 | align-items: center;
6 | justify-content: center;
7 | border-radius: 2px;
8 | border: 1px solid var(--color-gray);
9 | }
10 |
11 | .statusTagWrapper > span {
12 | font-family: var(--font-family-text);
13 | font-size: var(--font-size-small);
14 | line-height: var(--spacing-1);
15 | white-space: nowrap;
16 | overflow: hidden;
17 | text-overflow: ellipsis;
18 | }
19 |
20 | .statusTagWrapper > img {
21 | margin-right: 0.5rem;
22 | }
23 |
24 | .greenCheck {
25 | color: var(--color-primary-light);
26 | border-color: var(--color-primary-light);
27 | }
28 |
29 | .grayNegative {
30 | color: var(--color-gray);
31 | border-color: var(--color-gray);
32 | }
33 |
34 | .orangeNegativeCircled {
35 | color: var(--color-orange);
36 | border-color: var(--color-orange);
37 | }
38 |
39 | .bluePending {
40 | color: var(--color-primary);
41 | border-color: var(--color-primary);
42 | }
43 |
44 | .yellowTime {
45 | color: var(--color-yellow);
46 | border-color: var(--color-yellow);
47 | }
48 |
49 | .blackTime {
50 | color: var(--color-primary-dark);
51 | border-color: var(--color-primary-dark);
52 | }
53 |
54 | .blueNegative {
55 | color: var(--color-dark);
56 | border-color: var(--color-dark);
57 | }
58 |
--------------------------------------------------------------------------------
/src/stories/assets/flow.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Layout/Column.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { classNames } from "../../utils";
4 | import grid from "./grid.module.css";
5 | import styles from "./styles.module.css";
6 | import isNumber from "lodash/isNumber";
7 |
8 | const Column = ({
9 | children,
10 | style,
11 | className,
12 | xs,
13 | sm,
14 | md,
15 | lg,
16 | xl,
17 | ...props
18 | }) => {
19 | const base = [xs, sm, md, lg, xl].find((bp) => isNumber(bp));
20 | return (
21 |
33 | {children}
34 |
35 | );
36 | };
37 |
38 | Column.propTypes = {
39 | children: PropTypes.node.isRequired,
40 | style: PropTypes.object,
41 | className: PropTypes.string,
42 | xs: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
43 | sm: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
44 | md: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
45 | lg: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
46 | xl: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
47 | };
48 |
49 | export default Column;
50 |
--------------------------------------------------------------------------------
/src/components/BoxTextInput/styles.module.css:
--------------------------------------------------------------------------------
1 | .boxtextinput {
2 | background-color: transparent;
3 | font-family: var(--font-family-text);
4 | border: 1px solid var(--input-border-color);
5 | outline: 0;
6 | width: 100%;
7 | font-size: var(--font-size-normal);
8 | font-weight: var(--font-weight-regular);
9 | transition: all 0.25s;
10 | line-height: var(--spacing-2);
11 | padding: 1rem 2rem;
12 | padding-right: 4rem;
13 | margin-top: 1.2rem;
14 | }
15 |
16 | .boxTextInputRounded {
17 | border-radius: 0.5rem;
18 | }
19 |
20 | .boxTextInputError {
21 | border-color: var(--color-orange);
22 | }
23 |
24 | .errorMsg {
25 | visibility: hidden;
26 | color: var(--color-orange);
27 | font-size: var(--font-size-small);
28 | min-height: 2rem;
29 | margin-top: 0.3rem;
30 | }
31 |
32 | .errorMsgActive {
33 | visibility: visible;
34 | }
35 |
36 | .boxtextinput::placeholder {
37 | color: var(--text-input-color);
38 | }
39 |
40 | .boxtextinputWrapper {
41 | font-size: var(--font-size-normal);
42 | position: relative;
43 | }
44 |
45 | .boxtextinputWrapper input {
46 | color: var(--text-color);
47 | }
48 |
49 | .boxtextinputButton {
50 | cursor: pointer;
51 | border: 0;
52 | outline: 0;
53 | position: absolute;
54 | font-size: inherit;
55 | background: none !important;
56 | right: 1rem;
57 | top: calc(50% - 0.5rem);
58 | transform: translate(-50%, -50%);
59 | user-select: none;
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/StatusTag/negative_circle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/stories/assets/code-brackets.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/hooks/useHover.js:
--------------------------------------------------------------------------------
1 | import { useState, useRef, useCallback } from "react";
2 |
3 | // Hook (copied from https://gist.github.com/gragland/a32d08580b7e0604ff02cb069826ca2f)
4 | export default function useHover() {
5 | const [value, setValue] = useState(false);
6 |
7 | // Wrap in useCallback so we can use in dependencies below
8 | const handleMouseOver = useCallback(() => setValue(true), []);
9 | const handleMouseOut = useCallback(() => setValue(false), []);
10 |
11 | // Keep track of the last node passed to callbackRef
12 | // so we can remove its event listeners.
13 | const ref = useRef();
14 |
15 | // Use a callback ref instead of useEffect so that event listeners
16 | // get changed in the case that the returned ref gets added to
17 | // a different element later. With useEffect, changes to ref.current
18 | // wouldn't cause a rerender and thus the effect would run again.
19 | const callbackRef = useCallback(
20 | (node) => {
21 | if (ref.current) {
22 | ref.current.removeEventListener("mouseover", handleMouseOver);
23 | ref.current.removeEventListener("mouseout", handleMouseOut);
24 | }
25 |
26 | ref.current = node;
27 |
28 | if (ref.current) {
29 | ref.current.addEventListener("mouseover", handleMouseOver);
30 | ref.current.addEventListener("mouseout", handleMouseOut);
31 | }
32 | },
33 | [handleMouseOver, handleMouseOut]
34 | );
35 |
36 | return [callbackRef, value];
37 | }
38 |
--------------------------------------------------------------------------------
/src/stories/Button.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button } from "..";
3 | import "./page.css";
4 |
5 | const ButtonObj = {
6 | title: "Design System/Components/Button",
7 | component: Button,
8 | };
9 |
10 | export default ButtonObj;
11 |
12 | const Template = ({ children, ...args }) => (
13 |
14 | );
15 |
16 | export const Primary = Template.bind({});
17 | Primary.args = {
18 | children: "Button",
19 | };
20 |
21 | export const Secondary = Template.bind({});
22 | Secondary.args = {
23 | kind: "secondary",
24 | children: "Button",
25 | };
26 |
27 | export const Disabled = Template.bind({});
28 | Disabled.args = {
29 | kind: "disabled",
30 | children: "Button",
31 | };
32 |
33 | export const Small = Template.bind({});
34 | Small.args = {
35 | kind: "primary",
36 | children: "Button",
37 | size: "sm",
38 | };
39 |
40 | export const Medium = Template.bind({});
41 | Medium.args = {
42 | kind: "primary",
43 | children: "Button",
44 | size: "md",
45 | };
46 |
47 | export const Large = Template.bind({});
48 | Large.args = {
49 | kind: "primary",
50 | children: "Button",
51 | size: "lg",
52 | };
53 |
54 | export const FullWidth = Template.bind({});
55 | FullWidth.args = {
56 | kind: "primary",
57 | children: "Button",
58 | fullWidth: true,
59 | };
60 |
61 | export const Loading = Template.bind({});
62 | Loading.args = {
63 | kind: "primary",
64 | children: "Button",
65 | loading: true,
66 | };
67 |
--------------------------------------------------------------------------------
/src/stories/assets/comments.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Layout/Container.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const Container = ({
7 | children,
8 | style,
9 | className,
10 | topBannerHeight,
11 | headerHeight,
12 | bannerAndMainGap,
13 | singleContent,
14 | ...props
15 | }) => {
16 | const topBannerRowHeight =
17 | typeof topBannerHeight !== "undefined" ? `${topBannerHeight}px` : "14rem";
18 | const headerRowHeight =
19 | typeof headerHeight !== "undefined" ? `${headerHeight}px` : "6rem";
20 | const mainAndBannerGapSize =
21 | typeof bannerAndMainGap !== "undefined" ? `${bannerAndMainGap}px` : "3rem";
22 | const gridRows = singleContent
23 | ? `6rem`
24 | : `${headerRowHeight} ${topBannerRowHeight} ${mainAndBannerGapSize}`;
25 | return (
26 |
33 | {children}
34 |
35 | );
36 | };
37 |
38 | Container.propTypes = {
39 | children: PropTypes.node,
40 | topBannerHeight: PropTypes.number,
41 | headerHeight: PropTypes.number,
42 | bannerAndMainGap: PropTypes.number,
43 | style: PropTypes.object,
44 | singleContent: PropTypes.bool,
45 | className: PropTypes.string,
46 | };
47 |
48 | Container.defaultProps = {
49 | singleContent: false,
50 | };
51 |
52 | export default Container;
53 |
--------------------------------------------------------------------------------
/src/components/Typography/tests/typography.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { create } from "react-test-renderer";
3 | import H1 from "../H1";
4 | import H2 from "../H2";
5 | import H3 from "../H3";
6 | import H4 from "../H4";
7 | import H5 from "../H5";
8 | import H6 from "../H6";
9 | import P from "../P";
10 | import Text from "../Text";
11 |
12 | describe("Typography Elements", () => {
13 | test("H1 Matches Snapshot", () => {
14 | const h1 = create(header1
);
15 | expect(h1.toJSON()).toMatchSnapshot();
16 | });
17 | test("H2 Matches Snapshot", () => {
18 | const h2 = create(header1
);
19 | expect(h2.toJSON()).toMatchSnapshot();
20 | });
21 | test("H3 Matches Snapshot", () => {
22 | const h3 = create(header1
);
23 | expect(h3.toJSON()).toMatchSnapshot();
24 | });
25 | test("H4 Matches Snapshot", () => {
26 | const h4 = create(header1
);
27 | expect(h4.toJSON()).toMatchSnapshot();
28 | });
29 | test("H5 Matches Snapshot", () => {
30 | const h5 = create(header1
);
31 | expect(h5.toJSON()).toMatchSnapshot();
32 | });
33 | test("H6 Matches Snapshot", () => {
34 | const h6 = create(header1
);
35 | expect(h6.toJSON()).toMatchSnapshot();
36 | });
37 | test("P Matches Snapshot", () => {
38 | const p = create(header1
);
39 | expect(p.toJSON()).toMatchSnapshot();
40 | });
41 | test("Text Matches Snapshot", () => {
42 | const text = create(header1);
43 | expect(text.toJSON()).toMatchSnapshot();
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/src/components/CopyableText/tests/__snapshots__/copyableText.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`CopyableText component Matches snapshot 1`] = `
4 |
7 |
11 | test
12 |
13 |
17 |
20 | Copy to clipboard
21 |
22 |
52 |
53 |
54 | `;
55 |
--------------------------------------------------------------------------------
/src/components/Link/Link.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | const DefaultLinkComponent = React.forwardRef(({ children, ...props }, ref) => (
7 |
8 | {children}
9 |
10 | ));
11 |
12 | DefaultLinkComponent.propTypes = {
13 | children: PropTypes.node,
14 | };
15 |
16 | const Link = React.forwardRef(
17 | (
18 | {
19 | gray,
20 | dark,
21 | className,
22 | customComponent,
23 | children,
24 | noHoverEffect,
25 | truncate,
26 | ...props
27 | },
28 | ref
29 | ) => {
30 | const Comp = customComponent || DefaultLinkComponent;
31 | return (
32 |
43 | {children}
44 |
45 | );
46 | }
47 | );
48 |
49 | Link.propTypes = {
50 | customComponent: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
51 | children: PropTypes.node,
52 | gray: PropTypes.bool,
53 | dark: PropTypes.bool,
54 | className: PropTypes.string,
55 | noHoverEffect: PropTypes.bool,
56 | truncate: PropTypes.bool,
57 | style: PropTypes.object,
58 | };
59 |
60 | Link.defaultProps = {
61 | customComponent: null,
62 | };
63 |
64 | export default Link;
65 |
--------------------------------------------------------------------------------
/src/components/StatusTag/StatusTag.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 |
6 | import greenCheckIcon from "./green_check.svg";
7 | import negativeIcon from "./negative.svg";
8 | import negativeBlueIcon from "./negative_blue.svg";
9 | import negativeCircleIcon from "./negative_circle.svg";
10 | import pendingIcon from "./waiting.svg";
11 | import timeYellowIcon from "./time_yellow.svg";
12 | import timeBlackIcon from "./time_black.svg";
13 | import timeBlueIcon from "./time_blue.svg";
14 |
15 | const ICONS_MAP = {
16 | greenCheck: greenCheckIcon,
17 | grayNegative: negativeIcon,
18 | blueNegative: negativeBlueIcon,
19 | orangeNegativeCircled: negativeCircleIcon,
20 | bluePending: pendingIcon,
21 | yellowTime: timeYellowIcon,
22 | blackTime: timeBlackIcon,
23 | blueTime: timeBlueIcon,
24 | };
25 |
26 | const getIcon = (type) => ICONS_MAP[type];
27 |
28 | // TODO: replace icons with SVG sprite file ones when we have them
29 | const StatusTag = ({ type, text, className }) => {
30 | return (
31 |
33 |
34 | {text || type}
35 |
36 | );
37 | };
38 |
39 | StatusTag.propTypes = {
40 | type: PropTypes.oneOf(Object.keys(ICONS_MAP)),
41 | text: PropTypes.node,
42 | className: PropTypes.string,
43 | };
44 |
45 | StatusTag.defaultProps = {
46 | type: "grayNegative",
47 | };
48 |
49 | export default StatusTag;
50 |
--------------------------------------------------------------------------------
/src/components/Tabs/tests/__snapshots__/tabs.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Tabs Component Matches the snapshot 1`] = `
4 | Array [
5 |
8 | -
21 |
24 | tab1
25 |
26 |
29 | 1
30 |
31 |
32 | -
45 |
48 | tab2
49 |
50 |
53 | 4
54 |
55 |
56 |
,
57 |
65 |
66 | test1
67 |
68 |
,
69 | ]
70 | `;
71 |
--------------------------------------------------------------------------------
/src/stories/assets/repo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Dropdown/tests/dropdown.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Dropdown from "../Dropdown";
3 | import DropdownItem from "../DropdownItem";
4 | import { create } from "react-test-renderer";
5 | import { fireEvent, render } from "@testing-library/react";
6 |
7 | describe("Dropdown component", () => {
8 | test("Matches snapshot", () => {
9 | const dropdown = create(
10 |
11 | test1
12 | test2
13 |
14 | );
15 | expect(dropdown.toJSON()).toMatchSnapshot();
16 | });
17 | test("Items list show on click", async () => {
18 | const itemMockClick = jest.fn();
19 | const { getByTestId, queryByTestId, getByText, queryByText } = render(
20 |
21 | test1
22 | test2
23 |
24 | );
25 | const trigger = queryByTestId("trigger");
26 | const itemsList = queryByTestId("items-list");
27 | expect(trigger).toBeTruthy();
28 | expect(itemsList).toBeFalsy();
29 | fireEvent.click(getByTestId("trigger"));
30 | const updatedItemsList = queryByTestId("items-list");
31 | expect(updatedItemsList).toBeTruthy();
32 | const dropdownItem = queryByText(/test1/i);
33 | expect(dropdownItem).toBeTruthy();
34 | fireEvent.click(getByText("test1"));
35 | expect(itemMockClick).toBeCalled();
36 | const afterClickItemsList = queryByTestId("items-list");
37 | expect(afterClickItemsList).toBeFalsy();
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from "rollup-plugin-babel";
2 | import commonjs from "rollup-plugin-commonjs";
3 | import copy from "rollup-plugin-copy";
4 | import external from "rollup-plugin-peer-deps-external";
5 | import postcss from "rollup-plugin-postcss";
6 | import resolve from "rollup-plugin-node-resolve";
7 | import url from "@rollup/plugin-url";
8 | import svgr from "@svgr/rollup";
9 | import { visualizer } from "rollup-plugin-visualizer";
10 | import { uglify } from "rollup-plugin-uglify";
11 | import gzipPlugin from "rollup-plugin-gzip";
12 |
13 | import pkg from "./package.json";
14 |
15 | const isProduction = process.env.NODE_ENV === "production";
16 |
17 | const config = {
18 | input: "src/index.js",
19 | output: [
20 | {
21 | file: pkg.main,
22 | format: "cjs",
23 | sourcemap: "inline",
24 | },
25 | ],
26 | external: ["react-select"],
27 | plugins: [
28 | external(),
29 | postcss({
30 | modules: true,
31 | extract: true,
32 | }),
33 | url({
34 | include: ["**/*.woff", "**/*.ttf", "**/*.png", "**/*.svg"],
35 | limit: Infinity, // This allows files from any size to be bundled. If we want larger files copied
36 | // we need to reduce the limit.
37 | }),
38 | svgr(),
39 | babel({
40 | exclude: "node_modules/**",
41 | }),
42 | resolve(),
43 | commonjs(),
44 | isProduction && uglify(),
45 | isProduction && gzipPlugin(),
46 | copy({
47 | targets: [{ src: "src/css/exports.css", dest: "dist" }],
48 | }),
49 | isProduction && visualizer({ sourcemap: true, gzipSize: true }),
50 | ],
51 | };
52 |
53 | export default config;
54 |
--------------------------------------------------------------------------------
/src/components/Tabs/styles.module.css:
--------------------------------------------------------------------------------
1 | .tab {
2 | display: flex;
3 | align-items: center;
4 | font-size: var(--font-size-normal);
5 | cursor: pointer;
6 | white-space: nowrap;
7 | }
8 |
9 | .tab:hover {
10 | color: var(--tab-text-active-color) !important;
11 | }
12 |
13 | .tabHorizontal {
14 | composes: tab;
15 | padding-bottom: 0.6rem;
16 | margin-right: 5rem;
17 | border-bottom: 0.4rem solid transparent;
18 | }
19 |
20 | .tabVertical {
21 | composes: tab;
22 | min-height: 3rem;
23 | border-left: 0.4rem solid transparent;
24 | padding-left: 1rem;
25 | margin-top: 3rem;
26 | }
27 |
28 | .tabDropdownMode {
29 | composes: tab;
30 | display: flex;
31 | border-left: 0;
32 | padding-left: 0;
33 | margin-top: 2rem;
34 | }
35 |
36 | .tabsNav {
37 | list-style-type: none;
38 | display: flex;
39 | overflow: auto;
40 | }
41 |
42 | .wrap {
43 | flex-wrap: wrap;
44 | }
45 |
46 | .tabsNavVertical {
47 | composes: tabsNav;
48 | flex-direction: column;
49 | }
50 |
51 | .tabCount {
52 | display: flex;
53 | align-items: center;
54 | justify-content: center;
55 | background: var(--tab-count-background);
56 | height: 2rem;
57 | width: 2rem;
58 | margin-left: 0.5rem;
59 | }
60 |
61 | .dropdownArrowClass {
62 | right: 45px;
63 | top: 10px;
64 | }
65 |
66 | .dropdownListClass {
67 | margin-top: 3rem;
68 | }
69 |
70 | .activeDropdownTabClass {
71 | margin: 0 1.5rem 0 0;
72 | padding-left: 1.5rem;
73 | }
74 |
75 | .activeDropdownTabWrapper {
76 | display: flex;
77 | align-items: center;
78 | }
79 |
80 | .customDropdownItem {
81 | padding: 0.5rem;
82 | text-align: center;
83 | justify-content: center;
84 | }
85 |
--------------------------------------------------------------------------------
/src/components/Button/Button.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 | import React from "react";
3 | import { classNames } from "../../utils";
4 | import Spinner from "../Spinner/Spinner.jsx";
5 | import styles from "./styles.module.css";
6 |
7 | const Button = ({
8 | children,
9 | className,
10 | style,
11 | kind,
12 | size,
13 | icon,
14 | onClick,
15 | loading,
16 | fullWidth,
17 | noBorder,
18 | width,
19 | type,
20 | ...props
21 | }) => (
22 |
38 | );
39 |
40 | Button.propTypes = {
41 | children: PropTypes.node.isRequired,
42 | className: PropTypes.string,
43 | style: PropTypes.object,
44 | onClick: PropTypes.func,
45 | loading: PropTypes.bool,
46 | fullWidth: PropTypes.bool,
47 | icon: PropTypes.bool,
48 | width: PropTypes.number,
49 | size: PropTypes.oneOf(["sm", "md", "lg"]),
50 | type: PropTypes.oneOf(["button", "submit"]),
51 | kind: PropTypes.oneOf(["primary", "secondary", "disabled"]),
52 | noBorder: PropTypes.bool,
53 | };
54 |
55 | Button.defaultProps = {
56 | kind: "primary",
57 | size: "md",
58 | type: "button",
59 | icon: false,
60 | loading: false,
61 | fullWidth: false,
62 | noBorder: false,
63 | };
64 |
65 | export default Button;
66 |
--------------------------------------------------------------------------------
/src/stories/TextInput.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { TextInput, Icon } from "..";
3 | import "./page.css";
4 |
5 | const TextInputObj = {
6 | title: "Design System/Components/TextInput",
7 | component: TextInput,
8 | };
9 |
10 | export default TextInputObj;
11 |
12 | const Template = ({ children, ...args }) => (
13 | {children}
14 | );
15 |
16 | export const Basic = Template.bind({});
17 | Basic.args = {
18 | id: "test-input",
19 | label: "Input",
20 | name: "test-input",
21 | error: "",
22 | success: "",
23 | };
24 |
25 | export const Password = Template.bind({});
26 | Password.args = {
27 | id: "test-password",
28 | label: "Password",
29 | name: "test-password",
30 | type: "password",
31 | error: "",
32 | success: "",
33 | };
34 |
35 | export const Error = Template.bind({});
36 | Error.args = {
37 | id: "test-input2",
38 | label: "Input",
39 | name: "test-input2",
40 | error: "Incorrect input",
41 | success: "",
42 | };
43 |
44 | export const Success = Template.bind({});
45 | Success.args = {
46 | id: "test-input3",
47 | label: "Input",
48 | name: "test-input3",
49 | success: "Correct input",
50 | error: "",
51 | };
52 |
53 | export const Placeholder = Template.bind({});
54 | Placeholder.args = {
55 | id: "test-input4",
56 | label: "Input",
57 | name: "test-input4",
58 | placeholder: "Write here",
59 | error: "",
60 | success: "",
61 | };
62 |
63 | export const WithChildren = Template.bind({});
64 | WithChildren.args = {
65 | id: "test-input4",
66 | label: "Input",
67 | name: "test-input4",
68 | children: ,
69 | error: "",
70 | success: "",
71 | };
72 |
--------------------------------------------------------------------------------
/src/components/RadioButtonGroup/tests/radiobuttongroup.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { create } from "react-test-renderer";
3 | import { render, fireEvent } from "@testing-library/react";
4 | import { RadioButtonGroup } from "../RadioButtonGroup";
5 |
6 | const options = [
7 | { label: "foo", value: 1 },
8 | { label: "bar", value: 2 },
9 | ];
10 | describe("RadioButtonGroup component", () => {
11 | test("Matches snapshot", () => {
12 | const radiogroup = create(
13 |
19 | );
20 | expect(radiogroup.toJSON()).toMatchSnapshot();
21 | });
22 |
23 | test("Matches snapshot (set name property)", () => {
24 | const radiogroup = create(
25 |
32 | );
33 | expect(radiogroup.toJSON()).toMatchSnapshot();
34 | });
35 |
36 | test("RadioButton click triggers change function and updates value", () => {
37 | let value = 1;
38 | const mockHandleChange = jest.fn(() => {
39 | value = 2;
40 | });
41 | const { getByLabelText, queryByText } = render(
42 |
48 | );
49 | expect(queryByText(/foo/i)).toBeTruthy();
50 | expect(queryByText(/bar/i)).toBeTruthy();
51 | fireEvent.click(getByLabelText(/bar/i));
52 | expect(mockHandleChange).toBeCalled();
53 | expect(value).toBe(2);
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/src/utils.test.js:
--------------------------------------------------------------------------------
1 | import { classNames, isString, isObject } from "./utils";
2 |
3 | describe("classNames util", () => {
4 | it("returns empty if argument is falsy", () => {
5 | expect(classNames(undefined)).toBe("");
6 | expect(classNames(null)).toBe("");
7 | expect(classNames("")).toBe("");
8 | });
9 | it("returns a string with the string args separated by 1 space", () => {
10 | expect(classNames("test", "abc", "def")).toBe("test abc def");
11 | });
12 | it("does not include falsy values in the response", () => {
13 | expect(classNames(undefined, null, "test")).toBe("test");
14 | });
15 | it("does not include non-string values in the response", () => {
16 | expect(classNames("test", ["abc"], { key: "def" })).toBe("test");
17 | });
18 | });
19 |
20 | describe("isString util", () => {
21 | it("returns true if argument is a string", () => {
22 | expect(isString("abc")).toBeTruthy();
23 | expect(isString("")).toBeTruthy();
24 | });
25 | it("returns false if argument is not string", () => {
26 | expect(isString(["abc"])).toBeFalsy();
27 | expect(isString({})).toBeFalsy();
28 | expect(isString(null)).toBeFalsy();
29 | expect(isString(undefined)).toBeFalsy();
30 | });
31 | });
32 |
33 | describe("isObject util", () => {
34 | it("returns true if argument is an object", () => {
35 | expect(isObject({})).toBeTruthy();
36 | expect(isObject({ key: "value" })).toBeTruthy();
37 | });
38 | it("returns false if argument is not an object", () => {
39 | expect(isObject(["abc"])).toBeFalsy();
40 | expect(isObject("test")).toBeFalsy();
41 | expect(isObject(3)).toBeFalsy();
42 | expect(isObject(null)).toBeFalsy();
43 | expect(isObject(undefined)).toBeFalsy();
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/src/components/Message/Message.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import Card from "../Card/Card.jsx";
4 | import styles from "./styles.module.css";
5 | import Icon from "../Icon/Icon.jsx";
6 | import { classNames } from "../../utils";
7 | import H2 from "../Typography/H2.jsx";
8 |
9 | const typeToIcons = {
10 | info: "info",
11 | warning: "alert",
12 | success: "checkmark",
13 | error: "alert",
14 | blocked: "blocked",
15 | };
16 |
17 | const Message = ({
18 | style,
19 | className,
20 | iconContainerClassName,
21 | children,
22 | kind,
23 | title,
24 | ...props
25 | }) => {
26 | const renderIcon = () => (
27 |
32 | );
33 | return (
34 |
42 | {kind && (
43 |
45 | {renderIcon()}
46 |
47 | )}
48 |
49 | {!!title &&
{title}
}
50 | {children}
51 |
52 |
53 | );
54 | };
55 |
56 | Message.propTypes = {
57 | style: PropTypes.object,
58 | className: PropTypes.string,
59 | iconContainerClassName: PropTypes.string,
60 | children: PropTypes.node.isRequired,
61 | kind: PropTypes.oneOf(["info", "warning", "error", "success", "blocked"]),
62 | title: PropTypes.node,
63 | };
64 | Message.defaultProps = {
65 | kind: "info",
66 | };
67 |
68 | export default Message;
69 |
--------------------------------------------------------------------------------
/src/components/Typography/Text/styles.module.css:
--------------------------------------------------------------------------------
1 | .text {
2 | word-wrap: break-word;
3 | }
4 |
5 | .textAlignCenter {
6 | text-align: center;
7 | }
8 |
9 | .textAlignStart {
10 | text-align: left;
11 | }
12 |
13 | .textAlignEnd {
14 | text-align: right;
15 | }
16 |
17 | .sizeSmall {
18 | font-size: var(--font-size-small);
19 | line-height: var(--spacing-1);
20 | }
21 |
22 | .sizeNormal {
23 | font-size: var(--font-size-normal);
24 | line-height: var(--spacing-2);
25 | }
26 |
27 | .sizeLarge {
28 | font-size: var(--font-size-xxlarge);
29 | line-height: var(--spacing-3);
30 | }
31 |
32 | .sizeXLarge {
33 | font-size: var(--font-size-xxxlarge);
34 | line-height: var(--spacing-4);
35 | }
36 |
37 | .weightLight {
38 | font-weight: var(--font-weight-light);
39 | }
40 |
41 | .weightRegular {
42 | font-weight: var(--font-weight-regular);
43 | }
44 |
45 | .weightSemiBold {
46 | font-weight: var(--font-weight-semi-bold);
47 | }
48 |
49 | .weightBold {
50 | font-weight: var(--font-weight-bold);
51 | }
52 |
53 | .colorDefault {
54 | color: var(--text-color);
55 | }
56 |
57 | .colorPrimary {
58 | color: var(--color-primary);
59 | }
60 |
61 | .colorPrimaryDark {
62 | color: var(--color-primary-dark);
63 | }
64 |
65 | .colorGray {
66 | color: var(--color-gray);
67 | }
68 |
69 | .colorGrayDark {
70 | color: var(--color-gray-dark);
71 | }
72 |
73 | .colorGreen {
74 | color: var(--color-green);
75 | }
76 |
77 | .colorYellow {
78 | color: var(--color-yellow);
79 | }
80 |
81 | .colorOrange {
82 | color: var(--color-orange);
83 | }
84 |
85 | .bgColorBlueLighter {
86 | padding: 0.4rem 1rem;
87 | border-radius: 0.5rem;
88 | background-color: var(--copyable-text-background-color);
89 | }
90 |
91 | .monospace {
92 | font-family: monospace;
93 | }
94 |
--------------------------------------------------------------------------------
/src/components/Tooltip/Tooltip.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import PropTypes from "prop-types";
3 | import styles from "./styles.module.css";
4 | import { classNames } from "../../utils";
5 | import useMediaQuery from "../../hooks/useMediaQuery";
6 | import useClickOutside from "../../hooks/useClickOutside";
7 |
8 | const noop = () => ({});
9 |
10 | const Tooltip = ({
11 | children,
12 | content,
13 | placement,
14 | className,
15 | contentClassName,
16 | ...props
17 | }) => {
18 | // Uses "coarse" pointer to check if device is mobile and then enable tooltip
19 | // clickable toggle.
20 | const isMobile = useMediaQuery("(pointer: coarse)");
21 | const [isActive, toggleIsActive] = useState(false);
22 | const showTooltip = () => toggleIsActive(true);
23 | const hideTooltip = () => toggleIsActive(false);
24 | const [tooltipRef] = useClickOutside(hideTooltip);
25 | return (
26 |
31 |
38 | {content}
39 |
40 | {children}
41 |
42 | );
43 | };
44 |
45 | Tooltip.propTypes = {
46 | children: PropTypes.node,
47 | content: PropTypes.node,
48 | placement: PropTypes.oneOf(["top", "bottom", "right", "left"]),
49 | className: PropTypes.string,
50 | contentClassName: PropTypes.string,
51 | style: PropTypes.object,
52 | };
53 |
54 | Tooltip.defaultProps = {
55 | placement: "top",
56 | content: "content",
57 | };
58 |
59 | export default Tooltip;
60 |
--------------------------------------------------------------------------------
/src/hooks/useTruncate.js:
--------------------------------------------------------------------------------
1 | import { useLayoutEffect } from "react";
2 | import lineClamp from "clamp-js-main";
3 |
4 | /**
5 | * setStylesOnElement merges the provided styles with the target element
6 | * styles.
7 | * @param {Object} styles
8 | * @param {Object} element
9 | */
10 | const setStylesOnElement = (styles, element) => {
11 | Object.assign(element.style, styles);
12 | };
13 |
14 | /**
15 | * Simple clamp handles the clampping when there is only a single line to truncate.
16 | * This has a better cross-browse effect.
17 | * @param {Object} element
18 | */
19 | const simpleClamp = (element) => {
20 | setStylesOnElement(
21 | {
22 | display: "block",
23 | overflow: "hidden",
24 | whiteSpace: "nowrap",
25 | textOverflow: "ellipsis",
26 | },
27 | element
28 | );
29 | };
30 |
31 | /**
32 | * useTruncate is a hook which will truncate the element with the provided ID.
33 | * It is possible to specify after how many lines the truncate effect will be
34 | * applied though the 'linesBeforeTruncate' parameter.
35 | * @param {String} elementID
36 | * @param {Boolean} truncate
37 | * @param {number} linesBeforeTruncate
38 | */
39 |
40 | const useTruncate = (elementID, truncate, linesBeforeTruncate) => {
41 | useLayoutEffect(() => {
42 | if (truncate) {
43 | const element = document.getElementById(elementID);
44 | if (element) {
45 | if (linesBeforeTruncate === 1) {
46 | simpleClamp(element);
47 | } else {
48 | lineClamp.clamp
49 | ? lineClamp.clamp(element, { clamp: linesBeforeTruncate })
50 | : lineClamp(element, { clamp: linesBeforeTruncate });
51 | }
52 | }
53 | }
54 | }, [truncate, linesBeforeTruncate, elementID]);
55 | };
56 |
57 | export default useTruncate;
58 |
--------------------------------------------------------------------------------
/src/components/Table/styles.module.css:
--------------------------------------------------------------------------------
1 | .tableWrapper {
2 | padding-bottom: 2rem;
3 | }
4 |
5 | .table {
6 | table-layout: fixed;
7 | border-spacing: 0;
8 | border-collapse: collapse;
9 | width: 100%;
10 | }
11 |
12 | .paginator {
13 | margin-top: 3rem;
14 | justify-content: flex-end;
15 | }
16 |
17 | .tableCell {
18 | max-width: 12rem;
19 | min-width: 12rem;
20 | overflow-x: hidden;
21 | text-align: center;
22 | text-overflow: ellipsis;
23 | }
24 |
25 | .tableHeadCell {
26 | composes: tableCell;
27 | height: 5.2rem;
28 | }
29 |
30 | .tableBodyCell {
31 | composes: tableCell;
32 | height: 5rem;
33 | border-bottom: 0.1rem solid var(--table-cell-border-color);
34 | }
35 |
36 | .tableHead {
37 | background-color: var(--table-header-background);
38 | }
39 |
40 | @media (--sm-viewport) {
41 | .table {
42 | border: 0;
43 | }
44 |
45 | .tableHead {
46 | border: none;
47 | clip: rect(0 0 0 0);
48 | height: 1px;
49 | margin: -1px;
50 | overflow: hidden;
51 | padding: 0;
52 | position: absolute;
53 | width: 1px;
54 | }
55 |
56 | .tableRow {
57 | border: 0.1rem solid #f3f5f6;
58 | padding: 1.3rem 2rem;
59 | display: block;
60 | margin-bottom: 1rem;
61 | }
62 |
63 | .tableCell {
64 | max-width: 100%;
65 | }
66 |
67 | .tableBodyCellText {
68 | overflow-x: hidden;
69 | max-width: 20rem;
70 | text-overflow: ellipsis;
71 | }
72 |
73 | .tableBodyCell {
74 | display: flex;
75 | align-items: center;
76 | border: 0;
77 | justify-content: space-between;
78 | font-size: 1.35rem;
79 | }
80 |
81 | td.tableBodyCell:not(:last-child) {
82 | border-bottom: 0.1rem solid #f3f5f6;
83 | }
84 |
85 | .tableBodyCell::before {
86 | content: attr(data-label);
87 | font-weight: bold;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/stories/RadioButton.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { RadioButtonGroup } from "..";
3 | import "./page.css";
4 |
5 | const RadioButtonGroupObj = {
6 | title: "Design System/Components/RadioButtonGroup",
7 | component: RadioButtonGroup,
8 | };
9 |
10 | export default RadioButtonGroupObj;
11 |
12 | const Template = ({ children, ...args }) => {
13 | const [selectedValue, setSelectedValue] = React.useState(args.value);
14 | args.value = selectedValue;
15 | args.onChange = (e) => {
16 | setSelectedValue(e.value);
17 | };
18 | return ;
19 | };
20 |
21 | export const Basic = Template.bind({});
22 | Basic.args = {
23 | label: "Select an option",
24 | vertical: true,
25 | options: [
26 | { label: "foo", value: 1, description: "this is foo!" },
27 | { label: "bar", value: 2, description: "this is bar!" },
28 | ],
29 | };
30 |
31 | export const OneSelected = Template.bind({});
32 | OneSelected.args = {
33 | label: "Select an option",
34 | vertical: true,
35 | value: 1,
36 | options: [
37 | { label: "foo", value: 1, description: "this is foo!" },
38 | { label: "bar", value: 2, description: "this is bar!" },
39 | ],
40 | };
41 |
42 | export const Disabled = Template.bind({});
43 | Disabled.args = {
44 | label: "Select an option",
45 | vertical: true,
46 | disabled: true,
47 | options: [
48 | { label: "foo", value: 1, description: "this is foo!" },
49 | { label: "bar", value: 2, description: "this is bar!" },
50 | ],
51 | };
52 |
53 | export const Horizontal = Template.bind({});
54 | Horizontal.args = {
55 | label: "Select an option",
56 | vertical: false,
57 | options: [
58 | { label: "foo", value: 1, description: "this is foo!" },
59 | { label: "bar", value: 2, description: "this is bar!" },
60 | ],
61 | };
62 |
--------------------------------------------------------------------------------
/src/components/TextArea/styles.module.css:
--------------------------------------------------------------------------------
1 | .textArea {
2 | font-family: var(--font-family-text);
3 | font-weight: var(--font-weight-regular);
4 | max-width: 100%;
5 | min-width: 100%;
6 | padding: 2rem;
7 | min-height: 20rem;
8 | border: 1px solid var(--text-input-color);
9 | line-height: 1.5;
10 | font-size: var(--font-size-regular);
11 | background: var(--card-background);
12 | -webkit-appearance: none;
13 | color: var(--text-color);
14 | }
15 |
16 | .textAreaWrapper {
17 | width: 100%;
18 | position: relative;
19 | }
20 |
21 | .textAreaWrapper:not(:first-child) {
22 | margin-top: 2.2rem;
23 | }
24 |
25 | .textArea:focus,
26 | .textArea:not(:placeholder-shown) {
27 | top: -2rem;
28 | font-size: var(--font-size-small);
29 | border-bottom: 0.1rem solid var(--color-primary);
30 | }
31 |
32 | .errorMsg {
33 | visibility: hidden;
34 | color: var(--color-orange);
35 | font-size: var(--font-size-small);
36 | min-height: var(--spacing-1);
37 | line-height: var(--spacing-1);
38 | margin-top: 1px;
39 | }
40 |
41 | .errorMsgActive {
42 | visibility: visible;
43 | }
44 |
45 | .errorIcon {
46 | display: none;
47 | color: var(--color-orange);
48 | position: absolute;
49 | right: 0.5rem;
50 | top: 0.5rem;
51 | font-size: var(--font-size-large);
52 | }
53 |
54 | .errorIconActive {
55 | display: block;
56 | }
57 |
58 | /* override chrome's autocomplete styling */
59 |
60 | @keyframes autofill {
61 | to {
62 | color: white;
63 | background: transparent;
64 | }
65 | }
66 |
67 | .textArea:-webkit-autofill {
68 | transition-delay: 99999s;
69 | -webkit-transition-delay: 99999s;
70 | animation-name: "autofill";
71 | -webkit-animation-name: "autofill";
72 | animation-fill-mode: both;
73 | -webkit-animation-fill-mode: both;
74 | -webkit-text-fill-color: var(--text-color) !important;
75 | }
76 |
--------------------------------------------------------------------------------
/src/components/Modal/styles.module.css:
--------------------------------------------------------------------------------
1 | .modalWrapper {
2 | height: 100vh;
3 | width: 100%;
4 | position: fixed;
5 | top: 0;
6 | left: 0;
7 | display: flex;
8 | justify-content: center;
9 | align-items: flex-start;
10 | padding-top: 15vh;
11 | z-index: var(--z-index-big);
12 | transition: all 0.2s;
13 | background-color: rgba(0, 0, 0, 0.3);
14 | visibility: hidden;
15 | opacity: 0;
16 | }
17 |
18 | .modalWrapperVisible {
19 | composes: modalWrapper;
20 | visibility: visible;
21 | opacity: 1;
22 | }
23 |
24 | .modal {
25 | min-width: 50rem;
26 | max-width: 100rem;
27 | max-height: 70vh;
28 | padding: 2.5rem 4rem 3rem 4rem;
29 | position: relative;
30 | background: var(--card-background);
31 | transition: transform 0.5s;
32 | transform: scale(0.3);
33 | overflow-y: auto;
34 | display: flex;
35 | }
36 |
37 | .modalContent {
38 | width: 100%;
39 | height: 100%;
40 | }
41 |
42 | .modalVisible {
43 | composes: modal;
44 | transform: scale(1);
45 | }
46 |
47 | .modalClose {
48 | background: none;
49 | border: none;
50 | cursor: pointer;
51 | position: absolute;
52 | font-size: var(--font-size-xxlarge);
53 | top: 2rem;
54 | right: 3rem;
55 | color: var(--modal-close-color);
56 | transition: all 0.3s;
57 | }
58 |
59 | .modalClose:hover {
60 | background: none;
61 | color: var(--color-primary);
62 | }
63 |
64 | .modalTitle {
65 | margin-bottom: var(--spacing-2);
66 | }
67 |
68 | .iconWrapper {
69 | margin-right: 1rem;
70 | }
71 |
72 | .iconWrapper.lg {
73 | padding-top: 8px;
74 | }
75 |
76 | .iconWrapper.xlg {
77 | padding-top: 4px;
78 | }
79 |
80 | @media (--xs-viewport) {
81 | .modal {
82 | min-width: 100%;
83 | min-height: 100%;
84 | padding: 4rem 2.5rem;
85 | padding-bottom: 3rem;
86 | }
87 |
88 | .modalWrapper {
89 | padding-top: 0;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/components/Datepicker/test/datepicker.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import DatePicker from "../Datepicker";
3 | import { create } from "react-test-renderer";
4 | import { render } from "@testing-library/react";
5 |
6 | describe("DatePicker component", () => {
7 | test("Matches the snapshot", () => {
8 | const datePicker = create(
9 | {}}
12 | />
13 | );
14 | expect(datePicker.toJSON()).toMatchSnapshot();
15 | });
16 |
17 | test("lang prop values are used as months", () => {
18 | const years = {
19 | min: { year: 2018, month: 1, day: 25 },
20 | max: { year: 2020, month: 2, day: 4 },
21 | };
22 | const monthsTranslations = [
23 | "Jan",
24 | "Feb",
25 | "Mar",
26 | "Apr",
27 | "May",
28 | "Jun",
29 | "Jul",
30 | "Aug",
31 | "Sep",
32 | "Oct",
33 | "Nov",
34 | "Dec",
35 | ];
36 | const { queryByText, rerender, unmount } = render(
37 | {}}
41 | value={{ year: 2019, month: 11, day: 15 }}
42 | show={true}>
43 | <>>
44 |
45 | );
46 | // Selected value is 15.11.2019 => Nov month is displayed.
47 | expect(queryByText(/Nov/i)).toBeTruthy();
48 | unmount();
49 | rerender(
50 | {}}
54 | value={{ year: 2019, month: 12, day: 15 }}
55 | show={true}>
56 | <>>
57 |
58 | );
59 | // Selected value is 15.12.2019 => Nov month is displayed.
60 | expect(queryByText(/Dec/i)).toBeTruthy();
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | // https://jestjs.io/docs/en/configuration.html
2 |
3 | module.exports = {
4 | // Automatically clear mock calls and instances between every test
5 | clearMocks: true,
6 |
7 | // An array of glob patterns indicating a set of files for which coverage information should be collected
8 | collectCoverageFrom: ["src/**/*.{js,jsx,mjs}"],
9 |
10 | // The directory where Jest should output its coverage files
11 | coverageDirectory: "coverage",
12 |
13 | // An array of file extensions your modules use
14 | moduleFileExtensions: ["js", "json", "jsx"],
15 |
16 | // The test environment that will be used for testing
17 | testEnvironment: "jsdom",
18 |
19 | // The glob patterns Jest uses to detect test files
20 | testMatch: ["**/__tests__/**/*.js", "**/?(*.)+(test).js"],
21 |
22 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
23 | testPathIgnorePatterns: ["\\\\node_modules\\\\"],
24 |
25 | // This mocks the CSS Modules classes so JEST doesn't fail to import them. Also useful for snapshot testing (different class names don't mess up with the snapshot diff)
26 | moduleNameMapper: {
27 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
28 | "/__mocks__/fileMock.js",
29 | "\\.(css|less)$": "identity-obj-proxy",
30 | },
31 |
32 | setupFilesAfterEnv: ["./jest.setup.js"],
33 |
34 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
35 | testURL: "http://localhost",
36 |
37 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
38 | transformIgnorePatterns: ["/node_modules/"],
39 |
40 | // Indicates whether each individual test should be reported during the run
41 | verbose: false,
42 | };
43 |
--------------------------------------------------------------------------------
/src/components/BoxTextInput/tests/boxTextInput.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import BoxTextInput from "../BoxTextInput";
3 | import { create } from "react-test-renderer";
4 | import {
5 | defaultLightTheme,
6 | ThemeProvider,
7 | DEFAULT_LIGHT_THEME_NAME,
8 | } from "../../../theme";
9 | import { fireEvent, render } from "@testing-library/react";
10 |
11 | describe("BoxTextInput component", () => {
12 | test("Matches the snapshot", () => {
13 | const boxTextInputForm = create(
14 |
17 |
18 |
19 | );
20 | expect(boxTextInputForm.toJSON()).toMatchSnapshot();
21 |
22 | const boxTextInputDefault = create(
23 |
26 |
27 |
28 | );
29 | expect(boxTextInputDefault.toJSON()).toMatchSnapshot();
30 | });
31 |
32 | test("Calls onSubmit() prop on submit button click", () => {
33 | const mockedOnSubmit = jest.fn();
34 |
35 | const { getByTestId, queryByTestId } = render(
36 |
39 |
40 |
41 | );
42 |
43 | // submit button rendered
44 | expect(queryByTestId("submit-button")).toBeTruthy();
45 |
46 | // click submit button
47 | fireEvent.click(getByTestId("submit-button"));
48 |
49 | // expect onSubmit to be called
50 | expect(mockedOnSubmit).toHaveBeenCalledTimes(1);
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/src/components/Table/tests/__snapshots__/table.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Table Component Matches the snapshot 1`] = `
4 |
7 |
10 |
13 |
16 | |
20 | Test1
21 | |
22 |
26 | Test2
27 | |
28 |
29 |
30 |
31 |
34 | |
38 |
41 | testl1c1
42 |
43 | |
44 |
48 |
51 | testl1c2
52 |
53 | |
54 |
55 |
58 | |
62 |
65 | testl2c1
66 |
67 | |
68 |
72 |
75 | testl2c2
76 |
77 | |
78 |
79 |
80 |
81 |
82 | `;
83 |
--------------------------------------------------------------------------------
/src/components/TextArea/TextArea.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 | import React from "react";
3 | import { classNames } from "../../utils";
4 | import Icon from "../Icon/Icon.jsx";
5 | import styles from "./styles.module.css";
6 |
7 | const TextArea = ({
8 | type,
9 | id,
10 | error,
11 | wrapperClassNames,
12 | inputClassNames,
13 | wrapped,
14 | ...props
15 | }) => {
16 | return !wrapped ? (
17 |
18 |
29 |
38 |
40 | {error}
41 |
42 |
43 | ) : (
44 |
55 | );
56 | };
57 |
58 | TextArea.propTypes = {
59 | type: PropTypes.string,
60 | id: PropTypes.string.isRequired,
61 | error: PropTypes.node,
62 | wrapperClassNames: PropTypes.string,
63 | inputClassNames: PropTypes.string,
64 | placeholder: PropTypes.node,
65 | wrapped: PropTypes.bool,
66 | style: PropTypes.object,
67 | };
68 |
69 | TextArea.defaultProps = {
70 | type: "text",
71 | placeholder: "",
72 | };
73 |
74 | export default TextArea;
75 |
--------------------------------------------------------------------------------
/src/components/Typography/styles.module.css:
--------------------------------------------------------------------------------
1 | .textSourceSansPro {
2 | font-family: var(--font-family-text);
3 | }
4 |
5 | .header1 {
6 | composes: textSourceSansPro;
7 | font-size: var(--font-size-xxxlarge);
8 | font-weight: var(--font-weight-regular);
9 | line-height: var(--spacing-4);
10 | color: var(--text-heading-color);
11 | }
12 |
13 | .header2 {
14 | composes: textSourceSansPro;
15 | font-size: var(--font-size-xxlarge);
16 | font-weight: var(--font-weight-semi-bold);
17 | line-height: var(--spacing-3);
18 | color: var(--text-heading2-color);
19 | }
20 |
21 | .header3 {
22 | composes: textSourceSansPro;
23 | font-size: var(--font-size-xlarge);
24 | font-weight: var(--font-weight-semi-bold);
25 | line-height: var(--spacing-3);
26 | color: var(--color-primary-dark);
27 | }
28 |
29 | .header4 {
30 | composes: textSourceSansPro;
31 | font-size: var(--font-size-large);
32 | font-weight: var(--font-weight-semi-bold);
33 | line-height: var(--spacing-2);
34 | color: var(--color-primary-dark);
35 | }
36 |
37 | .header5 {
38 | composes: textSourceSansPro;
39 | font-size: var(--font-size-normal);
40 | font-weight: var(--font-weight-semi-bold);
41 | line-height: var(--spacing-2);
42 | color: var(--color-primary-dark);
43 | }
44 |
45 | .header6 {
46 | composes: textSourceSansPro;
47 | font-size: var(--font-size-small);
48 | font-weight: var(--font-weight-semi-bold);
49 | line-height: var(--spacing-1);
50 | color: var(--color-primary-dark);
51 | }
52 |
53 | .paragraph {
54 | composes: textSourceSansPro;
55 | font-size: var(--font-size-normal);
56 | font-weight: var(--font-weight-regular);
57 | line-height: var(--spacing-2);
58 | hyphens: auto;
59 | }
60 |
61 | .header1,
62 | .header2,
63 | .header3,
64 | .header4,
65 | .header5,
66 | .header6 {
67 | word-wrap: break-word;
68 | }
69 |
70 | .paragraph:not(:last-of-type) {
71 | margin-bottom: var(--spacing-2);
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/BoxTextInput/tests/__snapshots__/boxTextInput.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`BoxTextInput component Matches the snapshot 1`] = `
4 |
37 | `;
38 |
39 | exports[`BoxTextInput component Matches the snapshot 2`] = `
40 |
52 | `;
53 |
--------------------------------------------------------------------------------
/src/components/RadioButtonGroup/styles.module.css:
--------------------------------------------------------------------------------
1 | .radioButton {
2 | display: inline-block;
3 | position: relative;
4 | padding: 0 0.6rem;
5 | margin: 1rem 1rem 0 0;
6 | user-select: none;
7 | box-sizing: border-box;
8 | }
9 |
10 | .radioButton .circle {
11 | display: inline-block;
12 | position: relative;
13 | top: 0.2rem;
14 | margin: 0 0.5rem 0 0;
15 | width: 1.4rem;
16 | height: 1.4rem;
17 | border-radius: 50%;
18 | border: 0.1rem solid var(--radio-button-stroke-color);
19 | background-color: transparent;
20 | }
21 |
22 | .checked {
23 | border: 0.1rem solid var(--radio-button-selected-stroke-color) !important;
24 | }
25 |
26 | .radioButton .dot {
27 | border-radius: 50%;
28 | width: 0.6rem;
29 | height: 0.6rem;
30 | position: absolute;
31 | top: calc(50% - 0.3rem);
32 | left: calc(50% - 0.3rem);
33 | background: var(--radio-button-dot-color);
34 | }
35 |
36 | .radioButton.disabled,
37 | .radioButton.disabled .dot,
38 | .radioButton.disabled .circle {
39 | cursor: not-allowed;
40 | color: var(--color-gray-light);
41 | }
42 |
43 | .radioButton.disabled .dot {
44 | background: var(--radio-button-dot-color);
45 | opacity: 0.5;
46 | }
47 |
48 | .radioButton.disabled .circle {
49 | border: 0.1rem solid var(--radio-button-stroke-color);
50 | opacity: 0.5;
51 | }
52 |
53 | .radioButton input[type="radio"] {
54 | opacity: 0;
55 | position: fixed;
56 | }
57 |
58 | .radioButton input[type="radio"]:disabled {
59 | pointer-events: none;
60 | }
61 |
62 | .radioGroupList {
63 | list-style-type: none;
64 | display: flex;
65 | }
66 |
67 | .vertical {
68 | display: block;
69 | }
70 |
71 | .radioGroupLabel {
72 | font-size: var(--font-size-small);
73 | color: var(--text-secondary-color);
74 | }
75 |
76 | .radioButtonOptionLabel {
77 | color: var(--radio-button-option-label-color);
78 | }
79 |
80 | .radioGroup.disabled .radioButtonOptionLabel {
81 | color: var(--radio-button-option-label-disabled-color);
82 | }
83 |
84 | .radioButtonDescription {
85 | margin-left: 1.9rem;
86 | }
87 |
--------------------------------------------------------------------------------
/src/stories/Select.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Select } from "..";
3 | import "./page.css";
4 |
5 | const SelectObj = {
6 | title: "Design System/Components/Select",
7 | component: Select,
8 | };
9 |
10 | export default SelectObj;
11 |
12 | const Template = ({ children, ...args }) => ;
13 |
14 | export const Basic = Template.bind({});
15 | Basic.args = {
16 | options: [
17 | {
18 | value: "top",
19 | label: "Top",
20 | },
21 | {
22 | value: "new",
23 | label: "New",
24 | },
25 | {
26 | value: "old",
27 | label: "Old",
28 | },
29 | ],
30 | };
31 |
32 | export const Small = Template.bind({});
33 | Small.args = {
34 | width: 100,
35 | options: [
36 | {
37 | value: "top",
38 | label: "Top",
39 | },
40 | {
41 | value: "new",
42 | label: "New",
43 | },
44 | {
45 | value: "old",
46 | label: "Old",
47 | },
48 | ],
49 | };
50 |
51 | export const Mobile = Template.bind({});
52 | Mobile.args = {
53 | isMobile: true,
54 | options: [
55 | {
56 | value: "top",
57 | label: "Top",
58 | },
59 | {
60 | value: "new",
61 | label: "New",
62 | },
63 | {
64 | value: "old",
65 | label: "Old",
66 | },
67 | ],
68 | };
69 |
70 | const customSt = {
71 | control: () => ({
72 | borderRadius: "none",
73 | borderLeft: "none",
74 | borderRight: "none",
75 | borderTop: "none",
76 | }),
77 | dropdownIndicator: () => ({
78 | paddingRight: 0,
79 | }),
80 | };
81 |
82 | export const CustomStyles = Template.bind({});
83 | CustomStyles.args = {
84 | customStyles: customSt,
85 | options: [
86 | {
87 | value: "top",
88 | label: "Top",
89 | },
90 | {
91 | value: "new",
92 | label: "New",
93 | },
94 | {
95 | value: "old",
96 | label: "Old",
97 | },
98 | ],
99 | };
100 |
--------------------------------------------------------------------------------
/src/stories/ButtonIcon.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { ButtonIcon } from "..";
3 | import "./page.css";
4 | import styles from "./buttonicon.module.css";
5 |
6 | const ButtonIconObj = {
7 | title: "Design System/Components/ButtonIcon",
8 | component: ButtonIcon,
9 | };
10 |
11 | export default ButtonIconObj;
12 |
13 | const Template = ({ children, ...args }) => ;
14 |
15 | export const Basic = () => (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
29 |
30 |
35 |
40 |
41 |
42 |
43 | );
44 |
45 | export const Disabled = Template.bind({});
46 |
47 | Disabled.args = {
48 | disabled: "true",
49 | type: "link",
50 | };
51 |
52 | export const Loading = Template.bind({});
53 |
54 | Loading.args = {
55 | loading: "true",
56 | type: "link",
57 | };
58 |
59 | export const WithText = Template.bind({});
60 |
61 | WithText.args = {
62 | type: "create",
63 | iconColor: "#2970FF",
64 | text: "Create a New Wallet",
65 | };
66 |
67 | export const WithTooltip = Template.bind({});
68 |
69 | WithTooltip.args = {
70 | type: "create",
71 | iconColor: "#2970FF",
72 | tooltipText: "Create a New Wallet",
73 | tooltipPlacement: "bottom",
74 | };
75 |
--------------------------------------------------------------------------------
/src/components/Select/test/__snapshots__/select.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Select component Matches the snapshot 1`] = `
4 |
5 |
9 |
13 |
19 |
24 |
27 |
31 | Select...
32 |
33 |
50 |
51 |
68 |
69 |
70 |
71 | `;
72 |
--------------------------------------------------------------------------------
/src/stories/assets/plugin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/stories/Introduction.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from "@storybook/addon-docs";
2 |
3 |
4 |
5 | ## Install
6 |
7 | Make sure you have [nodejs](https://nodejs.org/en/) 8+ and [yarn](https://yarnpkg.com/en/) or [npm](https://www.npmjs.com/) installed.
8 |
9 | #### yarn
10 |
11 | ```bash
12 | yarn add pi-ui
13 | ```
14 |
15 | #### npm
16 |
17 | ```bash
18 | npm install --save pi-ui
19 | ```
20 |
21 | ## Usage
22 |
23 | ```jsx
24 | import React, { Component } from "react";
25 |
26 | import { Button } from "pi-ui";
27 |
28 | class Example extends Component {
29 | render() {
30 | return ;
31 | }
32 | }
33 | ```
34 |
35 | ### Breakpoints
36 |
37 | Pi-ui's custom breakpoints make use of the [postcss-preset-env](https://github.com/csstools/postcss-plugins/tree/main/plugin-packs/postcss-preset-env) plugin. In order to use the breakpoints you have to add the plugin to your environment. For this, see the [postcss install page](https://github.com/csstools/postcss-plugins/blob/main/plugin-packs/postcss-preset-env/INSTALL.md#webpack).
38 |
39 | In your `postcss.config.js`, you will have to `importFrom` pi-ui's `export.css` file. If you are developing with a linked pi-ui, import from your local pi-ui. Example:
40 |
41 | ```
42 | const { resolveOwn } = require("../../utils");
43 |
44 | module.exports = {
45 | plugins: [
46 | [
47 | "postcss-preset-env",
48 | {
49 | // When developing with a linked `pi-ui`
50 | // Point the importFrom to the path of the linked package in your env
51 | importFrom: resolveOwn("../../../../pi-ui/dist/exports.css")
52 | }
53 | ]
54 | ]
55 | };
56 | ```
57 |
58 | If you are developing with a npm/yarn installed pi-ui package, import from your `node-modules`. Example:
59 |
60 | ```
61 | module.exports = {
62 | plugins: [
63 | [
64 | "postcss-preset-env",
65 | {
66 | // When developing with an installed `pi-ui`
67 | // Point the importFrom to the path of the package in your node_modules
68 | importFrom: "./node_modules/pi-ui/dist/exports.css"
69 | }
70 | ]
71 | ]
72 | };
73 | ```
74 |
--------------------------------------------------------------------------------
/src/components/Message/styles.module.css:
--------------------------------------------------------------------------------
1 | .message {
2 | padding: 20px 40px;
3 | display: flex;
4 | align-items: center;
5 | color: var(--text-color) !important;
6 | }
7 |
8 | .withTitle {
9 | align-items: flex-start;
10 | }
11 |
12 | .iconContainer {
13 | height: 100%;
14 | display: flex;
15 | align-items: center;
16 | }
17 |
18 | .withTitle .iconContainer {
19 | padding-top: 6px;
20 | }
21 |
22 | .iconForTitle {
23 | align-items: flex-start;
24 | }
25 |
26 | .content {
27 | display: flex;
28 | flex-direction: column;
29 | }
30 |
31 | .info {
32 | composes: message;
33 | background-color: var(--info-message-background);
34 | }
35 |
36 | .icon-info {
37 | margin-right: 10px;
38 | }
39 |
40 | .error {
41 | composes: message;
42 | background-color: var(--error-message-background);
43 | }
44 |
45 | .icon-error {
46 | margin-right: 10px;
47 | }
48 |
49 | .icon-error > path:first-of-type {
50 | fill: var(--error-message-color);
51 | }
52 |
53 | .icon-error > path:last-of-type {
54 | fill: var(--error-message-background);
55 | }
56 |
57 | .warning {
58 | composes: message;
59 | background-color: var(--warning-message-background);
60 | }
61 |
62 | .icon-warning {
63 | margin-right: 10px;
64 | }
65 |
66 | .icon-warning > path:first-of-type {
67 | fill: var(--warning-message-color);
68 | }
69 |
70 | .icon-warning > path:last-of-type {
71 | fill: var(--warning-message-background);
72 | }
73 |
74 | .success {
75 | composes: message;
76 | background-color: var(--success-message-background);
77 | }
78 |
79 | .blocked {
80 | composes: message;
81 | background-color: var(--blocked-message-background);
82 | }
83 |
84 | .icon-blocked {
85 | margin-right: 10px;
86 | }
87 |
88 | .icon-blocked line,
89 | .icon-blocked circle {
90 | fill: none;
91 | stroke: var(--blocked-message-color);
92 | }
93 |
94 | .icon-success {
95 | margin-right: 10px;
96 | }
97 |
98 | .icon-success > path:first-of-type,
99 | .icon-success > circle:first-of-type {
100 | fill: var(--success-message-color);
101 | }
102 |
103 | .icon-success > path:last-of-type {
104 | fill: var(--success-message-background);
105 | }
106 |
--------------------------------------------------------------------------------
/src/stories/StatusBar.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { StatusBar, Icon } from "..";
3 | import "./page.css";
4 |
5 | const StatusBarObj = {
6 | title: "Design System/Components/StatusBar",
7 | component: StatusBar,
8 | };
9 |
10 | export default StatusBarObj;
11 |
12 | const Template = ({ children, ...args }) => ;
13 |
14 | export const Basic = Template.bind({});
15 | Basic.args = {
16 | status: [
17 | {
18 | label: "Yes",
19 | amount: 300,
20 | color: "green",
21 | },
22 | {
23 | label: "No",
24 | amount: 200,
25 | color: "orange",
26 | },
27 | {
28 | label: "Maybe",
29 | amount: 500,
30 | color: "blue",
31 | },
32 | ],
33 | markerPosition: "50%",
34 | markerTooltipText: "50% Yes votes required for approval",
35 | max: 5000,
36 | };
37 |
38 | export const NoMarker = Template.bind({});
39 | NoMarker.args = {
40 | status: [
41 | {
42 | label: "Yes",
43 | amount: 300,
44 | color: "green",
45 | },
46 | {
47 | label: "No",
48 | amount: 200,
49 | color: "orange",
50 | },
51 | {
52 | label: "Maybe",
53 | amount: 500,
54 | color: "blue",
55 | },
56 | ],
57 | showMarker: false,
58 | max: 5000,
59 | };
60 |
61 | export const Custom = Template.bind({});
62 | Custom.args = {
63 | status: [
64 | {
65 | label: "Local",
66 | amount: 9.45931345,
67 | color: "green",
68 | renderAmountComponent: 9.45931345 DCR,
69 | },
70 | {
71 | label: "Remote",
72 | amount: 1.45931345,
73 | color: "orange",
74 | renderAmountComponent: 1.45931345 DCR,
75 | },
76 | ],
77 | markerPosition: "50%",
78 | max: 10.9431937482,
79 | layout: "balance",
80 | showPercent: false,
81 | renderStatusInfoComponent: ,
82 | renderMarkerComponent: (
83 |
94 |
95 |
96 | ),
97 | };
98 |
--------------------------------------------------------------------------------
/src/components/Dropdown/styles.module.css:
--------------------------------------------------------------------------------
1 | .dropdownWrapper {
2 | position: relative;
3 | align-self: flex-start;
4 | display: inline-flex;
5 | }
6 |
7 | .headerWrapper {
8 | display: flex;
9 | align-items: center;
10 | }
11 |
12 | .headerWrapper:hover {
13 | cursor: pointer;
14 | }
15 |
16 | .dropdownHeader {
17 | display: flex;
18 | margin-right: 0.8rem;
19 | color: var(--color-primary-dark);
20 | }
21 |
22 | .arrowAnchor {
23 | position: relative;
24 | cursor: pointer;
25 | margin-left: 1.25rem;
26 | }
27 |
28 | .arrowAnchor:after {
29 | content: "";
30 | width: 0;
31 | height: 0;
32 | border: 7px solid transparent;
33 | border-color: var(--dropdown-arrow-color) transparent transparent transparent;
34 | position: absolute;
35 | top: -4px;
36 | right: 2px;
37 | }
38 |
39 | .arrowAnchor:hover:after {
40 | border-color: var(--color-primary) transparent transparent transparent;
41 | }
42 |
43 | .arrowAnchor.open:after {
44 | top: -11px;
45 | border-color: transparent transparent var(--color-primary) transparent;
46 | }
47 |
48 | .headerWrapper:hover .arrowAnchor:not(.open):after {
49 | border-color: var(--color-primary) transparent transparent transparent;
50 | }
51 |
52 | .dropdownList {
53 | z-index: 100;
54 | position: absolute;
55 | background-color: var(--card-background);
56 | display: inline-flex;
57 | left: 50%;
58 | transform: translateX(-50%);
59 | flex-direction: column;
60 | justify-content: space-around;
61 | white-space: pre;
62 | margin-top: 3.5rem;
63 | padding: 0 0 2rem 0 !important;
64 | -webkit-box-shadow: 0 1rem 2rem 0 rgba(0, 0, 0, 0.16);
65 | -moz-box-shadow: 0 1rem 2rem 0 rgba(0, 0, 0, 0.16);
66 | box-shadow: 0 1rem 2rem 0 rgba(0, 0, 0, 0.16);
67 | }
68 |
69 | .dropdownItem {
70 | list-style-type: none;
71 | display: flex;
72 | overflow: auto;
73 | text-align: left;
74 | justify-content: flex-end;
75 | font-size: var(--font-size-normal);
76 | color: var(--tab-text-color);
77 | padding: 1.25rem;
78 | user-select: none;
79 | width: 100%;
80 | }
81 |
82 | .dropdownItem:not(:first-child) {
83 | margin-top: 1.2rem;
84 | }
85 |
86 | .dropdownItem * {
87 | user-select: none;
88 | }
89 |
90 | .dropdownItem:hover,
91 | .dropdownItem:hover > * {
92 | cursor: pointer;
93 | color: var(--dropdown-item-hover-color) !important;
94 | }
95 |
--------------------------------------------------------------------------------
/src/theme/ThemeProvider.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | useState,
3 | useLayoutEffect,
4 | useMemo,
5 | createContext,
6 | useContext,
7 | } from "react";
8 | import PropTypes from "prop-types";
9 | import defaultLightTheme from "./lightTheme";
10 | import { DEFAULT_LIGHT_THEME_NAME } from "./constants";
11 |
12 | const ThemeContext = createContext({
13 | theme: defaultLightTheme,
14 | themeName: DEFAULT_LIGHT_THEME_NAME,
15 | setThemeName: () => {},
16 | });
17 |
18 | export const useTheme = () => useContext(ThemeContext) || {};
19 |
20 | export const ThemeProvider = ({
21 | themes,
22 | defaultThemeName,
23 | fonts,
24 | children,
25 | }) => {
26 | const [themeName, setThemeName] = useState(defaultThemeName);
27 | const theme = useMemo(() => themes[themeName], [themes, themeName]);
28 | useLayoutEffect(() => {
29 | applyTheme(theme);
30 | if (fonts) {
31 | applyFontAsset(fonts);
32 | }
33 | }, [theme, fonts]);
34 |
35 | return (
36 |
42 | {children}
43 |
44 | );
45 | };
46 |
47 | ThemeProvider.propTypes = {
48 | themes: PropTypes.object.isRequired,
49 | defaultThemeName: (props, propName) => {
50 | const givenThemes = props.themes;
51 | if (!Object.keys(givenThemes).includes(props[propName])) {
52 | return new Error(`${propName} must match one of the given themes`);
53 | }
54 | },
55 | fonts: PropTypes.array,
56 | children: PropTypes.node,
57 | };
58 |
59 | const applyFontAsset = (fonts) => {
60 | const newStyle = document.createElement("style");
61 | fonts.forEach((fontFace) => {
62 | let fontFaceStr = "";
63 | for (const [key, value] of Object.entries(fontFace)) {
64 | fontFaceStr = `${fontFaceStr}
65 | ${key}: ${value};
66 | `;
67 | }
68 | const fontFaceNode = document.createTextNode(`
69 | @font-face {
70 | ${fontFaceStr}
71 | }
72 | `);
73 | newStyle.appendChild(fontFaceNode);
74 | });
75 | newStyle.type = "text/css";
76 | document.head.appendChild(newStyle);
77 | };
78 |
79 | function applyTheme(theme) {
80 | Object.keys(theme).forEach((key) => {
81 | document.documentElement.style.setProperty(`--${key}`, theme[key]);
82 | });
83 | }
84 |
--------------------------------------------------------------------------------
/src/components/Tabs/tests/tabs.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { create } from "react-test-renderer";
3 | import {
4 | defaultLightTheme,
5 | ThemeProvider,
6 | DEFAULT_LIGHT_THEME_NAME,
7 | } from "../../../theme";
8 | import { render, fireEvent, waitFor } from "@testing-library/react";
9 | import Tabs from "../Tabs";
10 | import Tab from "../Tab";
11 |
12 | describe("Tabs Component", () => {
13 | test("Matches the snapshot", () => {
14 | const tabs = create(
15 |
18 |
19 |
20 | test1
21 |
22 |
23 | test2
24 |
25 |
26 |
27 | );
28 | expect(tabs.toJSON()).toMatchSnapshot();
29 | });
30 |
31 | test("Changing tabs works properly", async () => {
32 | // create TabsContainer helper component to be able
33 | // to serve activeTabIndex state parameter to Tabs
34 | const TabsContainer = () => {
35 | const [activeTabIndex, setActiveTabIndex] = React.useState(0);
36 |
37 | return (
38 |
41 |
42 |
43 | test1
44 |
45 |
46 | test2
47 |
48 |
49 |
50 | );
51 | };
52 | const { getByTestId, queryByText, getByText, queryAllByText } = render(
53 |
54 | );
55 | expect(queryByText(/test1/i)).toBeTruthy();
56 | expect(queryByText(/test2/i)).toBeFalsy();
57 | fireEvent.click(getByTestId("tab-1"));
58 |
59 | // wait until `test2` content appears entirely
60 | await waitFor(() =>
61 | expect(getByText("test2").parentNode.style.opacity).toBe("1")
62 | );
63 | expect(queryByText(/test1/i)).toBeFalsy();
64 | expect(queryAllByText(/test2/i)).toBeTruthy();
65 | });
66 | });
67 |
--------------------------------------------------------------------------------
/src/components/Table/tests/table.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { create } from "react-test-renderer";
3 | import { render, fireEvent } from "@testing-library/react";
4 | import Table from "../Table";
5 | import {
6 | defaultLightTheme,
7 | ThemeProvider,
8 | DEFAULT_LIGHT_THEME_NAME,
9 | } from "../../../theme";
10 |
11 | const mockData = [
12 | {
13 | Test1: "testl1c1",
14 | Test2: "testl1c2",
15 | },
16 | {
17 | Test1: "testl2c1",
18 | Test2: "testl2c2",
19 | },
20 | ];
21 |
22 | const mockHeaders = Object.keys(mockData[0]);
23 |
24 | describe("Table Component", () => {
25 | test("Matches the snapshot", () => {
26 | const table = create(
27 |
30 |
31 |
32 | );
33 | expect(table.toJSON()).toMatchSnapshot();
34 | });
35 |
36 | test("Table pagination", () => {
37 | const { getByTestId, queryByText, queryByTestId } = render(
38 |
41 |
42 |
43 | );
44 |
45 | let line1column1 = queryByText("testl1c1");
46 | let line2column1 = queryByText("testl2c1");
47 | const back = queryByTestId("back");
48 | const next = queryByTestId("next");
49 |
50 | expect(line1column1).toBeTruthy();
51 | expect(line2column1).toBeFalsy();
52 | expect(back).toBeTruthy();
53 | expect(next).toBeTruthy();
54 |
55 | // nothing happens
56 | fireEvent.click(getByTestId("back"));
57 | line2column1 = queryByText("testl2c1");
58 | expect(line2column1).toBeFalsy();
59 |
60 | // next
61 | fireEvent.click(getByTestId("next"));
62 | line1column1 = queryByText("testl1c1");
63 | line2column1 = queryByText("testl2c1");
64 | expect(line2column1).toBeTruthy();
65 | expect(line1column1).toBeFalsy();
66 |
67 | // back
68 | fireEvent.click(getByTestId("back"));
69 | line1column1 = queryByText("testl1c1");
70 | line2column1 = queryByText("testl2c1");
71 | expect(line2column1).toBeFalsy();
72 | expect(line1column1).toBeTruthy();
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/src/components/CopyableText/CopyableText.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 | import React, { useState } from "react";
3 | import { copyToClipboard as copy } from "./helpers";
4 | import { classNames, idPropTypeCheckForTruncatedComponents } from "../../utils";
5 | import styles from "./styles.module.css";
6 | import Tooltip from "../Tooltip/Tooltip.jsx";
7 | import Icon from "../Icon/Icon.jsx";
8 | import TextHighlighted from "../TextHighlighted/TextHighlighted.jsx";
9 |
10 | const CopyableText = ({
11 | id,
12 | truncate,
13 | children,
14 | className,
15 | hoverText,
16 | textClassName,
17 | textStyle,
18 | buttonClassName,
19 | buttonStyle,
20 | tooltipPlacement,
21 | ...props
22 | }) => {
23 | const [feedbackActive, setFeedbackActive] = useState(false);
24 | const onCopyToClipboard = (value) => {
25 | copy(value);
26 | setFeedbackActive(true);
27 | setTimeout(() => {
28 | setFeedbackActive(false);
29 | }, 1000);
30 | };
31 | return (
32 |
33 |
38 | {children}
39 |
40 |
43 | onCopyToClipboard(children)}
48 | className={classNames(styles.copyToClipboard, buttonClassName)}
49 | style={buttonStyle}
50 | />
51 |
52 |
53 | );
54 | };
55 |
56 | CopyableText.propTypes = {
57 | truncate: PropTypes.bool,
58 | children: PropTypes.node,
59 | hoverText: PropTypes.node,
60 | className: PropTypes.string,
61 | textClassName: PropTypes.string,
62 | style: PropTypes.object,
63 | textStyle: PropTypes.object,
64 | buttonClassName: PropTypes.string,
65 | buttonStyle: PropTypes.object,
66 | tooltipPlacement: PropTypes.string,
67 | id: idPropTypeCheckForTruncatedComponents,
68 | };
69 |
70 | CopyableText.defaultProps = {
71 | truncate: true,
72 | hoverText: "Copy to clipboard",
73 | tooltipPlacement: "right",
74 | };
75 |
76 | export default CopyableText;
77 |
--------------------------------------------------------------------------------
/src/stories/Typography.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { H1, H2, H3, H4, H5, H6, P, Text, TextHighlighted } from "..";
3 | import "./page.css";
4 |
5 | const TypographyObj = {
6 | title: "Design System/Components/Typography",
7 | };
8 |
9 | export default TypographyObj;
10 |
11 | const H1Template = ({ children, ...args }) => {children}
;
12 | const H2Template = ({ children, ...args }) => {children}
;
13 | const H3Template = ({ children, ...args }) => {children}
;
14 | const H4Template = ({ children, ...args }) => {children}
;
15 | const H5Template = ({ children, ...args }) => {children}
;
16 | const H6Template = ({ children, ...args }) => {children}
;
17 | const PTemplate = ({ children, ...args }) => {children}
;
18 | const TextTemplate = ({ children, ...args }) => (
19 | {children}
20 | );
21 | const TextHighlightedTemplate = ({ children, ...args }) => (
22 | {children}
23 | );
24 |
25 | export const Header1 = H1Template.bind({});
26 | Header1.args = {
27 | children: "Header1 - 28px - Regular",
28 | };
29 | export const Header2 = H2Template.bind({});
30 | Header2.args = {
31 | children: "Header2 - 24px - Semibold",
32 | };
33 | export const Header3 = H3Template.bind({});
34 | Header3.args = {
35 | children: "Header3 - 20px - Semibold",
36 | };
37 | export const Header4 = H4Template.bind({});
38 | Header4.args = {
39 | children: "Header4 - 18px - Semibold",
40 | };
41 | export const Header5 = H5Template.bind({});
42 | Header5.args = {
43 | children: "Header5 - 16px - Semibold",
44 | };
45 | export const Header6 = H6Template.bind({});
46 | Header6.args = {
47 | children: "Header6 - 13px - Semibold",
48 | };
49 | export const Paragraph = PTemplate.bind({});
50 | Paragraph.args = {
51 | children: `Lorem ipsum dolor sit, amet consectetur adipisicing elit. Alias
52 | voluptate delectus eius nesciunt porro! Eum expedita voluptate dolor
53 | dignissimos autem recusandae quia, quasi, beatae asperiores, aliquam
54 | maxime reiciendis earum voluptatum`,
55 | };
56 | export const RegularText = TextTemplate.bind({});
57 | RegularText.args = {
58 | children: "Regular text",
59 | };
60 | export const RegularTextHighlighted = TextHighlightedTemplate.bind({});
61 | RegularTextHighlighted.args = {
62 | children: "Regular highlighted text",
63 | };
64 |
--------------------------------------------------------------------------------
/src/components/Button/styles.module.css:
--------------------------------------------------------------------------------
1 | .btn,
2 | .btn:link,
3 | .btn:visited {
4 | font-size: var(--font-size-normal);
5 | font-weight: var(--font-weight-semi-bold);
6 | line-height: var(--spacing-2);
7 | cursor: pointer;
8 | text-decoration: none;
9 | transition: all 0.2s;
10 | backface-visibility: hidden;
11 | }
12 |
13 | .sm {
14 | padding: 0.6rem 1rem;
15 | border-radius: 0.5rem;
16 | font-size: var(--font-size-small);
17 | }
18 |
19 | .md {
20 | padding: 1rem 3rem;
21 | border-radius: 0.5rem;
22 | }
23 |
24 | .lg {
25 | padding: 1.5rem 3.6rem;
26 | border-radius: 0.5rem;
27 | }
28 |
29 | .icon {
30 | border-radius: 1rem;
31 | }
32 |
33 | .btn:not(:last-child):not(:only-child) {
34 | margin-right: var(--spacing-1);
35 | }
36 |
37 | .primary {
38 | composes: btn;
39 | color: var(--color-white);
40 | background-color: var(--btn-background-color);
41 | border: 1px solid var(--btn-background-color);
42 | }
43 |
44 | .secondary {
45 | composes: btn;
46 | color: var(--btn-background-color);
47 | background-color: transparent;
48 | border: 1px solid var(--btn-background-color);
49 | }
50 |
51 | .noBorder {
52 | border: 1px solid transparent !important;
53 | }
54 |
55 | .disabled {
56 | composes: btn;
57 | cursor: not-allowed;
58 | color: var(--btn-disabled-text-color);
59 | background-color: var(--btn-disabled-background-color);
60 | border: 1px solid var(--btn-disabled-background-color);
61 | }
62 |
63 | .primary:focus,
64 | .primary:hover {
65 | background-color: var(--color-blue-alt);
66 | border: 1px solid var(--color-blue-alt);
67 | }
68 |
69 | .secondary:focus,
70 | .secondary:hover {
71 | color: var(--color-blue-alt);
72 | border: 1px solid var(--color-blue-alt);
73 | }
74 |
75 | .primary:focus,
76 | .primary:hover,
77 | .secondary:focus,
78 | .secondary:hover {
79 | outline: 0;
80 | }
81 |
82 | .primary:active,
83 | .secondary:active {
84 | transform: scale(1);
85 | box-shadow: 0 0 0;
86 | }
87 |
88 | .fullWidth {
89 | width: 100%;
90 | }
91 |
92 | /* EXTRA SMALL ADJUSTMENTS
93 | This breakpoint is used only here
94 | Thats why we're not using a custom media export */
95 | @media screen and (max-width: 380px) {
96 | .sm {
97 | padding: 0.45rem 0.75rem;
98 | }
99 |
100 | .md {
101 | padding: 0.75rem 2.25rem;
102 | }
103 |
104 | .lg {
105 | padding: 1.2rem 2.7rem;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/stories/assets/stackalt.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Table/Table.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import PropTypes from "prop-types";
3 | import TableHeader from "./TableHeader.jsx";
4 | import TableBody from "./TableBody.jsx";
5 | import Paginator from "../Paginator/Paginator.jsx";
6 | import styles from "./styles.module.css";
7 | import { classNames } from "../../utils";
8 |
9 | const Table = ({
10 | data,
11 | headers,
12 | linesPerPage,
13 | disablePagination,
14 | className,
15 | wrapperClassName,
16 | headClassName,
17 | headerCellClassName,
18 | rowClassName,
19 | bodyClassName,
20 | bodyCellClassName,
21 | paginationGap,
22 | }) => {
23 | if (linesPerPage < 1) {
24 | throw new Error("Invalid prop. linesPerPage should be bigger than 1");
25 | }
26 | const totalPages = Math.ceil(data.length / linesPerPage);
27 | const pagesArr = [...new Array(totalPages)].map((_, i) => i + 1);
28 | const [page, setPage] = useState(0);
29 | const onPageChange = ({ selected }) => setPage(selected);
30 | const startIndex = page * linesPerPage;
31 | return (
32 |
33 |
47 | {!disablePagination && pagesArr.length > 1 && (
48 |
53 | )}
54 |
55 | );
56 | };
57 |
58 | Table.propTypes = {
59 | data: PropTypes.array.isRequired,
60 | headers: PropTypes.arrayOf(PropTypes.node).isRequired,
61 | linesPerPage: PropTypes.number,
62 | disablePagination: PropTypes.bool,
63 | className: PropTypes.string,
64 | wrapperClassName: PropTypes.string,
65 | headClassName: PropTypes.string,
66 | headerCellClassName: PropTypes.string,
67 | rowClassName: PropTypes.string,
68 | bodyClassName: PropTypes.string,
69 | bodyCellClassName: PropTypes.string,
70 | paginationGap: PropTypes.number,
71 | };
72 |
73 | Table.defaultProps = {
74 | linesPerPage: 10,
75 | disablePagination: false,
76 | paginationGap: 2,
77 | };
78 |
79 | export default Table;
80 |
--------------------------------------------------------------------------------