))}
34 | );
35 |
36 | expect(wrapper.hasClass(fakeClass)).toBe(true);
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/src/stories/step-progress-bar.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/react";
2 | import React from "react";
3 | import { Box } from "../components/box";
4 | import Step from "../components/step-progress-bar/step";
5 | import StepProgressBar from "../components/step-progress-bar/step-progress-bar";
6 |
7 | const stories = storiesOf("Step Progress Bar", module);
8 |
9 | stories.add("Default", () => (
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | ));
19 |
20 | stories.add("With other components", () => (
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | ));
30 |
31 | stories.add("Colors", () => (
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | ));
47 |
--------------------------------------------------------------------------------
/src/stories/switch.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/react";
2 | import React from "react";
3 | import Switch from "../components/switch";
4 |
5 | const stories = storiesOf("Switch", module);
6 |
7 | stories.add("Default", () => (
8 |
9 |
24 |
25 | ));
26 |
27 | stories.add("As radio", () => (
28 |
29 |
44 |
45 | ));
46 |
--------------------------------------------------------------------------------
/documents/components/radio.md:
--------------------------------------------------------------------------------
1 | # Radio
2 |
3 | ## Label
4 | It is also possible to add label next to the radio by giving label property
5 |
6 | ```
7 |
8 |
9 |
10 |
11 | ```
12 |
13 |
14 |
15 | ## Error
16 | Adding error prop to radio element changes the component's style to indicate an error has happened.
17 |
18 | ```
19 |
20 | ```
21 |
22 |
23 |
24 | #### Additional content
25 | You can use any other HTML radio properties:
26 | ```
27 |
28 | ```
29 |
--------------------------------------------------------------------------------
/src/styles/components/_accordion.scss:
--------------------------------------------------------------------------------
1 | @import "../_variables";
2 | @import "../animations/_accordion.scss";
3 |
4 | .q-accordion {
5 | background-color: $white;
6 | }
7 |
8 | .q-accordion-header {
9 | padding: $padding-x 15px;
10 | border-bottom: 1px solid $very-light-pink;
11 | border-top: 1px solid $very-light-pink;
12 | width: 100%;
13 | height: $modal-header-height;
14 | display: flex;
15 | background-color: $white;
16 | justify-content: space-between;
17 |
18 | .q-accordion-title {
19 | font-size: $font-size-base;
20 | font-weight: $bold;
21 | text-overflow: ellipsis;
22 | white-space: nowrap;
23 | overflow: hidden;
24 | margin-bottom: 0;
25 | height: 100%;
26 | display: flex;
27 | align-items: center;
28 | }
29 |
30 | .q-icon {
31 | line-height: 1rem;
32 | transition: transform 300ms;
33 | display: flex;
34 | align-items: center;
35 |
36 | &.expanded {
37 | transform: rotate(180deg);
38 | }
39 | }
40 | }
41 |
42 | .q-accordion-content {
43 | background-color: $white;
44 | border-bottom: 1px solid $very-light-pink;
45 | overflow: hidden;
46 | will-change: max-height;
47 | padding: 0 1rem;
48 | }
49 |
50 | .q-accordion-group{
51 | border: 1px solid $border-gray;
52 |
53 | .q-accordion, .q-accordion-header{
54 | border:0;
55 | border-radius: 0;
56 | }
57 |
58 | .q-accordion:not(:last-child){
59 | border-bottom: 1px solid $border-gray;
60 | }
61 | }
62 |
63 | //Animations
64 | @include accordion-animation;
65 |
--------------------------------------------------------------------------------
/src/styles/components/_input.scss:
--------------------------------------------------------------------------------
1 | @import '../mixins/_color-scheme';
2 | @import '../mixins/_fluid.scss';
3 |
4 | .q-input-wrapper {
5 | display: block;
6 | text-align: left;
7 |
8 | .q-label {
9 | display: block;
10 | font-weight: $bold;
11 | font-size: $font-size-paragraph;
12 | line-height: $line-height-paragraph;
13 | padding: 0;
14 | color: $black;
15 | margin-bottom: 10px;
16 | }
17 |
18 | .q-span {
19 | display: inherit;
20 | color: #ababab;
21 | margin: -5px 0 5px 0;
22 | font-size: $font-size-small-paragraph;
23 | }
24 |
25 | .q-input-error {
26 | color: $red;
27 | font-weight: $bold;
28 | margin-top: 5px;
29 | }
30 |
31 | &.q-fluid {
32 | @include fluid;
33 |
34 | .q-input {
35 | width: 100%;
36 | }
37 | }
38 |
39 | .q-input {
40 | background-color: $border-gray-two;
41 | font-size: $input-font-size;
42 | border: 1px solid $border-gray;
43 | width: inherit;
44 | border-radius: $border-radius;
45 | height: $input_height;
46 | padding: 10px;
47 | color: $dark-gray;
48 | -webkit-appearance: none;
49 | }
50 |
51 | .q-input:focus {
52 | outline: none !important;
53 | background-color: $background-gray-two;
54 | }
55 |
56 | &.q-disabled {
57 | .q-input {
58 | @include disabled;
59 | }
60 |
61 | .q-label {
62 | color: $light-gray;
63 | }
64 | }
65 |
66 | &.q-error {
67 | .q-input {
68 | border-color: $red;
69 | background-color: $border-error;
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/input/input.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import IInput from "../../interfaces/input";
4 | import classNamesDefault from "../../utils/class-names-default";
5 |
6 | export default class Input extends PureComponent
{
7 | public render() {
8 | const {
9 | error,
10 | errorMessage,
11 | fluid,
12 | subtext,
13 | type = "text",
14 | label,
15 | disabled,
16 | className,
17 | ...props
18 | } = this.props;
19 | const newType = type === "checkbox" || type === "radio" ? "text" : type;
20 | const inputClasses = classNames(
21 | "q-input-wrapper",
22 | classNamesDefault({ error, disabled, fluid }),
23 | className);
24 | return (
25 |
26 | {label && (
27 |
28 | )}
29 | {subtext && {subtext}}
30 |
36 | {error && errorMessage && (
37 | {errorMessage}
38 | )}
39 |
40 | );
41 | }
42 | }
43 |
44 | interface IProps extends IInput {
45 | disabled?: boolean;
46 | error?: boolean;
47 | errorMessage?: string;
48 | label?: string;
49 | subtext?: string;
50 | fluid?: boolean;
51 | className?: string;
52 | }
53 |
--------------------------------------------------------------------------------
/src/styles/components/_subheader.scss:
--------------------------------------------------------------------------------
1 | @import "../animations/_modal.scss";
2 | @import "../mixins/text-truncate";
3 | @import '../_colors';
4 |
5 |
6 | .q-subheader {
7 | background-color: $white;
8 | display: flex;
9 | height: 51px;
10 | border: 0;
11 | border-bottom: 1px solid $border-gray;
12 | align-content: center;
13 | position: relative;
14 |
15 | .q-icon {
16 | display: flex;
17 | font-size: 20px;
18 | align-items: center;
19 | }
20 |
21 | .left-icon {
22 | @extend .q-icon;
23 | height: 100%;
24 | display: flex;
25 | top: 0;
26 | align-items: center;
27 | position: absolute;
28 | left: 20px;
29 | justify-content: space-between;
30 | }
31 |
32 | .title {
33 | margin-left: 5px;
34 | margin-right: 5px;
35 |
36 | .q-typography{
37 | line-height: 51px;
38 | }
39 | }
40 |
41 | .title-group{
42 | align-items: center;
43 | flex-direction: column;
44 | .q-subtitle {
45 | margin-top: 3px;
46 | }
47 | }
48 |
49 | .title, .title-group{
50 | flex: 12;
51 | text-align: center;
52 | display: flex;
53 | justify-content: center;
54 | .q-typography{
55 | width: 75vw;
56 | text-align: center;
57 | @include text-truncate;
58 | }
59 | }
60 |
61 | .right-icon {
62 | @extend .q-icon;
63 | height: 100%;
64 | padding: 10px;
65 | display: flex;
66 | top: 0;
67 | align-items: center;
68 | position: absolute;
69 |
70 | right: 10px;
71 | justify-content: space-between;
72 | }
73 |
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/documents/components/accordion.md:
--------------------------------------------------------------------------------
1 | # Accordion
2 |
3 | Accordion component hides and reveals its contents depending on user
4 | action. By default accordion holds an expanded state set to false.
5 | But you can implement your own state to control accordion component.
6 |
7 | ## Default
8 |
9 | ```html
10 |
11 |
12 | Accordion 1
13 |
14 |
15 | Some content
16 | Some content
17 | Some content
18 | Some content
19 | Some content
20 |
21 |
22 |
23 | ```
24 | ## Controlled
25 |
26 | ```js
27 | const [expanded, setExpanded] = useState(true);
28 |
29 | const handleChange = () => {
30 | setExpanded(!expanded);
31 | };
32 |
33 | const handleClick = () => {
34 | setExpanded(!expanded);
35 | };
36 |
37 | return (
38 | <>
39 |
40 |
41 | Accordion 1
42 |
43 |
44 | Some content
45 | Some content
46 | Some content
47 | Some content
48 | Some content
49 |
50 |
51 |
52 |
53 | >
54 | );
55 | ```
56 |
--------------------------------------------------------------------------------
/src/components/radio/radio.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import IInput from "../../interfaces/input";
4 | import { colorTypes } from "../../types/color";
5 | import { variantTypes } from "../../types/typography";
6 | import classNamesDefault from "../../utils/class-names-default";
7 | import Typography from "../typography";
8 |
9 | export default class Radio extends PureComponent {
10 | public render() {
11 | const {
12 | error,
13 | label,
14 | labelVariant = "body",
15 | labelColor = "black",
16 | type,
17 | value,
18 | className,
19 | ...props
20 | } = this.props;
21 | const radioClasses = classNames(
22 | "q-radio-wrapper",
23 | classNamesDefault({ error }),
24 | className,
25 | );
26 | return (
27 |
28 |
36 |
42 |
43 | );
44 | }
45 | }
46 |
47 | interface IProps extends IInput {
48 | error?: boolean;
49 | label?: string;
50 | labelVariant?: variantTypes;
51 | labelColor?: colorTypes;
52 | value: any;
53 | className?: string;
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/icon/icon.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import IIcon from "../../interfaces/icon";
4 | import { colorTypes } from "../../types/color";
5 | import { sizeTypes } from "../../types/icon";
6 | import classNamesDefault from "../../utils/class-names-default";
7 |
8 | export default class Icon extends PureComponent {
9 |
10 | public render() {
11 | const { name, size, sizePixel, color, circular, disabled, children, stroke, className, ...props } = this.props;
12 | const iconClass = classNames(
13 | classNamesDefault({ name, disabled }),
14 | "q-icon",
15 | size && `q-icon-${size}`,
16 | `icon-${name}`,
17 | circular && "q-circular",
18 | color && `${color}`,
19 | className,
20 | );
21 | const sizePixelWrapper = sizePixel ? {
22 | fontSize: `${sizePixel}px`,
23 | } : {};
24 |
25 | const renderSpanElements = (): JSX.Element[] =>
26 | new Array(stroke)
27 | .fill(undefined)
28 | .map((_, i) => );
29 |
30 | return (
31 |
32 | {!!stroke && renderSpanElements()}
33 | {children}
34 |
35 | );
36 | }
37 | }
38 |
39 | export interface IProps extends IIcon {
40 | name: string;
41 | size?: sizeTypes;
42 | sizePixel?: number;
43 | disabled?: boolean;
44 | circular?: boolean;
45 | color?: colorTypes;
46 | onClick?: (event: any) => any;
47 | stroke?: number;
48 | className?: string;
49 | children?: React.ReactNode;
50 | }
51 |
--------------------------------------------------------------------------------
/src/styles/components/_step-progress-bar.scss:
--------------------------------------------------------------------------------
1 | @import "../_variables";
2 |
3 | .q-spb {
4 | width: 100%;
5 | padding-inline-start: 0;
6 | display: flex;
7 | justify-content: space-between;
8 |
9 | li {
10 | width: 100%;
11 | color: $border-gray;
12 | font-size: $font-size-small-paragraph;
13 | list-style-type: none;
14 | text-align: center;
15 | position: relative;
16 | }
17 |
18 | li:before {
19 | content: '';
20 | width: $space-large;
21 | height: $space-large;
22 | line-height: $line-height-header-2;
23 | border: 2px solid $border-gray;
24 | background-color: $border-gray;
25 | display: block;
26 | text-align: center;
27 | margin: 0 auto $space-medium auto;
28 | border-radius: 50%;
29 | border: 2px solid $white;
30 | transition: all .7s;
31 | position: relative;
32 | z-index: 1;
33 | }
34 |
35 | li:after {
36 | content: '';
37 | width: 100%;
38 | height: 2px;
39 | position: absolute;
40 | background-color: $border-gray;
41 | top: 6.5px;
42 | left: -50%;
43 | transition: all .7s;
44 | }
45 |
46 | li:first-child:after {
47 | content: none;
48 | }
49 |
50 | @each $color,
51 | $value in $colors {
52 | &.#{$color} {
53 |
54 | li.active {
55 | color: $value;
56 | }
57 |
58 | li.active:before {
59 | border-color: $value;
60 | background-color: $value;
61 | transition: all .7s;
62 | border: 2px solid $white;
63 | }
64 |
65 | li.active:after {
66 | background-color: $value;
67 | transition: all .7s;
68 | }
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/src/components/checkbox/checkbox.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import IInput from "../../interfaces/input";
4 | import { colorTypes } from "../../types/color";
5 | import { variantTypes } from "../../types/typography";
6 | import classNamesDefault from "../../utils/class-names-default";
7 | import Typography from "../typography";
8 |
9 | export default class CheckBox extends PureComponent {
10 | public render() {
11 | const {
12 | error,
13 | label,
14 | labelVariant = "body",
15 | labelColor = "black",
16 | type,
17 | value,
18 | className,
19 | ...props
20 | } = this.props;
21 |
22 | const checkBoxClasses = classNames(
23 | "q-checkbox-wrapper",
24 | classNamesDefault({ error }),
25 | className,
26 | );
27 |
28 | return (
29 |
30 |
38 |
39 |
44 |
45 | );
46 | }
47 | }
48 |
49 | interface IProps extends IInput {
50 | error?: boolean;
51 | label?: string;
52 | labelVariant?: variantTypes;
53 | labelColor?: colorTypes;
54 | value: any;
55 | className?: string;
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/button/button.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import IButton from "../../interfaces/button";
4 | import { buttonSize, variantTypes } from "../../types/button";
5 | import classNamesDefault from "../../utils/class-names-default";
6 | import Icon from "../icon";
7 | import Loader from "../loader";
8 |
9 | export default class Button extends PureComponent {
10 |
11 | public render() {
12 | const {
13 | variant = "primary",
14 | fluid,
15 | disabled,
16 | round,
17 | size = "medium",
18 | circular,
19 | loading = false,
20 | ripple,
21 | icon,
22 | children,
23 | className,
24 | ...props
25 | } = this.props;
26 | const buttonClasses = classNames(
27 | classNamesDefault({ variant, fluid, disabled, round }),
28 | size && `q-button-${size}`,
29 | circular && "q-circular",
30 | loading && "loading", "q-button",
31 | className,
32 | );
33 |
34 | return (
35 |
44 | );
45 | }
46 | }
47 |
48 | interface IButtonProps extends IButton {
49 | variant?: variantTypes;
50 | size?: buttonSize;
51 | fluid?: boolean;
52 | disabled?: boolean;
53 | icon?: string;
54 | round?: boolean;
55 | circular?: boolean;
56 | className?: string;
57 | loading?: boolean;
58 | ripple?: boolean;
59 | }
60 |
--------------------------------------------------------------------------------
/src/stories/subheader.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/react";
2 | import { faker } from '@faker-js/faker';
3 | import React from "react";
4 | import SubHeader from "../components/subheader";
5 |
6 | const stories = storiesOf("SubHeader", module);
7 |
8 | const action = () => alert("Clicked");
9 |
10 | stories.add("Default", () => (
11 |
12 |
13 |
14 | ));
15 |
16 | stories.add("Subtitle", () => (
17 |
18 |
19 |
20 |
21 |
22 |
23 | ));
24 |
25 | stories.add("Subtitle with default/overriden title tag", () => (
26 |
27 |
28 |
29 |
30 |
31 |
32 | ));
33 |
34 | stories.add("Icons", () => (
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | ));
51 |
52 | stories.add("Icon with action", () => (
53 |
54 |
55 |
56 | ));
57 |
--------------------------------------------------------------------------------
/documents/components/list.md:
--------------------------------------------------------------------------------
1 | # List
2 | Lists can be defined as continuous, vertical indexes of text or images.
3 |
4 | Lists are a continuous group of text or images. They are composed of items containing primary and supplemental actions, which are represented by icons and text.
5 |
6 | ### Simple List
7 |
8 | ```js
9 |
10 | 15 gün içerisinde ücretsiz iade. Detaylı bilgi için.Tıklayın.
11 | 24 saatte kargoda fırsatı iş günlerinde geçerlidir.
12 | Modelin Ölçüleri: Boy: 1.75, Göğüs: 80, Bel: 60, Kalça: 88
13 | Mankenin üzerindeki ürün S/36 bedendir.
14 |
15 | ```
16 |
17 | ### Dotted
18 | Setting dotted prop to false will remove predefined dot from the items. This option is set to true by default.
19 |
20 | ```js
21 |
22 | 15 gün içerisinde ücretsiz iade. Detaylı bilgi için.Tıklayın.
23 | 24 saatte kargoda fırsatı iş günlerinde geçerlidir.
24 | Modelin Ölçüleri: Boy: 1.75, Göğüs: 80, Bel: 60, Kalça: 88
25 | Mankenin üzerindeki ürün S/36 bedendir.
26 |
27 | ```
28 |
29 | ### Item
30 | To customize more, it is possible to give an icon name
31 |
32 | ```js
33 |
34 | 15 gün içerisinde ücretsiz iade. Detaylı bilgi için
35 | 24 saatte kargoda fırsatı iş günlerinde geçerlidir.
36 | Modelin Ölçüleri: Boy: 1.75, Göğüs: 80, Bel: 60, Kalça: 88
37 | Mankenin üzerindeki ürün S/36 bedendir.
38 |
39 | ```
40 |
--------------------------------------------------------------------------------
/documents/components/typography.md:
--------------------------------------------------------------------------------
1 | # Typography
2 |
3 | Typography is a wrapper component used to control the styles and
4 | general structure of headers, paragraphs and texts.
5 |
6 | ## Variant
7 |
8 | ```html
9 |
10 |
11 | h1 Hello there
12 |
13 |
14 | ```
15 | Variant prop decides the component style to be rendered. If there is
16 | no component prop then variant prop also decided the component type.
17 |
18 | Some variant types are:
19 |
20 | ```html
21 | * "h1"
22 | * "h2"
23 | * "h3"
24 | * "paragraph"
25 | * "smallParagraph"
26 | * "body"
27 | ```
28 |
29 | ## Component
30 |
31 | ```html
32 |
33 |
34 | h1 Hello there
35 |
36 |
37 | ```
38 |
39 | The component prop decides the component type. If component prop
40 | is used with variant prop, the component is rendered with component
41 | prop type but its styles is the one declared in variant prop.
42 |
43 | ## Display
44 |
45 | Typography can be a inline or a block component based on its display
46 | prop.
47 |
48 | ```html
49 |
50 |
51 | h1 Hello there inline
52 |
53 |
54 | ```
55 |
56 | ## Color
57 |
58 | Typography has different color variants.
59 |
60 | * "primary"
61 | * "green"
62 | * "red"
63 | * "black"
64 | * "dark-gray"
65 | * "light-gray"
66 | * "border-gray"
67 |
68 | ```html
69 |
70 |
71 | h1 Hello there green
72 |
73 |
74 | ```
75 |
76 | #### Additional content
77 | You can use a custom class.
78 |
79 | ```html
80 |
81 | h1 Hello there
82 |
83 | ```
84 |
--------------------------------------------------------------------------------
/src/components/loader/loader.tsx:
--------------------------------------------------------------------------------
1 | import classnames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import IDiv from "../../interfaces/div";
4 |
5 | export default class Loader extends PureComponent {
6 | public componentDidUpdate(prevProps: ILoaderProps) {
7 | const { active, disableScroll } = this.props;
8 | const wasScrollDisabled = prevProps.disableScroll;
9 | const wasActive = prevProps.active;
10 |
11 | if (active && disableScroll && (!wasScrollDisabled || !wasActive)) {
12 | document.body.classList.add("q-disable-loader-scroll");
13 | } else if (wasScrollDisabled && (!active || (active && !disableScroll))) {
14 | document.body.classList.remove("q-disable-loader-scroll");
15 | }
16 | }
17 |
18 | public componentDidMount() {
19 | if (this.props.disableScroll && this.props.active) {
20 | document.body.classList.add("q-disable-loader-scroll");
21 | }
22 | }
23 |
24 | public render() {
25 | const { active, fixed, disableScroll, ...props } = this.props;
26 |
27 | if (!active) {
28 | return null;
29 | }
30 |
31 | const loaderClasses = classnames({
32 | "q-loader": true,
33 | "q-loader-fixed": Boolean(fixed),
34 | });
35 |
36 | return (
37 |
38 |
39 |
40 | {Array.from({ length: 12 }, (_, i) =>
)}
41 |
42 |
43 |
44 | );
45 | }
46 | }
47 |
48 | export interface ILoaderProps extends IDiv {
49 | active: boolean;
50 | type?: string;
51 | fixed?: boolean;
52 | disableScroll?: boolean;
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/link/link.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import ILink from "../../interfaces/link";
4 | import { buttonSize, variantTypes } from "../../types/button";
5 | import classNamesDefault from "../../utils/class-names-default";
6 | import Icon from "../icon";
7 | import Loader from "../loader";
8 |
9 | export default class Link extends PureComponent {
10 |
11 | public render() {
12 | const {
13 | variant = "primary",
14 | size = "medium",
15 | fluid,
16 | disabled,
17 | icon,
18 | round,
19 | ripple = true,
20 | circular,
21 | children,
22 | className,
23 | loading = false,
24 | to,
25 | ...props
26 | } = this.props;
27 | const buttonClasses = classNames(
28 | classNamesDefault({ variant, fluid, disabled, round }),
29 | size && `q-button-${size}`,
30 | circular && "q-circular",
31 | loading && "loading",
32 | ripple && "q-ripple",
33 | className, "q-anchor",
34 | disabled && to && "q-anchor-disabled",
35 | );
36 | return (
37 |
42 |
43 | {icon && }
44 | {children && {children}}
45 |
46 | );
47 | }
48 | }
49 |
50 | interface ILinkProps extends ILink {
51 | variant?: variantTypes;
52 | size?: buttonSize;
53 | fluid?: boolean;
54 | disabled?: boolean;
55 | icon?: string;
56 | round?: boolean;
57 | ripple?: boolean;
58 | circular?: boolean;
59 | className?: string;
60 | loading?: boolean;
61 | to: string;
62 | children?: React.ReactNode;
63 | }
64 |
--------------------------------------------------------------------------------
/documents/components/step-progress-bar.md:
--------------------------------------------------------------------------------
1 | # Step Progress Bar
2 |
3 | A progress bar indicating what stage an action is. It is green by default and should get childrens of type Step.
4 |
5 | ```html
6 |
7 |
8 |
9 | ```
10 |
11 | ## Step
12 |
13 | Step Progress Bar can have unlimited steps. Each added step will adjust the style in occurding to the width of the parent.
14 |
15 | ```html
16 |
17 |
18 |
19 |
20 |
21 | ```
22 |
23 | Giving an active prop to a step declares that step as an active. Consecutive steps must be all active in order to properly display styles. For example if only the last Step element is active, other Steps will not be shown as active. To accomplish this set all Steps to active through the latest active step.
24 |
25 | An all completed step progress bar example is like this
26 |
27 | ```html
28 |
29 |
30 |
31 |
32 |
33 |
34 | ```
35 |
36 | ## Color
37 |
38 | You can change the color prop to give some predefined styles to the component. For now, there are only 4 type of variants which are:
39 |
40 | * "primary"
41 | * "green"
42 | * "red"
43 | * "black"
44 | * "dark-gray"
45 | * "light-gray"
46 | * "border-gray"
47 |
48 | ```html
49 |
50 |
51 |
52 | ```
53 |
54 | #### Additional content
55 | You can give additional class names.
56 |
57 | ```html
58 |
59 |
60 |
61 | ```
--------------------------------------------------------------------------------
/src/styles/components/_switch.scss:
--------------------------------------------------------------------------------
1 | @import '../mixins/_color-scheme';
2 |
3 | .q-switch-wrapper {
4 | user-select: none;
5 | position: relative;
6 | display: flex;
7 | width: $switch-width;
8 | height: $switch-height;
9 | margin: 0;
10 |
11 | .q-input {
12 | opacity: 0;
13 | width: 0;
14 | height: 0;
15 | }
16 |
17 | .q-label {
18 | margin-left: 46px;
19 | margin-top: 2px;
20 | }
21 |
22 | .q-switch-slider {
23 | position: absolute;
24 | cursor: pointer;
25 | border-radius: 23px;
26 | top: 0;
27 | left: 0;
28 | right: 0;
29 | bottom: 0;
30 | background-color: $dark-gray-two;
31 | -webkit-transition: .4s;
32 | transition: .4s;
33 | }
34 |
35 | .q-switch-slider:before {
36 | position: absolute;
37 | content: "";
38 | height: 20px;
39 | width: 20px;
40 | left: 3px;
41 | bottom: 3px;
42 | background-color: $white;
43 | -webkit-transition: .3s;
44 | border-radius: 50%;
45 | transition: .4s;
46 | }
47 |
48 | .q-input:checked + .q-switch-slider {
49 | background-color: $primary;
50 | }
51 |
52 | .q-input:checked + .q-switch-slider:before {
53 | -webkit-transform: translateX(16px);
54 | -ms-transform: translateX(16px);
55 | transform: translateX(16px);
56 | }
57 |
58 |
59 | &.q-error {
60 | color: $red;
61 |
62 | .q-switch-slider {
63 | background-color: $red;
64 | }
65 |
66 | .q-input:checked + .q-switch-slider {
67 | background-color: $red;
68 | }
69 |
70 | }
71 |
72 | &.q-disabled {
73 | color: $dark-gray;
74 |
75 | .q-switch-slider {
76 | background-color: $dark-gray;
77 | }
78 |
79 | .q-switch-slider:before {
80 | background-color: $dark-gray-two;
81 | }
82 |
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/components/typography/typography.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import ITypography from "../../interfaces/typography";
4 | import { colorTypes } from "../../types/color";
5 | import { displayTypes, variantTypes } from "../../types/typography";
6 | import classNamesDefault from "../../utils/class-names-default";
7 |
8 | export default class Typography extends PureComponent {
9 |
10 | public render() {
11 | const {
12 | variant,
13 | component,
14 | bold = false,
15 | underline,
16 | noWrap = false,
17 | display = "initial",
18 | color,
19 | children,
20 | className,
21 | ...props
22 | } = this.props;
23 | const typographyClasses = classNames(
24 | "q-typography",
25 | classNamesDefault({ variant }),
26 | bold && "bold",
27 | underline && "underline",
28 | noWrap && "ellipsis",
29 | display && `q-${display}`,
30 | color && `${color}`,
31 | className,
32 | );
33 | const defaultVariantMapping: any = {
34 | body: "p",
35 | h1: "h1",
36 | h2: "h2",
37 | h3: "h3",
38 | paragraph: "p",
39 | smallParagraph: "p",
40 | subtitle: "p",
41 | };
42 | const Component = component || defaultVariantMapping[variant];
43 | return (
44 |
45 | {children}
46 |
47 | );
48 | }
49 | }
50 |
51 | interface ITypographyProps extends ITypography {
52 | variant: variantTypes;
53 | component?: string;
54 | bold?: boolean;
55 | underline?: boolean;
56 | noWrap?: boolean;
57 | display?: displayTypes;
58 | color?: colorTypes;
59 | className?: string;
60 | children?: React.ReactNode;
61 | }
62 |
--------------------------------------------------------------------------------
/src/styles/components/_modal.scss:
--------------------------------------------------------------------------------
1 | @import "../animations/_modal.scss";
2 | @import "../_variables";
3 |
4 | .q-modal-main {
5 | position: fixed;
6 | background-color: $background-gray-two;
7 | top: 0;
8 | left: 0;
9 | display: block;
10 | text-align: center;
11 | height: 100vh;
12 | width: 100vw;
13 | z-index: $modal-z-index;
14 | overflow-y: scroll;
15 |
16 | .q-modal-header {
17 | padding: 1rem 1rem 0 1rem;
18 | border-bottom: 1px solid $very-light-pink;
19 | width: 100%;
20 | height: $modal-header-height;
21 | display: flex;
22 | position: fixed;
23 | background-color: $white;
24 | z-index: $modal-z-index;
25 |
26 | .q-modal-title {
27 | font-size: $font-size-base;
28 | font-weight: $bold;
29 | width: 90%;
30 | text-overflow: ellipsis;
31 | white-space: nowrap;
32 | overflow: hidden;
33 | margin-bottom: 0;
34 | text-align: center;
35 | margin-right: auto;
36 | margin-left: auto;
37 | padding-left: $modal-icon-size;
38 | height: 100%;
39 | line-height: $font-size-base;
40 | }
41 |
42 | .q-icon {
43 | padding: 0;
44 | }
45 |
46 | &.q-has-left-icon {
47 | .q-modal-title {
48 | padding-left: 0;
49 | }
50 | }
51 | }
52 |
53 | .q-modal-content {
54 | background-color: $white;
55 | overflow-y: scroll;
56 |
57 | -ms-overflow-style: none;
58 | padding: $modal-content-padding;
59 | margin: 75px 15px 15px;
60 | border: 1px solid $very-light-pink;
61 |
62 | &::-webkit-scrollbar {
63 | width: 0;
64 | height: 0;
65 | }
66 | }
67 |
68 | .q-modal-actions {
69 | margin: 15px;
70 | }
71 |
72 | }
73 |
74 | body.q-disable-scroll {
75 | overflow: hidden;
76 | }
77 |
78 | //Animations
79 | @include modal-animation;
80 |
--------------------------------------------------------------------------------
/src/stories/bottom-sheet.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/react";
2 | import React, { PureComponent } from "react";
3 | import BottomSheet from "../components/bottom-sheet";
4 | import Button from "../components/button";
5 | import Icon from "../components/icon";
6 | import { animationTypes } from "../types/modal";
7 | import "./styles/bottom-sheet.css";
8 |
9 | const stories = storiesOf("Bottom Sheet", module);
10 |
11 | interface IProps {
12 | animation?: animationTypes;
13 | rightIcon?: string;
14 | leftIcon?: string;
15 | }
16 |
17 | interface IState {
18 | show: boolean;
19 | }
20 |
21 | class BottomSheetWrapper extends PureComponent {
22 | public constructor(props: IProps) {
23 | super(props);
24 | this.state = {show: false};
25 | this.handleChange = this.handleChange.bind(this);
26 | this.closeModal = this.closeModal.bind(this);
27 | }
28 |
29 | public render() {
30 | return (
31 | <>
32 | WebKit Scroll Bug Test
33 |
34 |
35 |
36 | Some stuff
37 | Some another stuff
38 | Some stuff
39 | Some stuff
40 | Some stuff
41 | Some stuff
42 | Some stuff
43 |
44 | Some stuff
45 | Some stuff
46 |
47 | >
48 | );
49 | }
50 |
51 | private handleChange() {
52 | this.setState({show: !this.state.show});
53 | }
54 |
55 | private closeModal() {
56 | this.setState({show: false});
57 | }
58 | }
59 |
60 | stories.add("Default", () => );
61 |
--------------------------------------------------------------------------------
/src/styles/animations/_modal.scss:
--------------------------------------------------------------------------------
1 | @mixin modal-animation($duration: 300ms) {
2 | .q-slideInRight {
3 | &.q-modal-enter {
4 | transform: translateX(100vw);
5 | }
6 |
7 | &.q-modal-enter-active {
8 | transform: translateX(0);
9 | transition: transform $duration;
10 | }
11 |
12 | &.q-modal-exit {
13 | transform: translateX(0);
14 | }
15 |
16 | &.q-modal-exit-active {
17 | transform: translateX(100vw);
18 | transition: transform $duration;
19 | }
20 | }
21 |
22 | .q-slideInLeft {
23 | &.q-modal-enter {
24 | transform: translateX(-100vw);
25 | }
26 |
27 | &.q-modal-enter-active {
28 | transform: translateX(0);
29 | transition: transform $duration;
30 | }
31 |
32 | &.q-modal-exit {
33 | transform: translateX(0);
34 | }
35 |
36 | &.q-modal-exit-active {
37 | transform: translateX(-100vw);
38 | transition: transform $duration;
39 | }
40 | }
41 |
42 | .q-slideInDown {
43 | &.q-modal-enter {
44 | transform: translateY(100vh);
45 | }
46 |
47 | &.q-modal-enter-active {
48 | transform: translateY(0);
49 | transition: transform $duration;
50 | }
51 |
52 | &.q-modal-exit {
53 | transform: translateY(0);
54 | }
55 |
56 | &.q-modal-exit-active {
57 | transform: translateY(100vh);
58 | transition: transform $duration;
59 | }
60 | }
61 |
62 | .q-slideInUp {
63 | &.q-modal-enter {
64 | transform: translateY(-100vh);
65 | }
66 |
67 | &.q-modal-enter-active {
68 | transform: translateY(0);
69 | transition: transform $duration;
70 | }
71 |
72 | &.q-modal-exit {
73 | transform: translateY(0);
74 | }
75 |
76 | &.q-modal-exit-active {
77 | transform: translateY(-100vh);
78 | transition: transform $duration;
79 | }
80 | }
81 | }
82 |
83 |
84 |
--------------------------------------------------------------------------------
/documents/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Page Not Found
7 |
8 |
23 |
24 |
25 |
26 |
404
27 |
Page Not Found
28 |
The specified file was not found on this website. Please check the URL for mistakes and try again.
29 |
Why am I seeing this?
30 |
This page was generated by the Firebase Command-Line Interface. To modify it, edit the 404.html file in your project's configured public directory.
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/styles/components/_popup.scss:
--------------------------------------------------------------------------------
1 | @import "../animations/_popup.scss";
2 | @import "../variables";
3 |
4 | .q-popup-main {
5 | position: absolute;
6 | background-color: $white;
7 | display: block;
8 | text-align: center;
9 | padding: $popup-padding;
10 | width: 100%;
11 | max-width: 90vw;
12 | max-height: 90vh;
13 | border-radius: $border-radius-popup;
14 | z-index: 1031;
15 | box-sizing: border-box;
16 |
17 | .q-popup-content {
18 | overflow-y: scroll;
19 | -ms-overflow-style: none;
20 | -webkit-overflow-scrolling: touch;
21 | max-height: 50vh;
22 |
23 | &::-webkit-scrollbar {
24 | width: 0;
25 | height: 0;
26 | }
27 | }
28 |
29 | .q-popup-icon-position {
30 | display: flex;
31 | justify-content: flex-end;
32 | width: 16px;
33 | position: absolute;
34 | right: 0;
35 | }
36 |
37 | .q-popup-icon-left {
38 | justify-content: flex-start;
39 | }
40 | }
41 |
42 | //Animations
43 | @include popup-animation;
44 |
45 | .q-popup-overlay {
46 | height: 100%;
47 | width: 100%;
48 | position: fixed;
49 | top: 0;
50 | left: 0;
51 | z-index: 1030;
52 | background-color: rgba(0, 0, 0, 0.5);
53 | display: flex;
54 | justify-content: center;
55 | align-items: center;
56 | }
57 |
58 | body.q-disable-scroll {
59 | overflow: hidden;
60 | }
61 |
62 | .q-popup-header {
63 | display: flex;
64 | align-items: center;
65 | position: relative;
66 | margin-bottom: $space-medium;
67 | min-height: 16px;
68 |
69 | &.with-border {
70 | padding-bottom: $popup-padding;
71 | border-bottom: 1px solid $border-gray;
72 | margin-left: -$popup-padding;
73 | margin-right: -$popup-padding;
74 |
75 | .q-popup-icon-position {
76 | right: $popup-padding !important;
77 | }
78 | }
79 |
80 | &-text {
81 | white-space: nowrap;
82 | overflow: hidden;
83 | text-overflow: ellipsis;
84 | margin: 0 auto;
85 | padding: 0 $space-large;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/components/quantity-selector/quantity-selector.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import { qsSize } from "../../types/quantity-selector";
4 | import classNamesDefault from "../../utils/class-names-default";
5 | import Icon from "../icon";
6 | import { IProps as IIconProps } from "../icon/icon";
7 |
8 | export default class QuantitySelector extends PureComponent {
9 | public render() {
10 | const { count, fluid, size = "medium" } = this.props;
11 |
12 | const qsClasses = classNames(
13 | classNamesDefault({ fluid }),
14 | size && `q-${size}`,
15 | "q-qs-main",
16 | );
17 |
18 | return (
19 |
20 |
{this.renderDecrementIcon()}
21 |
{count || 0}
22 |
{this.renderIncrementIcon()}
23 |
24 | );
25 | }
26 |
27 | private renderDecrementIcon() {
28 | const { onDecrement, count, iconProps } = this.props;
29 | const disabled = !count;
30 | const className = count === 1 ? "icon-trash" : "icon-minus";
31 | const name = count === 1 ? "trash" : "minus";
32 |
33 | return (
34 |
41 | );
42 | }
43 |
44 | private renderIncrementIcon() {
45 | const { onIncrement, iconProps } = this.props;
46 |
47 | return (
48 |
54 | );
55 | }
56 | }
57 |
58 | interface IQuantitySelectorProps {
59 | onIncrement: () => void;
60 | onDecrement: () => void;
61 | count?: number;
62 | size?: qsSize;
63 | fluid?: boolean;
64 | iconProps?: IIconProps | any;
65 | }
66 |
--------------------------------------------------------------------------------
/documents/components/checkbox.md:
--------------------------------------------------------------------------------
1 | # Checkbox
2 |
3 | Checkbox control component allow a user to check an option or not. These components must be used with different value prop as they work as normal HTML checkbox tag.
4 |
5 | ## Label
6 | It is possible to add label next to the checkbox by giving label property
7 |
8 | ```
9 |
10 | ```
11 |
12 |
13 |
14 | ## Error
15 | Adding error prop to checkbox element changes the component's style to indicate an error has happened.
16 |
17 | ```
18 |
19 | ```
20 |
21 |
22 |
23 | #### Additional content
24 | And of course you can use any other HTML checkbox properties:
25 | ```
26 |
27 | ```
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/components/modal/modal.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React from "react";
3 | import { CSSTransition } from "react-transition-group";
4 | import { animationTypes } from "../../types/modal";
5 | import Actions from "./modal-actions";
6 | import Content from "./modal-content";
7 | import Header from "./modal-header";
8 |
9 | export default class Modal extends React.Component {
10 | public static Header = Header;
11 | public static Actions = Actions;
12 | public static Content = Content;
13 |
14 | public componentDidMount(): void {
15 | if (this.props.show && typeof window !== "undefined") {
16 | document.body.classList.add("q-disable-scroll");
17 | }
18 | }
19 |
20 | public componentWillUpdate(
21 | nextProps: Readonly,
22 | nextState: Readonly<{}>,
23 | nextContext: any,
24 | ) {
25 | if (nextProps.show !== this.props.show && this.props.onChange) {
26 | this.props.onChange();
27 | }
28 | if (nextProps.show !== this.props.show && nextProps.show && typeof window !== "undefined") {
29 | document.body.classList.add("q-disable-scroll");
30 | document.documentElement.classList.add("q-disable-scroll");
31 | } else if (nextProps.show !== this.props.show && !nextProps.show && typeof window !== "undefined") {
32 | document.body.classList.remove("q-disable-scroll");
33 | document.documentElement.classList.remove("q-disable-scroll");
34 | }
35 | }
36 |
37 | public render() {
38 | const { show, children, animation = "slideInRight", className } = this.props;
39 | const modalClasses = classNames(
40 | "q-modal-main",
41 | className,
42 | );
43 | return
49 | {children}
50 | ;
51 | }
52 | }
53 |
54 | interface IModalProps {
55 | show: boolean;
56 | animation?: animationTypes;
57 | children?: React.ReactNode;
58 | onChange?: () => void;
59 | className?: string;
60 | }
61 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 |
4 | defaults: &defaults
5 | docker:
6 | - image: cimg/node:20.10
7 |
8 |
9 | orbs:
10 | codecov: codecov/codecov@1.0.4
11 |
12 |
13 | jobs:
14 | test:
15 | <<: *defaults
16 | steps:
17 | - checkout
18 | - restore_cache:
19 | keys:
20 | - dependencies-{{ checksum "package.json" }}
21 | - run: npm install
22 | - save_cache:
23 | paths:
24 | - node_modules
25 | key: dependencies-{{ checksum "package.json" }}
26 | - run: npm run coverage
27 | - store_artifacts:
28 | path: coverage
29 | - codecov/upload:
30 | file: coverage/*.json
31 | token: $CODECOV_TOKEN
32 | - run: npm run build
33 | - persist_to_workspace:
34 | root: .
35 | paths:
36 | - README.md
37 | - CHANGELOG.md
38 | - LICENSE
39 | - package.json
40 | - package-lock.json
41 | - .npmignore
42 | - components
43 | - enums
44 | - interfaces
45 | - styles
46 | - types
47 | - utils
48 | - coverage
49 | - images
50 | deploy:
51 | <<: *defaults
52 | steps:
53 | - attach_workspace:
54 | at: .
55 | - run:
56 | name: List Workspace
57 | command: ls
58 | - run:
59 | name: Authenticate with registry
60 | command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN_STOREFRONT_TR" > .npmrc
61 | - run:
62 | name: Publish package
63 | command: npm publish
64 |
65 | workflows:
66 | version: 2
67 | test-deploy:
68 | jobs:
69 | - test:
70 | filters:
71 | tags:
72 | only: /^v.*/
73 | - hold:
74 | type: approval
75 | requires:
76 | - test
77 | filters:
78 | branches:
79 | only: master
80 | - deploy:
81 | requires:
82 | - hold
83 | filters:
84 | branches:
85 | only: master
86 |
--------------------------------------------------------------------------------
/src/styles/components/_select.scss:
--------------------------------------------------------------------------------
1 | @import '../mixins/_color-scheme';
2 | @import '../mixins/_fluid.scss';
3 |
4 | .q-select-wrapper {
5 | display: inline-flex;
6 | position: relative;
7 | align-items: center;
8 | height: $select-height;
9 | width: fit-content;
10 | text-align: center;
11 | align-content: center;
12 | -webkit-appearance: none;
13 | -moz-appearance: none;
14 | appearance: none;
15 | border-radius: $border-radius;
16 | background-color: $background-gray;
17 | border: 1px solid $border-gray-two;
18 | color: $dark-gray;
19 |
20 | &.q-fluid {
21 | @include fluid;
22 | }
23 |
24 | &.q-disabled {
25 | opacity: 0.5;
26 | user-select: none;
27 | -moz-user-select: none;
28 |
29 | select {
30 | pointer-events: none;
31 | }
32 | }
33 |
34 | &.q-error {
35 | animation-name: select-error;
36 | animation-duration: .2s;
37 | animation-direction: alternate;
38 | animation-iteration-count: 2;
39 |
40 | &.q-select-wrapper {
41 | border-color: $red;
42 | }
43 | }
44 |
45 | @keyframes select-error {
46 | 0% {
47 | transform: scale(1);
48 | }
49 | 50% {
50 | transform: scale(1.2);
51 | }
52 | }
53 |
54 | &:not(.q-disabled) {
55 | .q-select-group:active {
56 | border: 1px solid $light-gray;
57 | }
58 | }
59 |
60 | .select-left-padding {
61 | padding-left: 10px;
62 | }
63 |
64 | .select-right-padding {
65 | padding-right: 20px;
66 | }
67 |
68 | .q-select-icon {
69 | font-size: 15px;
70 | pointer-events: none;
71 | position: absolute;
72 | }
73 | .q-select-icon-left {
74 | @extend .q-select-icon;
75 | left: 5px;
76 | }
77 | .q-select-icon-right {
78 | @extend .q-select-icon;
79 | right: 5px;
80 | }
81 | }
82 |
83 | .q-select {
84 | width: 100%;
85 | color: currentColor;
86 | padding: $select-padding;
87 | font-size: $font-size-paragraph;
88 | -webkit-appearance: none;
89 | -moz-appearance: none;
90 | appearance: none;
91 | border: 0;
92 | line-height: $select-line-height;
93 | background: 0;
94 | &:focus {
95 | outline: none !important;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/components/subheader/subheader.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import IDiv from "../../interfaces/div";
4 | import { sizeTypes } from "../../types/icon";
5 | import { variantTypes } from "../../types/typography";
6 | import Icon from "../icon";
7 | import Typography from "../typography";
8 |
9 | export default class SubHeader extends PureComponent {
10 | public render() {
11 | const {
12 | title,
13 | titleComponent,
14 | subTitle,
15 | rightIcon,
16 | leftIcon,
17 | rightIconSize,
18 | leftIconSize,
19 | className,
20 | leftIconOnClick,
21 | rightIconOnClick,
22 | ...props
23 | } = this.props;
24 | const subHeaderClasses = classNames("q-subheader", className);
25 | return (
26 |
27 |
28 | {leftIcon && }
29 |
30 | {subTitle &&
31 |
32 | {title}
33 | {subTitle}
34 |
35 | }
36 | {!subTitle &&
37 |
38 | {title}
39 |
40 | }
41 |
42 | {rightIcon && }
43 |
44 |
45 |
46 | );
47 | }
48 | }
49 |
50 | interface ISubHeader extends IDiv {
51 | title: string;
52 | titleComponent?: variantTypes;
53 | subTitle?: string;
54 | leftIcon?: string;
55 | rightIcon?: string;
56 | leftIconSize?: sizeTypes;
57 | rightIconSize?: sizeTypes;
58 | leftIconOnClick?: (event: any) => any;
59 | rightIconOnClick?: (event: any) => any;
60 | }
61 |
--------------------------------------------------------------------------------
/src/styles/components/_button.scss:
--------------------------------------------------------------------------------
1 | @import "../mixins/_color-scheme";
2 | @import "../mixins/_fluid.scss";
3 |
4 | .q-button {
5 | cursor: pointer;
6 | display: inline-block;
7 | position: relative;
8 | user-select: none;
9 | padding-left: $space-medium;
10 | padding-right: $space-medium;
11 | font-weight: $regular;
12 | font-family: $font-family;
13 | font-style: normal;
14 | font-stretch: normal;
15 | line-height: normal;
16 | letter-spacing: normal;
17 | text-align: center;
18 | border-radius: $border-radius-button;
19 |
20 | &.q-button-large {
21 | height: $button-height-large;
22 | font-size: $button-font-size-large;
23 | }
24 |
25 | &.q-button-medium {
26 | height: $button-height-medium;
27 | font-size: $button-font-size-medium;
28 | }
29 |
30 | &.q-button-small {
31 | height: $button-height-small;
32 | font-size: $button-font-size-small;
33 | }
34 |
35 | &.q-button-xsmall {
36 | height: $button-height-xsmall;
37 | font-size: $button-font-size-small;
38 | }
39 |
40 | &.q-primary {
41 | @include primary;
42 | }
43 |
44 | &.q-secondary {
45 | @include secondary;
46 | }
47 |
48 | &.q-gray {
49 | @include gray;
50 | }
51 |
52 | &.q-disabled {
53 | @include disabled;
54 | }
55 |
56 | &.q-fluid {
57 | @include fluid;
58 | }
59 |
60 | &.q-round {
61 | border-radius: $button-round;
62 | }
63 |
64 | &.q-circular {
65 | border-radius: 50%;
66 | width: $button-circular;
67 | height: $button-circular;
68 | padding: 0;
69 | }
70 |
71 | &:focus {
72 | outline: none !important;
73 | }
74 |
75 | &:hover {
76 | text-decoration: none;
77 | }
78 |
79 | //Icon
80 | .q-icon {
81 | padding-left: 0;
82 | padding-right: $space-small;
83 | }
84 |
85 | .q-icon:only-child {
86 | padding-right: 0;
87 | }
88 |
89 | //Loader
90 | &.loading {
91 | .q-loader {
92 | background-color: transparent;
93 | transform: scale(0.4);
94 | }
95 |
96 | //Hide button contents
97 | .q-loader + span {
98 | opacity: 0;
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/stories/loader.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/react";
2 | import { faker } from '@faker-js/faker';
3 | import React from "react";
4 | import { Box } from "../components/box";
5 | import Button from "../components/button";
6 | import Loader from "../components/loader";
7 |
8 | const stories = storiesOf("Loader", module);
9 | const onClick = () => alert("This should not pop up!");
10 | const hugeContent = faker.lorem.lines(300);
11 |
12 | stories.add("Loader over Box", () => (
13 |
14 |
15 |
16 | Content 1
17 |
18 | {faker.lorem.lines(20)}
19 |
20 |
21 |
22 | Content 2
23 | {faker.lorem.lines(20)}
24 |
25 |
26 |
27 | Content 3
28 | {faker.lorem.lines(20)}
29 |
30 |
31 | ));
32 |
33 | stories.add("Fixed loader", () => {
34 | const [loader, setLoader] = React.useState(false);
35 | const toggle = () => setLoader(!loader);
36 |
37 | return (
38 |
39 |
40 | This loader fixed but still scrollable
41 | PS: This loader will be fixed to document body not to parent
42 |
43 |
44 |
45 |
46 |
47 | {hugeContent}
48 |
49 |
50 | );
51 | });
52 |
53 | stories.add("Fixed loader with disabled scroll for body", () => {
54 | const [loader, setLoader] = React.useState(false);
55 | const toggle = () => setLoader(!loader);
56 |
57 | return (
58 |
59 |
60 | This loader fixed and body not scrollable
61 | PS: This loader will be fixed to document body not to parent
62 |
63 |
64 |
65 |
66 |
67 | {hugeContent}
68 |
69 |
70 | );
71 | });
72 |
--------------------------------------------------------------------------------
/src/stories/select.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/react";
2 | import React from "react";
3 | import Button from "../components/button";
4 | import Select from "../components/select";
5 |
6 | const stories = storiesOf("Select", module);
7 | const items = [
8 | {
9 | id: 1,
10 | name: "Car",
11 | variant: "primary",
12 | },
13 | {
14 | id: 2,
15 | name: "Bike",
16 | variant: "primary",
17 | },
18 | {
19 | id: 3,
20 | name: "Plane",
21 | variant: "secondary",
22 | },
23 | ];
24 |
25 | stories.add("Default", () => (
26 |
27 |
28 |
29 | ));
30 |
31 | stories.add("General Usage", () => {
32 | const [value, setValue] = React.useState("2");
33 |
34 | const handleSelect = (e: any) => {
35 | setValue(e.target.value);
36 | };
37 |
38 | const handleButton = () => {
39 | setValue(value === "1" ? "2" : "1");
40 | };
41 |
42 | return (
43 |
44 |
55 |
56 |
57 | );
58 | });
59 |
60 | stories.add("Icon", () => {
61 |
62 | return (
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | );
73 | });
74 |
75 | stories.add("Fluid", () => (
76 |
77 |
78 |
79 | ));
80 |
81 | stories.add("Disabled", () => (
82 |
83 |
84 |
85 | ));
86 |
87 | stories.add("Error", () => (
88 |
89 |
90 |
91 | ));
92 |
--------------------------------------------------------------------------------
/src/components/accordion/accordion.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import Content from "./accordion-content";
4 | import Header from "./accordion-header";
5 |
6 | export default class Accordion extends PureComponent {
7 | public static Header = Header;
8 | public static Content = Content;
9 |
10 | public constructor(props: IAccordionProps) {
11 | super(props);
12 | this.state = {
13 | expanded: this.props.expanded || false,
14 | };
15 | this.handleHeaderClick = this.handleHeaderClick.bind(this);
16 | }
17 |
18 | public componentWillReceiveProps(
19 | nextProps: IAccordionProps,
20 | prevProps: IAccordionProps) {
21 | if (nextProps.expanded !== undefined) {
22 | this.setState({ expanded: nextProps.expanded });
23 | }
24 | }
25 |
26 | public componentWillUpdate(
27 | nextProps: Readonly,
28 | nextState: Readonly,
29 | nextContext: any): void {
30 | if (nextState.expanded !== this.state.expanded) {
31 | if (!!this.props.onChange) {
32 | this.props.onChange(nextState.expanded);
33 | }
34 | }
35 | }
36 |
37 | public render() {
38 | const {
39 | className,
40 | children,
41 | } = this.props;
42 |
43 | const accordionClasses = classNames("q-accordion", className);
44 | const [AccordionHeader, AccordionContent] = React.Children.toArray(children);
45 |
46 | return (
47 |
48 | {React.cloneElement(
49 | AccordionHeader as React.ReactElement,
50 | { expanded: this.state.expanded, handleClick: this.handleHeaderClick })
51 | }
52 | {React.cloneElement(AccordionContent as React.ReactElement, { expanded: this.state.expanded })}
53 |
54 | );
55 | }
56 |
57 | private handleHeaderClick() {
58 | this.setState({ expanded: !this.state.expanded });
59 | }
60 |
61 | }
62 |
63 | interface IAccordionProps {
64 | className?: string;
65 | expanded?: boolean;
66 | children?: React.ReactNode;
67 | onChange?: (expanded: boolean) => void;
68 | }
69 |
70 | interface IAccordionState {
71 | expanded: boolean;
72 | }
73 |
--------------------------------------------------------------------------------
/src/styles/components/_radio.scss:
--------------------------------------------------------------------------------
1 | @import '../mixins/_color-scheme';
2 |
3 | .q-radio-wrapper {
4 | display: inline-flex;
5 |
6 | .q-input[type=radio] {
7 | opacity: 0;
8 | position: absolute;
9 | }
10 |
11 | .q-input[type=radio]:focus {
12 | outline: none !important;
13 | }
14 |
15 | .q-input[type=radio]:checked + .q-label > span::before {
16 | border-color: $primary;
17 | }
18 |
19 | .q-input[type=radio]:checked + .q-label > span::after {
20 | transform: scale(1);
21 | }
22 |
23 | span::before, span::after {
24 | position: absolute;
25 | content: '';
26 | border-radius: 50%;
27 | display: block;
28 | box-sizing: border-box;
29 | }
30 |
31 | span::after {
32 | top: 6px;
33 | left: 6px;
34 | width: 8px;
35 | height: 8px;
36 | }
37 |
38 | span {
39 | width: 25px;
40 | height: 25px;
41 | vertical-align: middle;
42 | display: inline-block;
43 | position: relative;
44 | cursor: pointer;
45 | outline: none;
46 | user-select: none;
47 |
48 | &::before {
49 | left: 0;
50 | top: 0;
51 | width: 20px;
52 | height: 20px;
53 | border: 2px solid rgba(0,0,0,.54);
54 | }
55 |
56 | &::after {
57 | transform: scale(0);
58 | background: $primary;
59 | }
60 | }
61 |
62 | .q-input[type=radio]:checked:disabled {
63 | & + .q-label > span {
64 | color: $light-gray;
65 | }
66 |
67 | & + .q-label span::before {
68 | border-color: $light-gray;
69 | }
70 |
71 | & + .q-label span::after {
72 | background: $light-gray;
73 | opacity: .4;
74 | }
75 | }
76 |
77 | .q-input[type=radio]:disabled {
78 | & + .q-label > span::before {
79 | opacity: .4;
80 | }
81 |
82 | & + .q-label > span {
83 | color: $light-gray;
84 | }
85 |
86 | & + .q-label .q-typography {
87 | color: $light-gray;
88 | opacity: .4;
89 | }
90 | }
91 |
92 | &.q-error {
93 | color: $red;
94 |
95 | .q-input[type=radio] + .q-label > span::before {
96 | border: 2px solid $red;
97 | }
98 |
99 | .q-input[type=radio] + .q-label > span::after {
100 | background: $red;
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/styles/components/_checkbox.scss:
--------------------------------------------------------------------------------
1 | @import '../mixins/_color-scheme';
2 | @import '../mixins/_fluid.scss';
3 |
4 | .q-checkbox-wrapper {
5 | user-select: none;
6 | font-family: icomoon !important;
7 |
8 | .q-input[type=checkbox] + .q-label {
9 | display: inline-flex;
10 | align-items: center;
11 | margin: $checkbox-margin;
12 | cursor: pointer;
13 | padding: $checkbox-padding;
14 | }
15 |
16 | .q-input[type=checkbox] {
17 | display: none;
18 | }
19 |
20 | .q-input[type=checkbox]:focus {
21 | display: none;
22 | background: transparent;
23 | box-shadow: none;
24 | outline: none;
25 | -webkit-tap-highlight-color: rgba(0,0,0,0) !important;
26 | }
27 |
28 | .q-input[type=checkbox] + .q-label:before {
29 | font-family: icomoon !important;
30 | content: "\e933";
31 | border: $checkbox-border-width solid $checkbox-border-passive;
32 | border-radius: $border-radius;
33 | display: inline-block;
34 | color: transparent;
35 | line-height: 1rem;
36 | transition: .2s;
37 | margin-right: 5px;
38 | }
39 |
40 | .q-input[type=checkbox]:checked + .q-label {
41 | font-weight: $bold;
42 | }
43 |
44 | .q-input[type=checkbox] + .q-label:active:before {
45 | transform: scale(0);
46 | }
47 |
48 | .q-input[type=checkbox]:checked + .q-label:before {
49 | background-color: $primary;
50 | border-color: $primary;
51 | color: $white;
52 | }
53 |
54 | .q-input[type=checkbox]:disabled + .q-label {
55 | color: $light-gray;
56 | }
57 |
58 | .q-input[type=checkbox]:checked:disabled + .q-label {
59 | color: $light-gray;
60 | }
61 |
62 | .q-input[type=checkbox]:disabled + .q-label:before {
63 | transform: scale(1);
64 | border-color: $light-gray;
65 | }
66 |
67 | .q-input[type=checkbox]:checked:disabled + .q-label:before {
68 | transform: scale(1);
69 | background-color: $light-gray;
70 | border-color: $light-gray;
71 | }
72 |
73 | &.q-error {
74 | .q-input[type=checkbox] + .q-label:before {
75 | border-color: $red;
76 | }
77 |
78 | .q-input[type=checkbox]:checked + .q-label:before {
79 | background-color: $white;
80 | border-color: $red;
81 | color: $red;
82 | }
83 |
84 | .q-input[type=checkbox] + .q-label {
85 | color: $red;
86 | }
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/components/rating/rating.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import IDiv from "../../interfaces/div";
4 | import { colorTypes } from "../../types/color";
5 | import { ratingSize } from "../../types/rating";
6 | import classNamesDefault from "../../utils/class-names-default";
7 | import Icon from "../icon";
8 |
9 | export default class Rating extends PureComponent {
10 |
11 | public defaultSizeMapping: any = {
12 | large: 18,
13 | medium: 14,
14 | small: 10,
15 | };
16 |
17 | private defaultMargin: number = 3;
18 | private defaultStars: number = 5;
19 |
20 | public calculateWidth(score: number, size: string, margin: number, stars: number) {
21 | const plus = (Math.ceil(score) - 1) * margin;
22 | return (score * this.defaultSizeMapping[size] + plus) * 100 /
23 | (stars * this.defaultSizeMapping[size] + margin * (stars - 1));
24 | }
25 |
26 | public render() {
27 | const {
28 | value = 0,
29 | icon = "star",
30 | size = "medium",
31 | color = "yellow",
32 | disabled,
33 | children, className, margin = this.defaultMargin, stars = this.defaultStars, ...props } = this.props;
34 | const ratingClasses = classNames(
35 | classNamesDefault({ disabled }),
36 | "q-rating",
37 | size && `q-rating-${size}`,
38 | className,
39 | );
40 |
41 | return (
42 |
43 |
44 | {Array.from({ length: stars }, (_, i) =>
45 | ,
46 | )}
47 |
48 |
49 | {Array.from({ length: stars }, (_, i) =>
50 | ,
51 | )}
52 |
53 |
54 | );
55 | }
56 | }
57 |
58 | interface IProps extends IDiv {
59 | value?: number;
60 | icon?: string;
61 | size?: ratingSize;
62 | disabled?: boolean;
63 | color?: colorTypes;
64 | className?: string;
65 | margin?: number;
66 | stars?: number;
67 | children?: React.ReactNode;
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/select/select.tsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 | import React, { PureComponent } from "react";
3 | import ISelect from "../../interfaces/select";
4 | import { colorTypes } from "../../types/color";
5 | import { variantTypes } from "../../types/select";
6 | import classNamesDefault from "../../utils/class-names-default";
7 | import Icon from "../icon";
8 |
9 | export default class Select extends PureComponent {
10 | public render() {
11 | const {
12 | items,
13 | variant,
14 | fluid,
15 | name = "name",
16 | leftIcon,
17 | rightIcon,
18 | rightIconColor,
19 | leftIconColor,
20 | className,
21 | disabled,
22 | error,
23 | valueKey = "value",
24 | ...props
25 | } = this.props;
26 | const selectDivClasses = classNames(
27 | "q-select-wrapper",
28 | classNamesDefault({ fluid, disabled }),
29 | className,
30 | error && "q-error",
31 | );
32 | const selectClasses = classNames(
33 | "q-select",
34 | !leftIcon && "select-left-padding",
35 | !rightIcon && "select-right-padding",
36 | );
37 | return (
38 |
39 | {leftIcon && }
40 |
54 | {rightIcon && }
55 |
56 | );
57 | }
58 | }
59 |
60 | interface IProps extends ISelect {
61 | variant?: variantTypes;
62 | items: any[];
63 | fluid?: boolean;
64 | value?: string;
65 | valueKey?: string;
66 | name?: string;
67 | error?: boolean;
68 | rightIcon?: string;
69 | leftIcon?: string;
70 | className?: string;
71 | selected?: string;
72 | rightIconColor?: colorTypes;
73 | leftIconColor?: colorTypes;
74 | }
75 |
--------------------------------------------------------------------------------
/src/components/step-progress-bar/test/step-progress-bar.spec.tsx:
--------------------------------------------------------------------------------
1 | import Enzyme, { mount, shallow } from "enzyme";
2 | import Adapter from "@cfaester/enzyme-adapter-react-18";
3 | import { faker } from '@faker-js/faker';
4 | import React from "react";
5 | import sinon from "sinon";
6 | import Step from "../step";
7 | import StepProgressBar from "../step-progress-bar";
8 |
9 | Enzyme.configure({ adapter: new Adapter() });
10 |
11 | const { lorem: { word }, number } = faker;
12 |
13 | describe("step progress bar specs", () => {
14 | const sandbox = sinon.createSandbox();
15 |
16 | afterEach(() => {
17 | sandbox.verifyAndRestore();
18 | });
19 |
20 | it("should render step progress bar", () => {
21 | const wrapper = shallow(
22 |
23 |
24 | ,
25 | );
26 | expect(wrapper.find(".q-spb")).toHaveLength(1);
27 | });
28 |
29 | it("should render additional steps", () => {
30 | const steps = Array.from({ length: number.int({max: 100}) }, () => word());
31 |
32 | const wrapper = mount(
33 |
34 | {steps.map((step) => )}
35 | ,
36 | );
37 | expect(wrapper.find(".q-spb")).toHaveLength(1);
38 | expect(wrapper.find(".q-spb-step")).toHaveLength(steps.length);
39 | });
40 |
41 | it("should steps get active", () => {
42 | const wrapper = mount(
43 |
44 |
45 |
46 |
47 | ,
48 | );
49 | expect(wrapper.find(".q-spb")).toHaveLength(1);
50 | expect(wrapper.find(".active")).toHaveLength(2);
51 | });
52 |
53 | it("should steps have given color prop", () => {
54 | const wrapper = shallow(
55 |
56 |
57 | ,
58 | );
59 | expect(wrapper.find(".q-spb.red")).toHaveLength(1);
60 | });
61 |
62 | it("should accept additional classNames", () => {
63 | const fakeClass = word();
64 | const wrapper = shallow(
65 |
66 |
67 | ,
68 | );
69 |
70 | expect(wrapper.hasClass(fakeClass)).toBe(true);
71 | });
72 |
73 | });
74 |
--------------------------------------------------------------------------------
/src/components/switch/test/switch.spec.tsx:
--------------------------------------------------------------------------------
1 | import Enzyme, { shallow } from "enzyme";
2 | import Adapter from "@cfaester/enzyme-adapter-react-18";
3 | import { faker } from '@faker-js/faker';
4 | import React from "react";
5 | import sinon from "sinon";
6 | import Switch from "../switch";
7 |
8 | Enzyme.configure({ adapter: new Adapter() });
9 |
10 | describe("switch specs", () => {
11 | const sandbox = sinon.createSandbox();
12 |
13 | afterEach(() => {
14 | sandbox.verifyAndRestore();
15 | });
16 |
17 | it("should render switch component", () => {
18 | const wrapper = shallow();
19 |
20 | expect(wrapper).toHaveLength(1);
21 | });
22 |
23 | it("should call onChange function ", () => {
24 | const spy = sandbox.spy();
25 | const text = faker.lorem.word();
26 | const wrapper = shallow();
27 | const event = { target: { value: text } };
28 |
29 | wrapper.find("input").simulate("change", event);
30 |
31 | expect(spy.calledOnce).toBe(true);
32 | });
33 |
34 | it("should not call callback function when changed and switch is disabled", () => {
35 | const spy = sandbox.spy();
36 | const text = faker.lorem.word();
37 | const wrapper = shallow();
38 | const event = { target: { value: text } };
39 |
40 | wrapper.simulate("change", event);
41 |
42 | expect(spy.calledOnce).toBe(false);
43 | });
44 |
45 | it("should render a label", () => {
46 | const text = faker.lorem.word();
47 | const wrapper = shallow();
48 |
49 | expect(wrapper.find("label")).toHaveLength(2);
50 | });
51 |
52 | it("should accept additional classNames", () => {
53 | const testClass = faker.lorem.word();
54 | const wrapper = shallow();
55 | expect(wrapper.find(`.${testClass}`)).toHaveLength(1);
56 | });
57 |
58 | it("should be checkbox type by fault", () => {
59 | const wrapper = shallow();
60 | const inputElement = wrapper.find("input");
61 | expect(inputElement.prop("type")).toBe("checkbox");
62 | });
63 |
64 | it("should have error className when error prop is passed", () => {
65 | const wrapper = shallow();
66 |
67 | expect(wrapper.hasClass("q-error")).toBe(true);
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/src/components/radio/test/radio.spec.tsx:
--------------------------------------------------------------------------------
1 | import Enzyme, { shallow } from "enzyme";
2 | import Adapter from "@cfaester/enzyme-adapter-react-18";
3 | import { faker } from '@faker-js/faker';
4 | import React from "react";
5 | import sinon from "sinon";
6 | import Radio from "../radio";
7 |
8 | Enzyme.configure({ adapter: new Adapter() });
9 |
10 | describe("radio specs", () => {
11 | const sandbox = sinon.createSandbox();
12 |
13 | afterEach(() => {
14 | sandbox.verifyAndRestore();
15 | });
16 |
17 | it("should render radio component", () => {
18 | const wrapper = shallow();
19 |
20 | expect(wrapper).toHaveLength(1);
21 | });
22 |
23 | it("should call onChange function ", () => {
24 | const spy = sandbox.spy();
25 | const text = faker.lorem.word();
26 | const wrapper = shallow();
27 | const event = { target: { value: text } };
28 |
29 | wrapper.find("input").simulate("change", event);
30 |
31 | expect(spy.calledWithExactly(event));
32 | });
33 |
34 | it("should not call callback function when changed and input is disabled", () => {
35 | const spy = sandbox.spy();
36 | const text = faker.lorem.word();
37 | const wrapper = shallow();
38 | const event = { target: { value: text } };
39 |
40 | wrapper.simulate("change", event);
41 |
42 | expect(spy.calledOnce).toBe(false);
43 | });
44 |
45 | it("should render a label", () => {
46 | const text = faker.lorem.word();
47 | const wrapper = shallow();
48 |
49 | expect(wrapper.find("label")).toHaveLength(1);
50 | });
51 |
52 | it("should accept additional classNames", () => {
53 | const testClass = faker.lorem.word();
54 | const wrapper = shallow();
55 | expect(wrapper.find(`.${testClass}`)).toHaveLength(1);
56 | });
57 |
58 | it("should convert text type prop to radio type prop", () => {
59 | const wrapper = shallow();
60 | const inputElement = wrapper.find("input");
61 | expect(inputElement.prop("type")).toBe("radio");
62 | });
63 |
64 | it("should have error className when error prop is passed", () => {
65 | const wrapper = shallow();
66 |
67 | expect(wrapper.hasClass("q-error")).toBe(true);
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/src/stories/list.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/react";
2 | import React from "react";
3 | import List from "../components/list";
4 |
5 | const stories = storiesOf("List", module);
6 |
7 | stories.add("Default", () => (
8 |
9 |
10 | 15 gün içerisinde ücretsiz iade. Detaylı bilgi için.Tıklayın.
11 |
12 |
13 | 24 saatte kargoda fırsatı iş günlerinde geçerlidir.
14 |
15 |
16 | Modelin Ölçüleri: Boy: 1.75, Göğüs: 80, Bel: 60, Kalça: 88
17 |
18 |
19 | Mankenin üzerindeki ürün S/36 bedendir.
20 |
21 |
22 | ));
23 |
24 | stories.add("No Dot", () => (
25 |
26 |
27 | 15 gün içerisinde ücretsiz iade. Detaylı bilgi için.Tıklayın.
28 |
29 |
30 | 24 saatte kargoda fırsatı iş günlerinde geçerlidir.
31 |
32 |
33 | Modelin Ölçüleri: Boy: 1.75, Göğüs: 80, Bel: 60, Kalça: 88
34 |
35 |
36 | Mankenin üzerindeki ürün S/36 bedendir.
37 |
38 |
39 | ));
40 |
41 | stories.add("Icon", () => (
42 |
43 |
44 |
45 | 15 gün içerisinde ücretsiz iade. Detaylı bilgi için
46 |
47 |
48 | 24 saatte kargoda fırsatı iş günlerinde geçerlidir.
49 |
50 |
51 | Modelin Ölçüleri: Boy: 1.75, Göğüs: 80, Bel: 60, Kalça: 88
52 |
53 |
54 | Mankenin üzerindeki ürün S/36 bedendir.
55 |
56 |
57 |
58 |
59 |
60 |
61 | 15 gün içerisinde ücretsiz iade. Detaylı bilgi için
62 |
63 |
64 | 24 saatte kargoda fırsatı iş günlerinde geçerlidir.
65 |
66 |
67 | Modelin Ölçüleri: Boy: 1.75, Göğüs: 80, Bel: 60, Kalça: 88
68 |
69 |
70 | Mankenin üzerindeki ürün S/36 bedendir.
71 |
72 |
73 |
74 | ));
75 |
--------------------------------------------------------------------------------
/src/stories/modal.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/react";
2 | import * as faker from "faker";
3 | import React, { PureComponent } from "react";
4 | import Button from "../components/button";
5 | import Modal from "../components/modal";
6 | import { animationTypes } from "../types/modal";
7 |
8 | const stories = storiesOf("Modal", module);
9 |
10 | interface IProps {
11 | animation?: animationTypes;
12 | rightIcon?: string;
13 | leftIcon?: string;
14 | }
15 |
16 | interface IState {
17 | show: boolean;
18 | }
19 |
20 | class ModalWrapper extends PureComponent {
21 | public constructor(props: IProps) {
22 | super(props);
23 | this.state = {show: false};
24 | this.handleChange = this.handleChange.bind(this);
25 | this.closeModal = this.closeModal.bind(this);
26 | }
27 |
28 | public render() {
29 | return (
30 | <>
31 |
32 |
33 |
39 | Cok y cok g cok cok cok cok cok cok cok cok cok cok cok cok cok cok
40 | cok uzun title
41 |
42 |
43 | {faker.lorem.words(1000)}
44 |
45 |
46 |
49 |
50 |
51 | >
52 | );
53 | }
54 |
55 | private handleChange() {
56 | this.setState({show: !this.state.show});
57 | }
58 |
59 | private closeModal() {
60 | this.setState({show: false});
61 | }
62 | }
63 |
64 | stories.add("Default", () => );
65 | stories.add("Left Icon", () => (
66 |
67 | ));
68 | stories.add("Right Icon", () => (
69 |
70 | ));
71 | stories.add("SlideInLeft", () => );
72 | stories.add("SlideInDown", () => );
73 | stories.add("SlideInUp", () => );
74 |
--------------------------------------------------------------------------------
/src/components/rating/test/rating.spec.tsx:
--------------------------------------------------------------------------------
1 | import Enzyme, { mount } from "enzyme";
2 | import Adapter from "@cfaester/enzyme-adapter-react-18";
3 | import { faker } from '@faker-js/faker';
4 | import React from "react";
5 | import sinon from "sinon";
6 | import Rating from "../rating";
7 |
8 | Enzyme.configure({ adapter: new Adapter() });
9 |
10 | describe("radio specs", () => {
11 | const sandbox = sinon.createSandbox();
12 |
13 | afterEach(() => {
14 | sandbox.verifyAndRestore();
15 | });
16 |
17 | it("should render rating component with 5 stars", () => {
18 | const wrapper = mount();
19 | expect(wrapper.find(".q-icon")).toHaveLength(10); // 5 empty, 5 colorfull
20 | });
21 |
22 | it("should render rating component with x stars", () => {
23 | const stars = faker.number.int({
24 | max: 10,
25 | min: 1,
26 | });
27 | const wrapper = mount();
28 | expect(wrapper.find(".q-icon")).toHaveLength(stars * 2);
29 | });
30 |
31 | it("should have half amount of stars filled", () => {
32 | const wrapper = mount();
33 | expect(wrapper.find(".q-rating-full").props().style).toHaveProperty("width", "50%");
34 | });
35 |
36 | it("should have full amount of stars filled", () => {
37 | const wrapper = mount();
38 | expect(wrapper.find(".q-rating-full").props().style).toHaveProperty("width", "100%");
39 | });
40 |
41 | it("should have full amount of stars filled in x stars", () => {
42 | const stars = faker.number.int({
43 | max: 10,
44 | min: 1,
45 | });
46 | const wrapper = mount();
47 | expect(wrapper.find(".q-rating-full").props().style).toHaveProperty("width", "100%");
48 | });
49 |
50 | it("should receive proper size prop", () => {
51 | const wrapper = mount();
52 | expect(wrapper.find(".q-rating-large")).toHaveLength(1);
53 | });
54 |
55 | it("should receive proper color prop", () => {
56 | const wrapper = mount();
57 | expect(wrapper.find(".q-icon.red")).toHaveLength(5);
58 | });
59 |
60 | it("should receive proper margin prop", () => {
61 | const margin = faker.number.int({
62 | max: 50,
63 | min: 1,
64 | });
65 | const wrapper = mount();
66 | expect(wrapper.find(".q-icon").first().props().style).toHaveProperty("marginRight", `${margin}px`);
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/src/components/link/test/link.spec.tsx:
--------------------------------------------------------------------------------
1 | import Enzyme, { mount, shallow } from "enzyme";
2 | import Adapter from "@cfaester/enzyme-adapter-react-18";
3 | import { faker } from '@faker-js/faker';
4 | import React from "react";
5 | import sinon from "sinon";
6 | import Link from "../link";
7 |
8 | Enzyme.configure({ adapter: new Adapter() });
9 |
10 | describe("link specs", () => {
11 | const sandbox = sinon.createSandbox();
12 |
13 | afterEach(() => {
14 | sandbox.verifyAndRestore();
15 | });
16 |
17 | it("should render link component", () => {
18 | const text = faker.lorem.word();
19 | const url = faker.lorem.word();
20 | const wrapper = shallow({text});
21 |
22 | expect(wrapper.find("a")).toHaveLength(1);
23 | });
24 |
25 | it("should have given href attribute", () => {
26 | const text = faker.lorem.word();
27 | const url = faker.lorem.word();
28 | const wrapper = shallow({text});
29 |
30 | expect(wrapper.find("a").prop("href")).toEqual(url);
31 | });
32 |
33 | it("should be rendered with given primary variant prop", () => {
34 | const url = faker.lorem.word();
35 | const wrapper = shallow();
36 |
37 | expect(wrapper.exists(".q-primary")).toEqual(true);
38 | });
39 |
40 | it("should have className fluid when given fluid prop", () => {
41 | const url = faker.lorem.word();
42 | const wrapper = shallow();
43 |
44 | expect(wrapper.exists(".q-fluid")).toEqual(true);
45 | });
46 |
47 | it("should have className round when given round prop", () => {
48 | const url = faker.lorem.word();
49 | const wrapper = shallow();
50 |
51 | expect(wrapper.exists(".q-round")).toEqual(true);
52 | });
53 |
54 | it("should have icon component when given icon prop", () => {
55 | const url = faker.lorem.word();
56 | const wrapper = mount();
57 | expect(wrapper.exists(".icon-heart")).toBe(true);
58 | });
59 |
60 | it("should have className circular when given circular prop", () => {
61 | const url = faker.lorem.word();
62 | const wrapper = shallow();
63 |
64 | expect(wrapper.exists(".q-circular")).toBe(true);
65 | });
66 |
67 | it("should have className disabled when given disabled prop", () => {
68 | const url = faker.lorem.word();
69 | const wrapper = shallow();
70 |
71 | expect(wrapper.exists(".q-anchor-disabled")).toBe(true);
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/src/components/box/test/box.spec.tsx:
--------------------------------------------------------------------------------
1 | import Enzyme, { shallow } from "enzyme";
2 | import Adapter from "@cfaester/enzyme-adapter-react-18";
3 | import { faker } from '@faker-js/faker';
4 | import React from "react";
5 | import sinon from "sinon";
6 |
7 | import {Box, BoxGroup} from "../index";
8 |
9 | Enzyme.configure({ adapter: new Adapter() });
10 |
11 | describe("Box specs", () => {
12 | const sandbox = sinon.createSandbox();
13 |
14 | afterEach(() => {
15 | sandbox.verifyAndRestore();
16 | });
17 |
18 | it("should render box component", () => {
19 | const text = faker.lorem.word();
20 | const wrapper = shallow({text});
21 |
22 | expect(wrapper.text()).toEqual(text);
23 | });
24 |
25 | it("should have spaced className when spaced prop is given", () => {
26 | const wrapper = shallow();
27 |
28 | expect(wrapper.hasClass("q-spaced")).toBe(true);
29 | });
30 |
31 | it("should have fitted className when fitted prop is given", () => {
32 | const wrapper = shallow();
33 |
34 | expect(wrapper.hasClass("q-fitted")).toBe(true);
35 | });
36 |
37 | it("should have left-aligned className by default", () => {
38 | const wrapper = shallow();
39 |
40 | expect(wrapper.hasClass("q-left-aligned")).toBe(true);
41 | });
42 |
43 | it("should have right-aligned className when textAlign prop is equal to right", () => {
44 | const wrapper = shallow();
45 |
46 | expect(wrapper.hasClass("q-right-aligned")).toBe(true);
47 | });
48 |
49 | it("should have right-aligned className when textAlign prop is equal to center", () => {
50 | const wrapper = shallow();
51 |
52 | expect(wrapper.hasClass("q-center-aligned")).toBe(true);
53 | });
54 |
55 | it("should have justify-aligned className when textAlign prop is equal to justify", () => {
56 | const wrapper = shallow();
57 |
58 | expect(wrapper.hasClass("q-justify-aligned")).toBe(true);
59 | });
60 |
61 | it("should have box-group className", () => {
62 | const wrapper = shallow();
63 |
64 | expect(wrapper.hasClass("q-box-group")).toBe(true);
65 | });
66 |
67 | it("should accept additional classNames", () => {
68 | const fakeClass = faker.lorem.word();
69 | const wrapperBox = shallow();
70 | const wrapperBoxGroup = shallow();
71 |
72 | expect(wrapperBox.hasClass(fakeClass)).toBe(true);
73 | expect(wrapperBoxGroup.hasClass(fakeClass)).toBe(true);
74 | });
75 | });
76 |
--------------------------------------------------------------------------------
/src/components/checkbox/test/checkbox.spec.tsx:
--------------------------------------------------------------------------------
1 | import Enzyme, { shallow } from "enzyme";
2 | import Adapter from "@cfaester/enzyme-adapter-react-18";
3 | import { faker } from '@faker-js/faker';
4 | import React from "react";
5 | import sinon from "sinon";
6 | import CheckBox from "../checkbox";
7 |
8 | Enzyme.configure({ adapter: new Adapter() });
9 |
10 | describe("checkbox specs", () => {
11 | const sandbox = sinon.createSandbox();
12 |
13 | afterEach(() => {
14 | sandbox.verifyAndRestore();
15 | });
16 |
17 | it("should render checkbox component", () => {
18 | const wrapper = shallow();
19 |
20 | expect(wrapper).toHaveLength(1);
21 | });
22 |
23 | it("should call onChange function ", () => {
24 | const spy = sandbox.spy();
25 | const text = faker.lorem.word();
26 | const wrapper = shallow();
27 | const event = { target: { value: text } };
28 |
29 | wrapper.find("input").simulate("change", event);
30 |
31 | expect(spy.calledOnce).toBe(true);
32 | });
33 |
34 | it("should not call callback function when changed and input is disabled", () => {
35 | const spy = sandbox.spy();
36 | const text = faker.lorem.word();
37 | const wrapper = shallow();
38 | const event = { target: { value: text } };
39 |
40 | wrapper.simulate("change", event);
41 |
42 | expect(spy.calledOnce).toBe(false);
43 | });
44 |
45 | it("should render a label", () => {
46 | const text = faker.lorem.word();
47 | const wrapper = shallow();
48 |
49 | expect(wrapper.find("label")).toHaveLength(1);
50 | });
51 |
52 | it("should accept additional classNames", () => {
53 | const testClass = faker.lorem.word();
54 | const wrapper = shallow();
55 | expect(wrapper.find(`.${testClass}`)).toHaveLength(1);
56 | });
57 |
58 | it("should convert text type prop to checkbox type prop", () => {
59 | const wrapper = shallow();
60 | const inputElement = wrapper.find("input");
61 | expect(inputElement.prop("type")).toBe("checkbox");
62 | });
63 |
64 | it("should have error className when error prop is passed", () => {
65 | const wrapper = shallow();
66 |
67 | expect(wrapper.hasClass("q-error")).toBe(true);
68 | });
69 |
70 | it("should accept additional classNames", () => {
71 | const fakeClass = faker.lorem.word();
72 | const wrapper = shallow();
73 |
74 | expect(wrapper.hasClass(fakeClass)).toBe(true);
75 | });
76 | });
77 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "quarkify",
3 | "version": "5.0.1",
4 | "license": "MIT",
5 | "description": "An awesome lightweight React UI Component library",
6 | "repository": "https://github.com/Trendyol/quarkify",
7 | "resolutions": {
8 | "@types/react": "18.3.2"
9 | },
10 | "scripts": {
11 | "start": "start-storybook -p 9009 -s public",
12 | "build": "npm run rm:dist && npm run build:tsc && cp -r ./src/styles .",
13 | "build:tsc": "tsc -p tsconfig.tsc.json --outDir .",
14 | "build:tsc:dev": "tsc -p tsconfig.tsc.json --outDir ./dist",
15 | "build:dev": "npm run rm:dist && npm run build:tsc:dev && cp -r ./src/styles ./dist",
16 | "test": "vitest run",
17 | "test:watch": "vitest",
18 | "test:ci": "vitest run",
19 | "eject": "react-scripts eject",
20 | "coverage": "vitest run --coverage",
21 | "coverage:ci": "CI=true vitest run --coverage",
22 | "lint": "tslint -c tslint.json 'src/**/*.tsx' --fix",
23 | "rm:dist": "rm -rf ./dist"
24 | },
25 | "dependencies": {
26 | "classnames": "^2.5.1",
27 | "react": "18.3.1",
28 | "react-dom": "18.3.1",
29 | "react-scripts": "^5.0.1",
30 | "react-transition-group": "^4.4.5",
31 | "sass": "^1.77.1"
32 | },
33 | "devDependencies": {
34 | "@cfaester/enzyme-adapter-react-18": "^0.8.0",
35 | "@faker-js/faker": "^8.4.1",
36 | "@types/classnames": "^2.3.0",
37 | "@types/enzyme": "^3.10.18",
38 | "@types/node": "20.12.12",
39 | "@types/react": "18.3.2",
40 | "@types/react-dom": "18.3.0",
41 | "@types/react-transition-group": "^4.4.10",
42 | "@types/sinon": "^17.0.3",
43 | "@vitest/coverage-v8": "^1.6.0",
44 | "css-loader": "^7.1.1",
45 | "enzyme": "^3.11.0",
46 | "husky": "^9.0.11",
47 | "lint-staged": "^15.2.2",
48 | "sinon": "^18.0.0",
49 | "source-map-loader": "^5.0.0",
50 | "ts-loader": "^9.5.1",
51 | "tslint": "^5.20.1",
52 | "tslint-react": "^4.1.0",
53 | "typescript": "5.4.5",
54 | "vite-tsconfig-paths": "^4.3.2",
55 | "vitest": "^1.6.0"
56 | },
57 | "overrides": {
58 | "react-scripts": {
59 | "typescript": "^5"
60 | }
61 | },
62 | "husky": {
63 | "hooks": {
64 | "pre-commit": "lint-staged && npm run coverage:ci"
65 | }
66 | },
67 | "eslintConfig": {
68 | "extends": "react-app"
69 | },
70 | "browserslist": {
71 | "production": [
72 | ">0.2%",
73 | "not dead",
74 | "not op_mini all"
75 | ],
76 | "development": [
77 | "last 1 chrome version",
78 | "last 1 firefox version",
79 | "last 1 safari version"
80 | ]
81 | },
82 | "lint-staged": {
83 | "*.{ts,tsx}": [
84 | "npm run lint",
85 | "git add"
86 | ]
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/components/ripple/test/ripple.spec.tsx:
--------------------------------------------------------------------------------
1 | import Enzyme, { shallow } from "enzyme";
2 | import Adapter from "@cfaester/enzyme-adapter-react-18";
3 | import { faker } from '@faker-js/faker';
4 | import React from "react";
5 | import sinon from "sinon";
6 | import Ripple from "../ripple";
7 |
8 | Enzyme.configure({ adapter: new Adapter() });
9 |
10 | const createMockDiv = (width: number, height: number) => {
11 | const div = document.createElement("div");
12 | Object.assign(div.style, {
13 | height: height + "px",
14 | width: width + "px",
15 | });
16 |
17 | div.getBoundingClientRect = () => ({
18 | bottom: height,
19 | height,
20 | left: 0,
21 | right: width,
22 | top: 0,
23 | width,
24 | });
25 | return div;
26 | };
27 |
28 | describe("ripple specs", () => {
29 | const sandbox = sinon.createSandbox();
30 | vi.useFakeTimers();
31 |
32 | afterEach(() => {
33 | sandbox.verifyAndRestore();
34 | });
35 |
36 | it("should wrap the children", () => {
37 | const wrapper = shallow();
38 |
39 | expect(wrapper.find(".q-ripple")).toHaveLength(1);
40 | });
41 |
42 | it("should return the wrapped component as is if active prop is false", () => {
43 | const wrapper = shallow();
44 |
45 | expect(wrapper.find(".q-ripple")).toHaveLength(0);
46 | });
47 |
48 | it("should accept additional classNames", () => {
49 | const fakeClass = faker.lorem.word();
50 | const wrapper = shallow();
51 |
52 | expect(wrapper.hasClass(fakeClass)).toBe(true);
53 | });
54 |
55 | it("should extend onClick", () => {
56 | const stub = sandbox.stub();
57 | const event = {
58 | currentTarget: createMockDiv(faker.number.int(), faker.number.int()),
59 | stopPropagation: sandbox.stub(),
60 | };
61 | const wrapper = shallow();
62 | wrapper.find(".q-ripple").simulate("click", event);
63 |
64 | expect(stub.calledOnce).toBe(true);
65 | });
66 |
67 | it("should not propagate the event", () => {
68 | const event = {
69 | currentTarget: createMockDiv(faker.number.int(), faker.number.int()),
70 | stopPropagation: sandbox.stub(),
71 | };
72 | const wrapper = shallow();
73 | wrapper.find(".q-ripple").simulate("click", event);
74 |
75 | expect(event.stopPropagation.calledOnce).toBe(true);
76 | });
77 |
78 | it("should disappear after animation ends", () => {
79 | const event = {
80 | currentTarget: createMockDiv(faker.number.int(), faker.number.int()),
81 | stopPropagation: sandbox.stub(),
82 | };
83 | const wrapper = shallow();
84 |
85 | wrapper.find(".q-ripple").simulate("click", event);
86 | vi.runAllTimers();
87 | const rippleStyle: {opacity: number} = wrapper.state("rippleStyle");
88 | expect(rippleStyle.opacity).toBe(0);
89 | });
90 |
91 | });
92 |
--------------------------------------------------------------------------------
/src/stories/quantity-selector.stories.tsx:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/react";
2 | import React from "react";
3 | import QuantitySelector from "../components/quantity-selector";
4 | import "./styles/select.css";
5 |
6 | const stories = storiesOf("QuantitySelector", module);
7 |
8 | function onIncrement() {
9 | console.log("onIncrement");
10 | }
11 |
12 | function onDecrement() {
13 | console.log("onIncrement");
14 | }
15 |
16 | const wrapperClasses = { display: "flex", padding: "5px" };
17 |
18 | stories.add("When count is 0", () => (
19 |
20 |
25 |
26 | ));
27 |
28 | stories.add("When count is 1", () => (
29 |
30 |
35 |
36 | ));
37 |
38 | stories.add("When count higher then 1", () => (
39 |
40 |
45 |
46 | ));
47 |
48 | stories.add("When no count specified", () => (
49 |
50 |
54 |
55 | ));
56 |
57 | stories.add("Fluid", () => (
58 |
59 |
65 |
66 | ));
67 |
68 | stories.add("Sizes", () => (
69 |
70 |
large
71 |
72 |
77 |
78 |
medium
79 |
80 |
85 |
86 |
small
87 |
88 |
93 |
94 |
xsmall
95 |
96 |
101 |
102 | ));
103 |
--------------------------------------------------------------------------------
/documents/components/icon.md:
--------------------------------------------------------------------------------
1 | # Icons
2 | Icon component uses icomoon library to render icon elements. Of course it is possible to overwrite the icons by giving appropriate class names to the Icon component.
3 |
4 | ## Name
5 | Icon component takes name prop as required to specify which icon will be rendered.
6 |
7 | ```html
8 |
9 |
10 | ```
11 |
12 |
13 |
14 | Here are the list of possible names:
15 |
16 | - search
17 | - spinner
18 | - shopping-cart
19 | - like
20 | - account
21 |
22 | ...
23 |
24 | ## Size
25 | An icon can have the following sizes:
26 | - xlarge
27 | - large
28 | - small
29 |
30 | By default, the size of the Icon component is set to normal.
31 |
32 | ```html
33 |
34 |
35 |
36 |
37 | ```
38 |
39 | ## Circular
40 | An icon can have a circular background
41 |
42 | ```html
43 |
44 | ```
45 |
46 |
47 |
48 |
49 | ## Variant
50 | It is possible to use most used icon color schemas which are:
51 |
52 | - primary (orange theme)
53 | - gray
54 |
55 | ```html
56 |
57 |
58 | ```
59 |
60 | ## Color
61 |
62 | Icon has different color variants.
63 |
64 | * "primary"
65 | * "green"
66 | * "red"
67 | * "black"
68 | * "dark-gray"
69 | * "light-gray"
70 | * "border-gray"
71 |
72 | ```html
73 |
74 | ```
75 |
76 | #### Additional content
77 | You can use some other props as well such as:
78 |
79 | ```html
80 |
81 |
82 | ```
83 |
84 |
85 |
--------------------------------------------------------------------------------
/src/components/icon/test/icon.spec.tsx:
--------------------------------------------------------------------------------
1 | import Enzyme, { shallow } from "enzyme";
2 | import Adapter from "@cfaester/enzyme-adapter-react-18";
3 | import { faker } from '@faker-js/faker';
4 | import React from "react";
5 | import sinon from "sinon";
6 | import Icon from "../icon";
7 |
8 | Enzyme.configure({ adapter: new Adapter() });
9 |
10 | describe("icon specs", () => {
11 | const sandbox = sinon.createSandbox();
12 |
13 | afterEach(() => {
14 | sandbox.verifyAndRestore();
15 | });
16 |
17 | it("should render icon component", () => {
18 | const text = faker.lorem.word();
19 | const wrapper = shallow();
20 | expect(wrapper.find(".icon-" + text)).toHaveLength(1);
21 | });
22 |
23 | it("should be rendered with given large size prop", () => {
24 | const wrapper = shallow();
25 |
26 | expect(wrapper.exists(".q-icon-large")).toEqual(true);
27 | });
28 |
29 | it("should have q-circular className when circular prop is passed", () => {
30 | const wrapper = shallow();
31 |
32 | expect(wrapper.exists(".q-circular")).toEqual(true);
33 | });
34 |
35 | it("should call function when close icon is clicked", () => {
36 | const spy = sandbox.spy();
37 | const wrapper = shallow();
38 | wrapper.find(".icon-search").simulate("click");
39 | expect(spy.calledOnce).toEqual(true);
40 | });
41 |
42 | it("should have the given color prop as the className", () => {
43 | const fakeIconName = faker.lorem.word();
44 |
45 | const wrapper = shallow();
46 |
47 | expect(wrapper.exists(".red")).toBe(true);
48 | });
49 |
50 | it("should accept additional classNames", () => {
51 | const fakeClass = faker.lorem.word();
52 | const wrapper = shallow();
53 |
54 | expect(wrapper.hasClass(fakeClass)).toBe(true);
55 | });
56 |
57 | it("should create correct amount of strokes", () => {
58 | const fakeClass = faker.lorem.word();
59 | const randomStrokes = faker.number.int({min: 1, max: 10});
60 | const wrapper = shallow();
61 |
62 | expect(wrapper.find(`.${fakeClass} > span`)).toHaveLength(randomStrokes);
63 | });
64 |
65 | it("should not create strokes when the value is not defined", () => {
66 | const fakeClass = faker.lorem.word();
67 | const wrapper = shallow();
68 |
69 | expect(wrapper.find(`.${fakeClass} > span`)).toHaveLength(0);
70 | });
71 |
72 | it("should not create strokes when the value is 0", () => {
73 | const fakeClass = faker.lorem.word();
74 | const wrapper = shallow();
75 |
76 | expect(wrapper.find(`.${fakeClass} > span`)).toHaveLength(0);
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/src/components/typography/test/typography.spec.tsx:
--------------------------------------------------------------------------------
1 | import Enzyme, { shallow } from "enzyme";
2 | import Adapter from "@cfaester/enzyme-adapter-react-18";
3 | import { faker } from '@faker-js/faker';
4 | import React from "react";
5 | import sinon from "sinon";
6 | import Typography from "../index";
7 |
8 | Enzyme.configure({ adapter: new Adapter() });
9 |
10 | describe("typography specs", () => {
11 |
12 | const sandbox = sinon.createSandbox();
13 |
14 | afterEach(() => {
15 | sandbox.verifyAndRestore();
16 | });
17 |
18 | it("should render product slider correctly", () => {
19 | const wrapper = shallow();
20 | expect(wrapper.find(".q-typography")).toHaveLength(1);
21 | });
22 |
23 | it("should be of type h1 if given h1 variant", () => {
24 | const wrapper = shallow();
25 | expect(wrapper.type()).toBe("h1");
26 | });
27 |
28 | it("should be of type h2 if given h2 variant", () => {
29 | const wrapper = shallow();
30 | expect(wrapper.type()).toBe("h2");
31 | });
32 |
33 | it("should be of type h3 if given h3 variant", () => {
34 | const wrapper = shallow();
35 | expect(wrapper.type()).toBe("h3");
36 | });
37 |
38 | it("should be of type p if given paragraph variant", () => {
39 | const wrapper = shallow();
40 | expect(wrapper.type()).toBe("p");
41 | });
42 |
43 | it("should be of type p if given smallParagraph variant", () => {
44 | const wrapper = shallow();
45 | expect(wrapper.type()).toBe("p");
46 | });
47 |
48 | it("should be of type p if given subtitle variant", () => {
49 | const wrapper = shallow();
50 | expect(wrapper.type()).toBe("p");
51 | });
52 |
53 | it("should have className underline when given underline prop", () => {
54 | const wrapper = shallow();
55 | expect(wrapper.exists(".underline")).toEqual(true);
56 | });
57 |
58 | it("should have className bold when given bold prop", () => {
59 | const wrapper = shallow();
60 | expect(wrapper.exists(".bold")).toEqual(true);
61 | });
62 |
63 | it("should have className ellipsis when given noWrap prop", () => {
64 | const wrapper = shallow();
65 | expect(wrapper.exists(".ellipsis")).toEqual(true);
66 | });
67 |
68 | it("should have the given color prop as the className", () => {
69 | const wrapper = shallow();
70 | expect(wrapper.exists(".red")).toBe(true);
71 | });
72 |
73 | it("should accept additional classNames", () => {
74 | const fakeClass = faker.lorem.word();
75 | const wrapper = shallow();
76 |
77 | expect(wrapper.hasClass(fakeClass)).toBe(true);
78 | });
79 |
80 | });
81 |
--------------------------------------------------------------------------------
/documents/components/select.md:
--------------------------------------------------------------------------------
1 | # Select
2 | Here are the some examples of usage of the select component:
3 |
4 | ```
5 | const items = [
6 | {
7 | name: "Plane",
8 | value: 3,
9 | variant: "secondary",
10 | }
11 | ]
12 |