├── documents ├── .nojekyll ├── _navbar.md ├── _media │ └── logo.png ├── images │ ├── logo.png │ ├── button.png │ ├── home1.png │ ├── home2.png │ ├── home3.png │ ├── logo3.png │ └── modal.png ├── _coverpage.md ├── components │ ├── layout.md │ ├── loader.md │ ├── product-slider.md │ ├── subheader.md │ ├── switch.md │ ├── quantity-selector.md │ ├── box.md │ ├── radio.md │ ├── accordion.md │ ├── list.md │ ├── typography.md │ ├── step-progress-bar.md │ ├── checkbox.md │ ├── icon.md │ ├── select.md │ ├── modal.md │ └── input.md ├── _sidebar.md ├── index.html ├── introduction.md └── 404.html ├── .prettierignore ├── src ├── react-app-env.d.ts ├── types │ ├── popup.ts │ ├── rating.ts │ ├── select.ts │ ├── link.ts │ ├── box.ts │ ├── quantity-selector.ts │ ├── icon.ts │ ├── switch.ts │ ├── button.ts │ ├── modal.ts │ ├── typography.ts │ ├── color.ts │ └── accordion.ts ├── styles │ ├── mixins │ │ ├── _fluid.scss │ │ ├── _flex.scss │ │ ├── _text-truncate.scss │ │ ├── _breakpoints.scss │ │ └── _color-scheme.scss │ ├── _icon-url.scss │ ├── _font.scss │ ├── components │ │ ├── _ripple.scss │ │ ├── _link.scss │ │ ├── _product-slider.scss │ │ ├── _layout.scss │ │ ├── _scroll-to-top.scss │ │ ├── _badge.scss │ │ ├── _list.scss │ │ ├── _box.scss │ │ ├── _bottom-sheet.scss │ │ ├── _icon.scss │ │ ├── _rating.scss │ │ ├── _quantity-selector.scss │ │ ├── _typography.scss │ │ ├── _accordion.scss │ │ ├── _input.scss │ │ ├── _subheader.scss │ │ ├── _step-progress-bar.scss │ │ ├── _switch.scss │ │ ├── _modal.scss │ │ ├── _popup.scss │ │ ├── _select.scss │ │ ├── _button.scss │ │ ├── _radio.scss │ │ └── _checkbox.scss │ ├── _globals.scss │ ├── animations │ │ ├── _accordion.scss │ │ ├── _popup.scss │ │ ├── _bottom-sheet.scss │ │ └── _modal.scss │ ├── _colors.scss │ ├── _all.scss │ └── _variables.scss ├── stories │ ├── styles │ │ ├── scroll-to-top.scss │ │ ├── bottom-sheet.css │ │ ├── accordion.css │ │ └── select.css │ ├── rating.stories.tsx │ ├── badge.stories.tsx │ ├── layout.stories.tsx │ ├── typography.stories.tsx │ ├── ripple.stories.tsx │ ├── product-slider.stories.tsx │ ├── icon.stories.tsx │ ├── scroll-to-top.stories.tsx │ ├── box.stories.tsx │ ├── link.stories.tsx │ ├── countdown.stories.tsx │ ├── step-progress-bar.stories.tsx │ ├── switch.stories.tsx │ ├── subheader.stories.tsx │ ├── bottom-sheet.stories.tsx │ ├── loader.stories.tsx │ ├── select.stories.tsx │ ├── list.stories.tsx │ ├── modal.stories.tsx │ └── quantity-selector.stories.tsx ├── components │ ├── badge │ │ ├── index.ts │ │ ├── badge.tsx │ │ └── test │ │ │ └── badge.spec.tsx │ ├── icon │ │ ├── index.ts │ │ ├── icon.tsx │ │ └── test │ │ │ └── icon.spec.tsx │ ├── input │ │ ├── index.ts │ │ └── input.tsx │ ├── link │ │ ├── index.ts │ │ ├── link.tsx │ │ └── test │ │ │ └── link.spec.tsx │ ├── list │ │ ├── index.ts │ │ ├── list.tsx │ │ ├── listItem.tsx │ │ └── test │ │ │ └── list.spec.tsx │ ├── modal │ │ ├── index.ts │ │ ├── modal-actions.tsx │ │ ├── modal-content.tsx │ │ ├── modal-header.tsx │ │ └── modal.tsx │ ├── popup │ │ └── index.ts │ ├── radio │ │ ├── index.ts │ │ ├── radio.tsx │ │ └── test │ │ │ └── radio.spec.tsx │ ├── button │ │ ├── index.ts │ │ ├── button.tsx │ │ └── test │ │ │ └── button.spec.tsx │ ├── layout │ │ ├── index.ts │ │ ├── layout.tsx │ │ └── test │ │ │ └── layout.spec.tsx │ ├── loader │ │ ├── index.ts │ │ └── loader.tsx │ ├── rating │ │ ├── index.ts │ │ ├── rating.tsx │ │ └── test │ │ │ └── rating.spec.tsx │ ├── ripple │ │ ├── index.ts │ │ └── test │ │ │ └── ripple.spec.tsx │ ├── select │ │ ├── index.ts │ │ └── select.tsx │ ├── switch │ │ ├── index.ts │ │ ├── switch.tsx │ │ └── test │ │ │ └── switch.spec.tsx │ ├── checkbox │ │ ├── index.ts │ │ ├── checkbox.tsx │ │ └── test │ │ │ └── checkbox.spec.tsx │ ├── countdown │ │ └── index.ts │ ├── subheader │ │ ├── index.ts │ │ └── subheader.tsx │ ├── typography │ │ ├── index.ts │ │ ├── typography.tsx │ │ └── test │ │ │ └── typography.spec.tsx │ ├── bottom-sheet │ │ └── index.ts │ ├── scroll-to-top │ │ └── index.ts │ ├── product-slider │ │ ├── index.ts │ │ ├── product-slider.tsx │ │ └── test │ │ │ └── product-slider.spec.tsx │ ├── quantity-selector │ │ ├── index.ts │ │ └── quantity-selector.tsx │ ├── box │ │ ├── index.ts │ │ ├── box-group.tsx │ │ ├── box.tsx │ │ └── test │ │ │ └── box.spec.tsx │ ├── accordion │ │ ├── index.ts │ │ ├── accordion-group.tsx │ │ ├── accordion-content.tsx │ │ ├── accordion-header.tsx │ │ └── accordion.tsx │ └── step-progress-bar │ │ ├── index.ts │ │ ├── step.tsx │ │ ├── step-progress-bar.tsx │ │ └── test │ │ └── step-progress-bar.spec.tsx ├── enums │ └── countdown.ts ├── interfaces │ ├── div.tsx │ ├── icon.tsx │ ├── list-item.tsx │ ├── list.tsx │ ├── link.tsx │ ├── button.tsx │ ├── input.tsx │ ├── select.tsx │ └── typography.tsx └── utils │ ├── class-names-default.ts │ └── detectPassiveEvents.ts ├── .storybook ├── addons.js └── config.js ├── .prettierrc ├── images ├── logo.png ├── button.png ├── home1.png ├── home2.png ├── home3.png └── modal.png ├── .npmignore ├── tslint.json ├── .gitignore ├── tsconfig.json ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── tsconfig.tsc.json ├── vitest.config.mts ├── LICENSE ├── .circleci └── config.yml └── package.json /documents/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | tsconfig.json 3 | -------------------------------------------------------------------------------- /documents/_navbar.md: -------------------------------------------------------------------------------- 1 | - Translations 2 | - [:uk: English](/) 3 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/types/popup.ts: -------------------------------------------------------------------------------- 1 | export type IconTypes = "close" | "back"; 2 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import "@storybook/addon-viewport/register"; 2 | -------------------------------------------------------------------------------- /src/styles/mixins/_fluid.scss: -------------------------------------------------------------------------------- 1 | @mixin fluid() { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /src/types/rating.ts: -------------------------------------------------------------------------------- 1 | export type ratingSize = "large" | "medium" | "small"; 2 | -------------------------------------------------------------------------------- /src/types/select.ts: -------------------------------------------------------------------------------- 1 | export type variantTypes = "primary" | "secondary"; 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": false, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/images/logo.png -------------------------------------------------------------------------------- /src/stories/styles/scroll-to-top.scss: -------------------------------------------------------------------------------- 1 | .basket-scroll { 2 | bottom: 70px; 3 | } -------------------------------------------------------------------------------- /src/types/link.ts: -------------------------------------------------------------------------------- 1 | export type variantTypes = "primary" | "secondary" | "gray"; 2 | -------------------------------------------------------------------------------- /images/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/images/button.png -------------------------------------------------------------------------------- /images/home1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/images/home1.png -------------------------------------------------------------------------------- /images/home2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/images/home2.png -------------------------------------------------------------------------------- /images/home3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/images/home3.png -------------------------------------------------------------------------------- /images/modal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/images/modal.png -------------------------------------------------------------------------------- /src/components/badge/index.ts: -------------------------------------------------------------------------------- 1 | import Badge from "./badge"; 2 | 3 | export default Badge; 4 | -------------------------------------------------------------------------------- /src/components/icon/index.ts: -------------------------------------------------------------------------------- 1 | import Icon from "./icon"; 2 | 3 | export default Icon; 4 | -------------------------------------------------------------------------------- /src/components/input/index.ts: -------------------------------------------------------------------------------- 1 | import Input from "./input"; 2 | 3 | export default Input; 4 | -------------------------------------------------------------------------------- /src/components/link/index.ts: -------------------------------------------------------------------------------- 1 | import Link from "./link"; 2 | 3 | export default Link; 4 | -------------------------------------------------------------------------------- /src/components/list/index.ts: -------------------------------------------------------------------------------- 1 | import List from "./list"; 2 | 3 | export default List; 4 | -------------------------------------------------------------------------------- /src/components/modal/index.ts: -------------------------------------------------------------------------------- 1 | import Modal from "./modal"; 2 | 3 | export default Modal; 4 | -------------------------------------------------------------------------------- /src/components/popup/index.ts: -------------------------------------------------------------------------------- 1 | import Popup from "./popup"; 2 | 3 | export default Popup; 4 | -------------------------------------------------------------------------------- /src/components/radio/index.ts: -------------------------------------------------------------------------------- 1 | import Radio from "./radio"; 2 | 3 | export default Radio; 4 | -------------------------------------------------------------------------------- /src/stories/styles/bottom-sheet.css: -------------------------------------------------------------------------------- 1 | .bottom-sheet{ 2 | background-color: red; 3 | } 4 | -------------------------------------------------------------------------------- /src/types/box.ts: -------------------------------------------------------------------------------- 1 | export type textAlignTypes = "left" | "right" | "center" | "justify"; 2 | -------------------------------------------------------------------------------- /src/components/button/index.ts: -------------------------------------------------------------------------------- 1 | import Button from "./button"; 2 | 3 | export default Button; 4 | -------------------------------------------------------------------------------- /src/components/layout/index.ts: -------------------------------------------------------------------------------- 1 | import Layout from "./layout"; 2 | 3 | export default Layout; 4 | -------------------------------------------------------------------------------- /src/components/loader/index.ts: -------------------------------------------------------------------------------- 1 | import Loader from "./loader"; 2 | 3 | export default Loader; 4 | -------------------------------------------------------------------------------- /src/components/rating/index.ts: -------------------------------------------------------------------------------- 1 | import Rating from "./rating"; 2 | 3 | export default Rating; 4 | -------------------------------------------------------------------------------- /src/components/ripple/index.ts: -------------------------------------------------------------------------------- 1 | import Ripple from "./ripple"; 2 | 3 | export default Ripple; 4 | -------------------------------------------------------------------------------- /src/components/select/index.ts: -------------------------------------------------------------------------------- 1 | import Select from "./select"; 2 | 3 | export default Select; 4 | -------------------------------------------------------------------------------- /src/components/switch/index.ts: -------------------------------------------------------------------------------- 1 | import Switch from "./switch"; 2 | 3 | export default Switch; 4 | -------------------------------------------------------------------------------- /src/types/quantity-selector.ts: -------------------------------------------------------------------------------- 1 | export type qsSize = "large" | "medium" | "small" | "xsmall"; 2 | -------------------------------------------------------------------------------- /documents/_media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/documents/_media/logo.png -------------------------------------------------------------------------------- /documents/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/documents/images/logo.png -------------------------------------------------------------------------------- /src/styles/_icon-url.scss: -------------------------------------------------------------------------------- 1 | @import url("https://i.icomoon.io/public/8a084d4b90/TyIcons/style.css"); 2 | -------------------------------------------------------------------------------- /documents/images/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/documents/images/button.png -------------------------------------------------------------------------------- /documents/images/home1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/documents/images/home1.png -------------------------------------------------------------------------------- /documents/images/home2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/documents/images/home2.png -------------------------------------------------------------------------------- /documents/images/home3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/documents/images/home3.png -------------------------------------------------------------------------------- /documents/images/logo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/documents/images/logo3.png -------------------------------------------------------------------------------- /documents/images/modal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trendyol/quarkify/HEAD/documents/images/modal.png -------------------------------------------------------------------------------- /src/components/checkbox/index.ts: -------------------------------------------------------------------------------- 1 | import CheckBox from "./checkbox"; 2 | 3 | export default CheckBox; 4 | -------------------------------------------------------------------------------- /src/components/countdown/index.ts: -------------------------------------------------------------------------------- 1 | import Countdown from "./countdown"; 2 | 3 | export default Countdown; 4 | -------------------------------------------------------------------------------- /src/components/subheader/index.ts: -------------------------------------------------------------------------------- 1 | import SubHeader from "./subheader"; 2 | 3 | export default SubHeader; 4 | -------------------------------------------------------------------------------- /src/components/typography/index.ts: -------------------------------------------------------------------------------- 1 | import Typography from "./typography"; 2 | 3 | export default Typography; 4 | -------------------------------------------------------------------------------- /src/styles/_font.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro&display=swap'); 2 | -------------------------------------------------------------------------------- /src/components/bottom-sheet/index.ts: -------------------------------------------------------------------------------- 1 | import BottomSheet from "./bottom-sheet"; 2 | 3 | export default BottomSheet; 4 | -------------------------------------------------------------------------------- /src/components/scroll-to-top/index.ts: -------------------------------------------------------------------------------- 1 | import ScrollToTop from "./scroll-to-top"; 2 | 3 | export default ScrollToTop; 4 | -------------------------------------------------------------------------------- /src/components/product-slider/index.ts: -------------------------------------------------------------------------------- 1 | import ProductSlider from "./product-slider"; 2 | 3 | export default ProductSlider; 4 | -------------------------------------------------------------------------------- /src/styles/mixins/_flex.scss: -------------------------------------------------------------------------------- 1 | @mixin flex-center { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | } 6 | -------------------------------------------------------------------------------- /src/types/icon.ts: -------------------------------------------------------------------------------- 1 | export type sizeTypes = "large" | "medium" | "small" | "xlarge"; 2 | export type variantTypes = "primary" | "gray"; 3 | -------------------------------------------------------------------------------- /src/types/switch.ts: -------------------------------------------------------------------------------- 1 | export type variantTypes = "primary" | "secondary" | "gray"; 2 | export type switchTypes = "radio" | "checkbox"; 3 | -------------------------------------------------------------------------------- /src/components/quantity-selector/index.ts: -------------------------------------------------------------------------------- 1 | import QuantitySelector from "./quantity-selector"; 2 | 3 | export default QuantitySelector; 4 | -------------------------------------------------------------------------------- /src/styles/components/_ripple.scss: -------------------------------------------------------------------------------- 1 | @import '../mixins/_fluid.scss'; 2 | 3 | .q-ripple{ 4 | &.q-fluid{ 5 | @include fluid; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/components/box/index.ts: -------------------------------------------------------------------------------- 1 | import Box from "./box"; 2 | import BoxGroup from "./box-group"; 3 | 4 | export { 5 | Box, 6 | BoxGroup, 7 | }; 8 | -------------------------------------------------------------------------------- /src/styles/mixins/_text-truncate.scss: -------------------------------------------------------------------------------- 1 | @mixin text-truncate { 2 | text-overflow: ellipsis; 3 | white-space: nowrap; 4 | overflow: hidden; 5 | } 6 | -------------------------------------------------------------------------------- /src/stories/styles/accordion.css: -------------------------------------------------------------------------------- 1 | .accordion-content { 2 | background-color: red; 3 | } 4 | 5 | .accordion-header { 6 | background-color: white; 7 | } 8 | -------------------------------------------------------------------------------- /src/types/button.ts: -------------------------------------------------------------------------------- 1 | export type variantTypes = "primary" | "secondary" | "gray"; 2 | export type buttonSize = "large" | "medium" | "small" | "xsmall"; 3 | -------------------------------------------------------------------------------- /src/types/modal.ts: -------------------------------------------------------------------------------- 1 | export type animationTypes = 2 | | "noAnimation" 3 | | "slideInLeft" 4 | | "slideInRight" 5 | | "slideInDown" 6 | | "slideInUp"; 7 | -------------------------------------------------------------------------------- /src/enums/countdown.ts: -------------------------------------------------------------------------------- 1 | export const INTERVAL_HANDLER_TIME = 1000; 2 | export const DATE_STRINGS = { 3 | DAY: "gün", 4 | HOUR: "saat", 5 | MINUTE: "dakika", 6 | }; 7 | -------------------------------------------------------------------------------- /src/types/typography.ts: -------------------------------------------------------------------------------- 1 | export type variantTypes = "h1" | "h2" | "h3" | 2 | "paragraph" | "smallParagraph" | "body" | "subtitle"; 3 | export type displayTypes = "block" | "inline"; 4 | -------------------------------------------------------------------------------- /src/components/accordion/index.ts: -------------------------------------------------------------------------------- 1 | import Accordion from "./accordion"; 2 | import AccordionGroup from "./accordion-group"; 3 | 4 | export { 5 | Accordion, 6 | AccordionGroup, 7 | }; 8 | -------------------------------------------------------------------------------- /src/components/step-progress-bar/index.ts: -------------------------------------------------------------------------------- 1 | import Step from "./step"; 2 | import StepProgressBar from "./step-progress-bar"; 3 | 4 | export { 5 | Step, 6 | StepProgressBar, 7 | }; 8 | -------------------------------------------------------------------------------- /src/interfaces/div.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export default interface IDiv 4 | extends React.DetailedHTMLProps, HTMLDivElement> { 5 | } 6 | -------------------------------------------------------------------------------- /src/interfaces/icon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export default interface IIcon 4 | extends React.DetailedHTMLProps, 5 | HTMLElement> { 6 | } 7 | -------------------------------------------------------------------------------- /src/interfaces/list-item.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export default interface IListItem extends React.DetailedHTMLProps, 4 | HTMLLIElement> {} 5 | -------------------------------------------------------------------------------- /src/interfaces/list.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export default interface IList extends React.DetailedHTMLProps 4 | , HTMLUListElement> {} 5 | -------------------------------------------------------------------------------- /src/interfaces/link.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export default interface ILink 4 | extends React.DetailedHTMLProps, HTMLAnchorElement> { 5 | } 6 | -------------------------------------------------------------------------------- /src/interfaces/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export default interface IButton 4 | extends React.DetailedHTMLProps, HTMLButtonElement> { 5 | } 6 | -------------------------------------------------------------------------------- /src/interfaces/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export default interface IInput 4 | extends React.DetailedHTMLProps, 5 | HTMLInputElement> { 6 | } 7 | -------------------------------------------------------------------------------- /src/interfaces/select.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export default interface ISelect 4 | extends React.DetailedHTMLProps, 5 | HTMLSelectElement> { 6 | } 7 | -------------------------------------------------------------------------------- /src/stories/styles/select.css: -------------------------------------------------------------------------------- 1 | .flex { 2 | display: flex; 3 | width: 100%; 4 | } 5 | 6 | .select-w40 { 7 | width: 40%; 8 | margin-right: 10px; 9 | } 10 | 11 | .button-w60 { 12 | width: 60%; 13 | } 14 | -------------------------------------------------------------------------------- /src/types/color.ts: -------------------------------------------------------------------------------- 1 | export type colorTypes = 2 | "primary" | 3 | "green" | 4 | "red" | 5 | "black" | 6 | "dark-gray" | 7 | "light-gray" | 8 | "border-gray" | 9 | "white" | 10 | "yellow" | 11 | "zero"; 12 | -------------------------------------------------------------------------------- /src/types/accordion.ts: -------------------------------------------------------------------------------- 1 | export type IconTypes = "close" | "back-button"; 2 | export type IconPosition = "left" | "right"; 3 | export type animationTypes = 4 | | "slideInLeft" 5 | | "slideInRight" 6 | | "slideInDown" 7 | | "slideInUp"; 8 | -------------------------------------------------------------------------------- /src/styles/components/_link.scss: -------------------------------------------------------------------------------- 1 | @import "button"; 2 | 3 | .q-anchor { 4 | @extend .q-button; 5 | display: inline-grid; 6 | align-items: center; 7 | text-decoration: none; 8 | 9 | &.q-anchor-disabled { 10 | pointer-events: none; 11 | } 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.spec.* 3 | .nyc_output/ 4 | coverage/ 5 | .idea/ 6 | .idea_modules/ 7 | .storybook/ 8 | src/stories/ 9 | docs/ 10 | documents/ 11 | .prettierrc 12 | CHANGELOG.md 13 | README.md 14 | images/ 15 | tslint.json 16 | dist/stories 17 | public/ 18 | src/ 19 | -------------------------------------------------------------------------------- /src/styles/components/_product-slider.scss: -------------------------------------------------------------------------------- 1 | @import "../variables"; 2 | 3 | .q-product-slider { 4 | display: flex; 5 | overflow-x: auto; 6 | overflow-y: hidden; 7 | -webkit-overflow-scrolling: touch; 8 | 9 | .q-item + .q-item { 10 | margin-left: 10px; 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/interfaces/typography.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export default interface ITypography 4 | extends React.DetailedHTMLProps, HTMLParagraphElement>, 5 | React.DetailedHTMLProps, HTMLHeadingElement> { 6 | } 7 | -------------------------------------------------------------------------------- /src/styles/_globals.scss: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | font-family: "Source Sans Pro", sans-serif; 4 | } 5 | 6 | *::before, 7 | *::after { 8 | box-sizing: border-box; 9 | } 10 | 11 | html { 12 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 13 | } 14 | 15 | body, p{ 16 | margin: 0; 17 | } 18 | -------------------------------------------------------------------------------- /documents/_coverpage.md: -------------------------------------------------------------------------------- 1 | ![logo](_media/logo.png) 2 | 3 | # quarkify 0.1.0 4 | 5 | > An awesome lightweight React UI Component library. 6 | 7 | - Simple and lightweight 8 | - No statically built html files 9 | - Dynamic structure 10 | - Easy usage 11 | 12 | [GitHub](https://github.com/Trendyol/quarkify/) 13 | [Getting Started](#quarkify) 14 | -------------------------------------------------------------------------------- /src/components/modal/modal-actions.tsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | 3 | export default class Actions extends PureComponent { 4 | public render() { 5 | const {children} = this.props; 6 | return
{children}
; 7 | } 8 | } 9 | 10 | interface IActionsProps { 11 | children?: React.ReactNode; 12 | } 13 | -------------------------------------------------------------------------------- /documents/components/layout.md: -------------------------------------------------------------------------------- 1 | # Layout 2 | 3 | Layout is a wrapper component used to display elements in a set style. 4 | 5 | ```html 6 | 7 | {children} 8 | 9 | ``` 10 | 11 | ## Fluid 12 | 13 | The fluid prop adds a padding to the layout component. 14 | 15 | ```html 16 | 17 | 18 |

random text

19 |
20 |
21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /src/styles/components/_layout.scss: -------------------------------------------------------------------------------- 1 | @import '../_globals'; 2 | @import '../_variables'; 3 | 4 | .q-layout { 5 | background-color: $background-gray; 6 | display: block; 7 | padding: $space-medium; 8 | margin: 0; 9 | border: 0; 10 | font-family: $font-family; 11 | outline: none; 12 | user-select: none; 13 | 14 | &.q-fitted { 15 | padding: 0; 16 | } 17 | &.q-fullscreen { 18 | height: 100%; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from "@storybook/react"; 2 | import '../src/styles/_globals.scss'; 3 | import '../src/styles/_icon-url.scss'; 4 | import '../src/styles/_font.scss'; 5 | import '../src/styles/_all.scss'; 6 | 7 | 8 | const req = require.context("../src/stories", true, /.stories.tsx$/); 9 | 10 | function loadStories() { 11 | req.keys().forEach((filename) => req(filename)); 12 | } 13 | 14 | configure(loadStories, module); 15 | -------------------------------------------------------------------------------- /src/components/modal/modal-content.tsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | 3 | export default class Content extends PureComponent { 4 | public render() { 5 | const {children, className = ""} = this.props; 6 | return
{children}
; 7 | } 8 | } 9 | 10 | interface IModalContentProps { 11 | className?: string; 12 | children?: React.ReactNode; 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/class-names-default.ts: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | 3 | const classNamesDefault = (props: any) => { 4 | return classNames( 5 | props.className, 6 | props.variant && `q-${props.variant}`, 7 | props.disabled && "q-disabled", 8 | props.size && `q-${props.size}`, 9 | props.fluid && "q-fluid", 10 | props.error && "q-error", 11 | props.round && "q-round", 12 | ); 13 | }; 14 | 15 | export default classNamesDefault; 16 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:latest", "tslint-react"], 3 | "rules": { 4 | "jsx-wrap-multiline": false, 5 | "quotemark": [true, "double", "jsx-double"], 6 | "jsx-boolean-value": false, 7 | "no-implicit-dependencies": [true, "dev"], 8 | "jsx-no-multiline-js": false, 9 | "no-console": ["error", { "allow": ["warn"] }] 10 | }, 11 | "linterOptions": { 12 | "exclude": [ 13 | "src/*.spec.tsx" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /documents/components/loader.md: -------------------------------------------------------------------------------- 1 | # Loader 2 | A loader is a component to display loading status. It can be used when 3 | the application needs to indicate to the user that there are running 4 | requests and the user should wait. This component's position is 5 | absolute and the parent wrapper component must have a relative position. 6 | 7 | ## Active 8 | 9 | A loader can be active or disabled. 10 | 11 | ``` 12 | //Active 13 | //Disabled 14 | ``` 15 | -------------------------------------------------------------------------------- /src/styles/mixins/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | @import "../variables"; 2 | 3 | @function breakpoint-min($name, $breakpoints: $grid-breakpoints) { 4 | $min: map-get($breakpoints, $name); 5 | @return if($min != 0, $min, null); 6 | } 7 | 8 | @mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) { 9 | $min: breakpoint-min($name, $breakpoints); 10 | @if $min { 11 | @media (min-width: $min) { 12 | @content; 13 | } 14 | } @else { 15 | @content; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/stories/rating.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from "@storybook/react"; 2 | import React from "react"; 3 | import Rating from "../components/rating"; 4 | 5 | const stories = storiesOf("Rating", module); 6 | 7 | stories.add("Default", () => ( 8 |
9 | 10 | 7
11 |
12 |
13 |
14 | )); 15 | -------------------------------------------------------------------------------- /src/styles/components/_scroll-to-top.scss: -------------------------------------------------------------------------------- 1 | @import "../mixins/flex"; 2 | 3 | .q-scroll-to-top { 4 | @include flex-center; 5 | height: 46px; 6 | width: 46px; 7 | border-radius: 50%; 8 | background: rgba(49, 49, 49, 0.7); 9 | font-size: 1.5rem; 10 | position: fixed; 11 | bottom: 20px; 12 | right: 20px; 13 | text-align: center; 14 | 15 | &.hide { 16 | display: none; 17 | } 18 | 19 | .q-icon { 20 | vertical-align: bottom; 21 | font-size: 22px; 22 | width: 26px; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/detectPassiveEvents.ts: -------------------------------------------------------------------------------- 1 | export const detectPassiveEvents = () => { 2 | let supportsPassive = false; 3 | try { 4 | const opts = Object.defineProperty({}, "passive", { 5 | get() { 6 | supportsPassive = true; 7 | }, 8 | }); 9 | // @ts-ignore 10 | window.addEventListener("testPassive", null, opts); 11 | // @ts-ignore 12 | window.removeEventListener("testPassive", null, opts); 13 | // tslint:disable-next-line:no-empty 14 | } catch (e) {} 15 | return supportsPassive; 16 | }; 17 | -------------------------------------------------------------------------------- /documents/components/product-slider.md: -------------------------------------------------------------------------------- 1 | # Product Slider 2 | 3 | Product Slider is a wrapper component in which it makes the children 4 | component it wraps slide horizontally. 5 | 6 | ```html 7 |
8 |

Similar Products

9 | 10 | {products.map((product, key) => ( 11 |
12 | 13 |

{product.brand}

14 |
15 | ))} 16 |
17 |

{faker.lorem.lines(10)}

18 |
19 | ``` 20 | -------------------------------------------------------------------------------- /src/stories/badge.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from "@storybook/react"; 2 | import React from "react"; 3 | import Badge from "../components/badge"; 4 | 5 | const stories = storiesOf("Badge", module); 6 | 7 | stories.add("Badge", () => ( 8 | <> 9 |
10 | Sepette %30 indirim 11 | 12 | Kargo Bedava 13 |
14 | 15 | )); 16 | -------------------------------------------------------------------------------- /documents/components/subheader.md: -------------------------------------------------------------------------------- 1 | # SubHeader 2 | 3 | SubHeader is a wrapper component with a set style. 4 | 5 | ```html 6 | 7 | ``` 8 | 9 | ## Icon 10 | 11 | SubHeader can have a left or right oriented icon. 12 | 13 | ```html 14 | 15 | 16 | ``` 17 | 18 | ## Action 19 | SubHeader icons can have a callback function. 20 | 21 | ```html 22 | 23 | ``` 24 | -------------------------------------------------------------------------------- /src/styles/animations/_accordion.scss: -------------------------------------------------------------------------------- 1 | @mixin accordion-animation($duration: 300ms) { 2 | .q-slideInDown { 3 | &.q-accordion-enter { 4 | max-height: 0; 5 | } 6 | 7 | &.q-accordion-enter-active { 8 | max-height: 100vh; 9 | transition: max-height $duration, transform $duration; 10 | } 11 | 12 | &.q-accordion-exit { 13 | max-height: 100vh; 14 | } 15 | 16 | &.q-accordion-exit-active { 17 | max-height: 0; 18 | transition: max-height $duration; 19 | } 20 | 21 | &.q-accordion-exit-done { 22 | } 23 | } 24 | } 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/stories/layout.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 Layout from "../components/layout"; 6 | 7 | const stories = storiesOf("Layout", module); 8 | 9 | stories.add("Default", () => ( 10 | 11 | 12 |

{faker.lorem.lines(10)}

13 |
14 |
15 | )); 16 | 17 | stories.add("Fluid", () => ( 18 | 19 | 20 |

{faker.lorem.lines(10)}

21 |
22 |
23 | )); 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | #storybook 9 | /docs 10 | 11 | # testing 12 | /coverage 13 | 14 | # production 15 | /build 16 | /dist 17 | /components 18 | /interfaces 19 | /stories 20 | /styles 21 | /types 22 | /utils 23 | /enums 24 | /assets 25 | 26 | # misc 27 | .DS_Store 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | .idea/ 33 | 34 | npm-debug.log* 35 | yarn-debug.log* 36 | yarn-error.log* 37 | 38 | # firebase 39 | .firebaserc 40 | .firebase 41 | firebase.json 42 | -------------------------------------------------------------------------------- /src/styles/animations/_popup.scss: -------------------------------------------------------------------------------- 1 | @mixin popup-animation($duration: 150ms) { 2 | .q-zoomIn { 3 | 4 | &.q-popup-enter { 5 | .q-popup-main { 6 | transform: scale(0); 7 | } 8 | } 9 | 10 | &.q-popup-enter-active { 11 | .q-popup-main { 12 | transform: scale(1); 13 | transition: transform $duration; 14 | } 15 | } 16 | 17 | &.q-popup-exit { 18 | .q-popup-main { 19 | transform: scale(1); 20 | transition: transform $duration; 21 | } 22 | } 23 | 24 | &.q-popup-exit-active { 25 | .q-popup-main { 26 | transform: scale(0); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "preserve", 21 | "types": [ 22 | "node", 23 | "vitest/globals" 24 | ] 25 | }, 26 | "include": [ 27 | "src" 28 | ] 29 | } -------------------------------------------------------------------------------- /src/components/step-progress-bar/step.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | 4 | export default class Step extends PureComponent { 5 | 6 | public render() { 7 | const { 8 | name, 9 | active = false, 10 | className, 11 | ...props } = this.props; 12 | 13 | const stepClasses = classNames( 14 | "q-spb-step", 15 | active && "active", 16 | className, 17 | ); 18 | 19 | return ( 20 |
  • {name}
  • 21 | ); 22 | } 23 | } 24 | 25 | interface IProps { 26 | name: string; 27 | active?: boolean; 28 | className?: string; 29 | } 30 | -------------------------------------------------------------------------------- /src/styles/mixins/_color-scheme.scss: -------------------------------------------------------------------------------- 1 | @import '../variables'; 2 | 3 | @mixin primary { 4 | background-color: $primary; 5 | color: $white; 6 | border: 1px solid $primary; 7 | i:before { 8 | color: $white; 9 | } 10 | } 11 | 12 | @mixin secondary { 13 | background-color: $white; 14 | color: $primary; 15 | border: 1px solid $primary; 16 | } 17 | 18 | @mixin gray { 19 | background-color: $white; 20 | color: $dark-gray; 21 | border: 1px solid $light-gray; 22 | } 23 | 24 | 25 | @mixin disabled { 26 | background-color: $disabled !important; 27 | opacity: 0.4 !important; 28 | color: $white !important; 29 | cursor: not-allowed !important; 30 | border: 0 !important; 31 | } 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/styles/components/_badge.scss: -------------------------------------------------------------------------------- 1 | @import "../_variables"; 2 | @import "../mixins/text-truncate"; 3 | 4 | .q-badge { 5 | font-size: 12px; 6 | line-height: 16px; 7 | display: flex; 8 | align-items: center; 9 | justify-content: center; 10 | border-radius: 3px; 11 | padding-left: 5px; 12 | padding-right: 5px; 13 | height: 20px; 14 | margin-right: 5px; 15 | min-width: 28px; 16 | @each $color, $value in $colors { 17 | &.#{$color} { 18 | color: $value; 19 | border: 1px solid $value; 20 | } 21 | } 22 | 23 | .q-icon { 24 | padding-left: 0; 25 | padding-right: $space-small; 26 | } 27 | 28 | .q-icon:only-child { 29 | padding-right: 0; 30 | } 31 | div { 32 | @include text-truncate(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/styles/components/_list.scss: -------------------------------------------------------------------------------- 1 | @import "../variables"; 2 | 3 | .q-list { 4 | 5 | list-style: none; 6 | padding: $list-padding; 7 | 8 | .q-list-item { 9 | word-wrap: break-word; 10 | position: relative; 11 | padding-left: $padding-x; 12 | margin-bottom: 0.625rem; 13 | line-height: $item-line-height; 14 | color: $dark-gray; 15 | 16 | .q-icon{ 17 | margin-right: 5px; 18 | } 19 | } 20 | 21 | .q-list-item::before { 22 | content: ""; 23 | position: absolute; 24 | left: 0; 25 | top: 7px; 26 | background-color: $primary; 27 | width: 5px; 28 | height: 5px; 29 | border-radius: 100%; 30 | } 31 | 32 | &.q-no-dot { 33 | .q-list-item::before { 34 | display: none; 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/components/accordion/accordion-group.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | import IDiv from "../../interfaces/div"; 4 | 5 | export default class AccordionGroup extends PureComponent { 6 | public render() { 7 | const { 8 | children, 9 | className, 10 | ...props 11 | } = this.props; 12 | const accordionGroupClasses = classNames( 13 | "q-accordion-group", 14 | className, 15 | ); 16 | return ( 17 |
    18 | {children} 19 |
    20 | ); 21 | } 22 | } 23 | 24 | // tslint:disable-next-line:no-empty-interface 25 | interface IAccordionGroupProps extends IDiv { 26 | children?: React.ReactNode; 27 | } 28 | -------------------------------------------------------------------------------- /src/components/box/box-group.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | import IDiv from "../../interfaces/div"; 4 | 5 | export default class BoxGroup extends PureComponent { 6 | public render() { 7 | const { 8 | children, 9 | className, 10 | ...props 11 | } = this.props; 12 | const boxGroupClasses = classNames( 13 | "q-box-group", 14 | className, 15 | ); 16 | return ( 17 |
    18 | {children} 19 |
    20 | ); 21 | } 22 | } 23 | 24 | // tslint:disable-next-line:no-empty-interface 25 | interface IBoxGroupProps extends IDiv { 26 | children?: React.ReactNode; 27 | } 28 | -------------------------------------------------------------------------------- /src/components/product-slider/product-slider.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | import IDiv from "../../interfaces/div"; 4 | 5 | export default class ProductSlider extends PureComponent { 6 | public render() { 7 | const { children, className, ...props } = this.props; 8 | const productSliderClasses = classNames( 9 | "q-product-slider", 10 | className, 11 | ); 12 | return ( 13 |
    14 | {React.Children.map(children, (child) => ( 15 |
    16 | {child} 17 |
    18 | ))} 19 |
    20 | ); 21 | } 22 | } 23 | 24 | interface IProductSliderProps extends IDiv { 25 | className?: string; 26 | } 27 | -------------------------------------------------------------------------------- /src/styles/components/_box.scss: -------------------------------------------------------------------------------- 1 | @import "../_variables"; 2 | 3 | .q-box { 4 | background-color: $white; 5 | border: 1px solid $border-gray; 6 | padding: $space-medium; 7 | position: relative; 8 | 9 | &.q-spaced { 10 | margin: $box-margin; 11 | } 12 | 13 | &.q-fitted { 14 | padding: 0; 15 | } 16 | 17 | &.q-left-aligned { 18 | text-align: left; 19 | } 20 | 21 | &.q-right-aligned { 22 | text-align: right; 23 | } 24 | 25 | &.q-center-aligned { 26 | text-align: center; 27 | } 28 | 29 | &.q-justify-aligned { 30 | text-align: justify; 31 | } 32 | } 33 | 34 | .q-box-group{ 35 | border: 1px solid $border-gray; 36 | 37 | .q-box{ 38 | border:0; 39 | border-radius: 0; 40 | } 41 | 42 | .q-box:not(:last-child){ 43 | border-bottom: 1px solid $border-gray; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tsconfig.tsc.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es5", 5 | "module": "commonjs", 6 | "lib": ["dom", "es6", "es5", "es2017"], 7 | "declaration": true, 8 | "outDir": "./dist", 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "experimentalDecorators": true, 12 | "emitDecoratorMetadata": true, 13 | "listEmittedFiles": true, 14 | "jsx": "react", 15 | "types": [], 16 | "typeRoots": ["src/types/**"], 17 | "sourceMap": true, 18 | "noImplicitAny": true 19 | }, 20 | "exclude": [ 21 | "*/**/node_modules", 22 | "*/**/test", 23 | "./src/stories/*", 24 | "!node_modules/@types", 25 | "!node_modules/@jest" 26 | ], 27 | "include": ["./src"], 28 | "emitDecoratorMetadata": true, 29 | "experimentalDecorators": true 30 | } 31 | -------------------------------------------------------------------------------- /vitest.config.mts: -------------------------------------------------------------------------------- 1 | import tsconfigPaths from 'vite-tsconfig-paths'; 2 | import { defineConfig, coverageConfigDefaults, configDefaults } from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | test: { 6 | globals: true, 7 | environment: "jsdom", 8 | exclude:[ 9 | ...configDefaults.exclude, 10 | 'src/stories/**', 11 | 'src/**/index.ts', 12 | 'src/**/{interfaces,enums}/**', 13 | ], 14 | coverage: { 15 | thresholds: { 16 | statements: 97.30, 17 | branches: 95.12, 18 | functions: 93.93, 19 | lines: 97.30, 20 | }, 21 | exclude: [ 22 | ...coverageConfigDefaults.exclude, 23 | 'src/stories/**', 24 | 'src/**/index.ts', 25 | 'src/**/{interfaces,enums}/**', 26 | ] 27 | } 28 | }, 29 | plugins: [tsconfigPaths()], 30 | }); -------------------------------------------------------------------------------- /documents/_sidebar.md: -------------------------------------------------------------------------------- 1 | - Getting started 2 | 3 | - [Introduction](introduction.md) 4 | 5 | - Components 6 | 7 | - [Accordion](components/accordion.md) 8 | - [Box](components/box.md) 9 | - [Button](components/button.md) 10 | - [Checkbox](components/checkbox.md) 11 | - [Icon](components/icon.md) 12 | - [Input](components/input.md) 13 | - [Layout](components/layout.md) 14 | - [List](components/list.md) 15 | - [Loader](components/loader.md) 16 | - [Modal](components/modal.md) 17 | - [Popup](components/popup.md) 18 | - [Product Slider](components/product-slider.md) 19 | - [Radio](components/radio.md) 20 | - [Select](components/select.md) 21 | - [SubHeader](components/subheader.md) 22 | - [StepProgressBar](components/step-progress-bar.md) 23 | - [Switch](components/switch.md) 24 | - [Typography](components/typography.md) 25 | 26 | -------------------------------------------------------------------------------- /src/components/list/list.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | import IList from "../../interfaces/list"; 4 | import ListItem from "./listItem"; 5 | 6 | export default class List extends PureComponent { 7 | public static Item = ListItem; 8 | 9 | public render() { 10 | const { 11 | noDot = false, 12 | children, 13 | className, 14 | ...props 15 | } = this.props; 16 | const listClasses = classNames( 17 | "q-list", 18 | noDot && "q-no-dot", 19 | className, 20 | ); 21 | return ( 22 |
      23 | {children} 24 |
    25 | ); 26 | } 27 | } 28 | 29 | interface IListProps extends IList { 30 | noDot?: boolean; 31 | className?: string; 32 | children?: React.ReactNode; 33 | } 34 | -------------------------------------------------------------------------------- /documents/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | quarkify - An awesome lightweight React UI Component library 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    13 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/components/layout/layout.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | import IDiv from "../../interfaces/div"; 4 | 5 | export default class Layout extends PureComponent { 6 | 7 | public render() { 8 | const { 9 | fitted, 10 | fullScreen, 11 | children, 12 | className, 13 | ...props 14 | } = this.props; 15 | const layoutClasses = classNames( 16 | "q-layout", 17 | fitted && "q-fitted", 18 | fullScreen && "q-fullscreen", 19 | className); 20 | return ( 21 |
    25 | {children} 26 |
    27 | ); 28 | } 29 | } 30 | 31 | interface ILayoutProps extends IDiv { 32 | fitted?: boolean; 33 | fullScreen?: boolean; 34 | children?: React.ReactNode; 35 | } 36 | -------------------------------------------------------------------------------- /src/components/step-progress-bar/step-progress-bar.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | import IList from "../../interfaces/list"; 4 | import { colorTypes } from "../../types/color"; 5 | 6 | export default class StepProgressBar extends PureComponent { 7 | 8 | public render() { 9 | const { 10 | color = "green", 11 | children, 12 | className, 13 | ...props } = this.props; 14 | 15 | const stepProgressBarClasses = classNames( 16 | "q-spb", 17 | color && `${color}`, 18 | className, 19 | ); 20 | 21 | return ( 22 |
      23 | {children} 24 |
    25 | ); 26 | } 27 | } 28 | 29 | interface IProps extends IList { 30 | color?: colorTypes; 31 | children?: React.ReactNode; 32 | className?: string; 33 | } 34 | -------------------------------------------------------------------------------- /src/stories/typography.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from "@storybook/react"; 2 | import React from "react"; 3 | import Typography from "../components/typography"; 4 | 5 | const stories = storiesOf("Typography", module); 6 | 7 | stories.add("Default", () => ( 8 |
    9 | 10 | h1 Hello there 11 | 12 | 13 | h2 Hello there color green 14 | 15 | 16 | h3 Hello there 17 | 18 | 19 | paragraph Hello there 20 | 21 | 22 | small-paragraph Hello there 23 | 24 | 25 | small-paragraph White color 26 | 27 |
    28 | 29 | )); 30 | -------------------------------------------------------------------------------- /src/components/list/listItem.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | import IListItem from "../../interfaces/list-item"; 4 | import { colorTypes } from "../../types/color"; 5 | import Icon from "../icon"; 6 | 7 | export default class ListItem extends PureComponent { 8 | public render() { 9 | const { 10 | icon, 11 | iconColor, 12 | children, 13 | className, 14 | ...props 15 | } = this.props; 16 | const listItemClasses = classNames( 17 | "q-list-item", 18 | className, 19 | ); 20 | return ( 21 |
  • 22 | {icon && } 23 | {children} 24 |
  • 25 | ); 26 | } 27 | } 28 | 29 | interface IItemProps extends IListItem { 30 | icon?: string; 31 | iconColor?: colorTypes; 32 | className?: string; 33 | children?: React.ReactNode; 34 | } 35 | -------------------------------------------------------------------------------- /src/styles/components/_bottom-sheet.scss: -------------------------------------------------------------------------------- 1 | @import '../variables'; 2 | @import "../animations/_bottom-sheet.scss"; 3 | 4 | .q-bottom-sheet-main { 5 | position: fixed; 6 | background-color: $white; 7 | display: block; 8 | bottom: 0; 9 | text-align: center; 10 | width: 100%; 11 | border-radius: $border-radius; 12 | z-index: 1022; 13 | overflow-y: scroll; 14 | max-height: 100vh; 15 | -ms-overflow-style: none; 16 | -webkit-overflow-scrolling: touch; 17 | .q-bottom-sheet-content { 18 | overflow-y: scroll; 19 | 20 | max-height: 100vh; 21 | } 22 | &::-webkit-scrollbar { 23 | width: 0; 24 | height: 0; 25 | } 26 | } 27 | 28 | //Animations 29 | @include bottom-sheet-animation; 30 | 31 | .q-bottom-sheet-overlay { 32 | height: 100%; 33 | width: 100%; 34 | position: fixed; 35 | top: 0; 36 | left: 0; 37 | z-index: 1021; 38 | display: flex; 39 | justify-content: center; 40 | align-items: center; 41 | } 42 | 43 | body.q-disable-scroll { 44 | overflow: hidden; 45 | } -------------------------------------------------------------------------------- /src/stories/ripple.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from "@storybook/react"; 2 | import React from "react"; 3 | import { Box } from "../components/box"; 4 | import Button from "../components/button"; 5 | import Layout from "../components/layout"; 6 | import Ripple from "../components/ripple"; 7 | 8 | const stories = storiesOf("Ripple", module); 9 | 10 | class TestComponent extends React.PureComponent { 11 | public render() { 12 | return ( 13 | 14 |

    1

    15 | 16 |

    1

    17 |
    18 | ); 19 | } 20 | } 21 | 22 | stories.add("Image", () => { 23 | return ( 24 | 25 | 26 | 27 | 28 | 29 | ); 30 | }); 31 | 32 | stories.add("Button", () => { 33 | return ( 34 | 35 | 36 | 37 | ); 38 | }); 39 | -------------------------------------------------------------------------------- /src/styles/animations/_bottom-sheet.scss: -------------------------------------------------------------------------------- 1 | @mixin bottom-sheet-animation($duration: 200ms) { 2 | .q-slideInDown { 3 | .q-bottom-sheet-main { 4 | transition: transform $duration ease-out; 5 | } 6 | &.q-bottom-sheet-overlay { 7 | transition: background-color $duration linear; 8 | } 9 | 10 | &.q-bottom-sheet-enter-done { 11 | background-color: rgba(0, 0, 0, .5); 12 | } 13 | &.q-bottom-sheet-enter { 14 | .q-bottom-sheet-main { 15 | transform: translateY(100vh); 16 | } 17 | } 18 | 19 | &.q-bottom-sheet-exit { 20 | .q-bottom-sheet-main { 21 | transform: translateY(0); 22 | } 23 | } 24 | &.q-bottom-sheet-exit-active { 25 | .q-bottom-sheet-main { 26 | transform: translateY(100%); 27 | transition: transform $duration; 28 | transition-timing-function: ease-out; 29 | } 30 | &.q-bottom-sheet-overlay { 31 | background-color: transparent; 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/stories/product-slider.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from "@storybook/react"; 2 | import { faker } from '@faker-js/faker'; 3 | import React from "react"; 4 | import ProductSlider from "../components/product-slider"; 5 | 6 | const stories = storiesOf("Product Slider", module); 7 | 8 | const products = [ 9 | { 10 | brand: faker.company.companyName(), 11 | src: "https://source.unsplash.com/random/150x250?fashion", 12 | }, 13 | ]; 14 | 15 | for (let i = 0; i < 10; i++) { 16 | products.push({ 17 | brand: faker.company.companyName(), 18 | src: "https://source.unsplash.com/random/150x250?fashion", 19 | }); 20 | } 21 | 22 | stories.add("Default", () => ( 23 |
    24 |

    Benzer Urunler

    25 | 26 | {products.map((product, key) => ( 27 |
    28 | 29 |

    {product.brand}

    30 |
    31 | ))} 32 |
    33 |

    {faker.lorem.lines(10)}

    34 |
    35 | )); 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /src/styles/components/_icon.scss: -------------------------------------------------------------------------------- 1 | @import "../mixins/_color-scheme"; 2 | @import "../mixins/_fluid.scss"; 3 | 4 | .q-icon { 5 | display: inline-block; 6 | line-height: $line-height; 7 | 8 | &.q-disabled { 9 | @include disabled; 10 | } 11 | 12 | &.q-circular { 13 | background-color: $white; 14 | border-radius: 50%; 15 | padding: 1.25rem; 16 | border: 1px solid $very-light-pink; 17 | box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.06); 18 | } 19 | 20 | // Icon Sizes 21 | &.q-icon-xlarge { 22 | font-size: $icon-font-size-xlarge; 23 | } 24 | 25 | &.q-icon-large { 26 | font-size: $icon-font-size-large; 27 | } 28 | 29 | &.q-icon-medium { 30 | font-size: $icon-font-size-medium; 31 | } 32 | 33 | &.q-icon-small { 34 | font-size: $icon-font-size-small; 35 | } 36 | 37 | //Color variants 38 | @each $color, $value in $colors { 39 | &.#{$color} { 40 | &:before { 41 | color: $value; 42 | } 43 | } 44 | } 45 | 46 | > span { 47 | font-family: inherit; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/components/box/box.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, {PureComponent} from "react"; 3 | import IDiv from "../../interfaces/div"; 4 | import {textAlignTypes} from "../../types/box"; 5 | 6 | export default class Box extends PureComponent { 7 | public render() { 8 | const { 9 | children, 10 | spaced, 11 | fitted, 12 | textAlign = "left", 13 | className, 14 | ...props 15 | } = this.props; 16 | const boxClasses = classNames( 17 | "q-box", 18 | spaced && "q-spaced", 19 | fitted && "q-fitted", 20 | `q-${textAlign}-aligned`, 21 | className, 22 | ); 23 | return ( 24 |
    {children}
    25 | ); 26 | } 27 | } 28 | 29 | interface IBoxProps extends IDiv { 30 | spaced?: boolean; 31 | fitted?: boolean; 32 | textAlign?: textAlignTypes; 33 | className?: string; 34 | children?: React.ReactNode; 35 | } 36 | -------------------------------------------------------------------------------- /src/components/accordion/accordion-content.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | import { CSSTransition } from "react-transition-group"; 4 | import IDiv from "../../interfaces/div"; 5 | import { animationTypes } from "../../types/modal"; 6 | 7 | export default class Content extends PureComponent { 8 | 9 | public render() { 10 | const { expanded, className, children } = this.props; 11 | const accordionContentClasses = classNames("q-accordion-content", className); 12 | return ( 13 | 19 |
    {children}
    20 |
    21 | ); 22 | } 23 | } 24 | 25 | interface IAccordionContentProps extends IDiv { 26 | animation?: animationTypes; 27 | className?: string; 28 | expanded?: boolean; 29 | children?: React.ReactNode; 30 | onChange?: () => void; 31 | } 32 | -------------------------------------------------------------------------------- /src/styles/_colors.scss: -------------------------------------------------------------------------------- 1 | $primary: #f27a1a; 2 | $primary-variant: #fef1e8; 3 | $green: #0bc15c; 4 | $red: #bb0000; 5 | $black: #333333; 6 | $dark-gray: #666666; 7 | $light-gray: #999999; 8 | $border-gray: #e6e6e6; 9 | $white: #ffffff; 10 | $yellow: #ffc000; 11 | $zero: #000000; 12 | $border-second: #deddbe; 13 | $warning: #fffff1; 14 | $supporter-blue: #4d8ee1; 15 | $supporter-gray: #d8d8d8; 16 | $supporter-red: #ec1C36; 17 | 18 | $colors: () !default; 19 | $colors: map_merge(( 20 | "primary": $primary, 21 | "primary-variant": $primary-variant, 22 | "green": $green, 23 | "red": $red, 24 | "black": $black, 25 | "dark-gray": $dark-gray, 26 | "light-gray": $light-gray, 27 | "border-gray": $border-gray, 28 | "white": $white, 29 | "yellow": $yellow, 30 | "zero": $zero, 31 | "border-second": $border-second, 32 | "warning": $warning, 33 | "supporter-blue": $supporter-blue, 34 | "supporter-gray": $supporter-gray, 35 | "supporter-red": $supporter-red 36 | ), $colors 37 | ); 38 | -------------------------------------------------------------------------------- /src/styles/_all.scss: -------------------------------------------------------------------------------- 1 | @import './globals'; 2 | 3 | // Mixins 4 | @import './mixins/color-scheme'; 5 | @import './mixins/flex'; 6 | @import "./mixins/fluid"; 7 | @import "./mixins/text-truncate"; 8 | @import "./mixins/breakpoints"; 9 | 10 | // Components 11 | @import './components/accordion'; 12 | @import './components/badge'; 13 | @import './components/box'; 14 | @import './components/button'; 15 | @import './components/checkbox'; 16 | @import './components/icon'; 17 | @import './components/input'; 18 | @import './components/layout'; 19 | @import './components/link'; 20 | @import './components/list'; 21 | @import './components/loader'; 22 | @import './components/modal'; 23 | @import './components/popup'; 24 | @import './components/product-slider'; 25 | @import './components/radio'; 26 | @import './components/scroll-to-top'; 27 | @import './components/select'; 28 | @import './components/subheader'; 29 | @import './components/switch'; 30 | @import './components/typography'; 31 | @import './components/ripple'; 32 | @import './components/quantity-selector'; 33 | @import './components/step-progress-bar'; 34 | -------------------------------------------------------------------------------- /src/components/accordion/accordion-header.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | import IDiv from "../../interfaces/div"; 4 | import Icon from "../icon"; 5 | 6 | export default class Header extends PureComponent { 7 | public render() { 8 | const { className, children, icon = "chevron-down", handleClick, expanded, ...props } = this.props; 9 | const accordionHeaderClasses = classNames("q-accordion-header", className); 10 | return ( 11 |
    12 |
    {children}
    13 | 20 |
    21 | ); 22 | } 23 | } 24 | 25 | export interface IHeaderProps extends IDiv { 26 | icon?: string; 27 | expanded?: boolean; 28 | className?: string; 29 | children?: React.ReactNode; 30 | handleClick?: () => void; 31 | } 32 | -------------------------------------------------------------------------------- /src/components/badge/badge.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | import IDiv from "../../interfaces/div"; 4 | import { textAlignTypes } from "../../types/box"; 5 | import { colorTypes } from "../../types/color"; 6 | import Icon from "../icon"; 7 | 8 | export default class Badge extends PureComponent { 9 | public render() { 10 | const { 11 | children, 12 | textAlign = "left", 13 | className, 14 | color, 15 | icon, 16 | ...props 17 | } = this.props; 18 | const badgeClasses = classNames( 19 | "q-badge", 20 | `q-${textAlign}-aligned`, 21 | color && `${color}`, 22 | className, 23 | ); 24 | return ( 25 |
    26 | {icon && } 27 | {children &&
    {children}
    } 28 |
    29 | ); 30 | } 31 | } 32 | 33 | interface IBadgeProps extends IDiv { 34 | textAlign?: textAlignTypes; 35 | className?: string; 36 | color?: colorTypes; 37 | icon?: string; 38 | children?: React.ReactNode; 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Trendyol Open Source 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /documents/components/switch.md: -------------------------------------------------------------------------------- 1 | # Switch 2 | 3 | Switch 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 | This component can be used as a radio or a checkbox. 5 | 6 | ## Type 7 | 8 | The default switch type is checkbox. 9 | 10 | ```html 11 | 12 | ``` 13 | 14 | To use switch as a radio component: 15 | 16 | ```html 17 | 18 | ``` 19 | 20 | ## Label 21 | It is possible to add label next to the switch by giving label property 22 | 23 | ```html 24 | 25 | ``` 26 | 27 | ## Error 28 | Adding error prop to switch element changes the component's style to indicate an error has happened. 29 | 30 | ``` 31 | 32 | ``` 33 | 34 | #### Additional content 35 | And of course you can use any other HTML checkbox or radio properties: 36 | ``` 37 | 38 | ``` 39 | -------------------------------------------------------------------------------- /src/components/badge/test/badge.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 Badge from "../badge"; 7 | 8 | Enzyme.configure({ adapter: new Adapter() }); 9 | 10 | describe("badge specs", () => { 11 | const sandbox = sinon.createSandbox(); 12 | 13 | afterEach(() => { 14 | sandbox.verifyAndRestore(); 15 | }); 16 | 17 | it("should render badge component", () => { 18 | const text = faker.lorem.word(); 19 | const wrapper = shallow({text}); 20 | 21 | expect(wrapper.find(".q-badge")).toHaveLength(1); 22 | }); 23 | 24 | it("should render badge component with search icon", () => { 25 | const text = faker.lorem.word(); 26 | const wrapper = mount({text}); 27 | 28 | expect(wrapper.exists(".icon-search")).toBe(true); 29 | }); 30 | 31 | it("should have the given color prop as the className", () => { 32 | const wrapper = shallow(); 33 | 34 | expect(wrapper.exists(".red")).toBe(true); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/styles/components/_rating.scss: -------------------------------------------------------------------------------- 1 | @import '../mixins/_color-scheme'; 2 | 3 | .q-rating { 4 | display: inline-block; 5 | align-items: center; 6 | position: relative; 7 | width: fit-content; 8 | 9 | &.q-rating-large { 10 | .q-icon{ 11 | width: 18px; 12 | font-size: 18px; 13 | line-height: 24px; 14 | } 15 | .q-icon::before{ 16 | display: inline-block; 17 | width: 18px; 18 | } 19 | } 20 | 21 | &.q-rating-medium { 22 | .q-icon{ 23 | width: 14px; 24 | font-size: 14px; 25 | line-height: 24px; 26 | } 27 | .q-icon::before{ 28 | display: inline-block; 29 | width: 14px; 30 | } 31 | } 32 | 33 | &.q-rating-small { 34 | .q-icon{ 35 | width: 10px; 36 | font-size: 10px; 37 | line-height: 24px; 38 | } 39 | .q-icon::before{ 40 | display: inline-block; 41 | width: 10px; 42 | } 43 | } 44 | 45 | .q-rating-empty { 46 | letter-spacing: 5px; 47 | } 48 | 49 | .q-rating-full { 50 | width: 0; 51 | position: absolute; 52 | letter-spacing: 5px; 53 | left: 0px; 54 | top: 0px; 55 | overflow: hidden; 56 | white-space: nowrap; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/components/modal/modal-header.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | import Icon from "../icon"; 4 | 5 | export default class Header extends PureComponent { 6 | public render() { 7 | const { 8 | children, 9 | rightIcon, 10 | leftIcon, 11 | leftIconOnClick, 12 | rightIconOnClick, 13 | className, 14 | } = this.props; 15 | const headerClasses = classNames("q-modal-header", leftIcon && "q-has-left-icon", className); 16 | return ( 17 |
    18 | {leftIcon && 19 | } 20 |

    {children}

    21 | {rightIcon && 22 | 23 | } 24 |
    25 | ); 26 | } 27 | } 28 | 29 | export interface IHeaderProps { 30 | leftIcon?: string; 31 | rightIcon?: string; 32 | className?: string; 33 | children?: React.ReactNode; 34 | leftIconOnClick?: (event: any) => any; 35 | rightIconOnClick?: (event: any) => any; 36 | } 37 | -------------------------------------------------------------------------------- /src/stories/icon.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from "@storybook/react"; 2 | import React from "react"; 3 | import Icon from "../components/icon"; 4 | import Layout from "../components/layout"; 5 | 6 | const stories = storiesOf("Icon", module); 7 | 8 | stories.add("Icons", () => ( 9 | 10 | 11 | 12 | 13 | 14 |
    15 |
    16 | 17 | 18 | 19 | 20 |
    21 |
    22 | 23 | 24 |
    25 | )); 26 | stories.add("Circular", () => ( 27 | 28 | 29 | 30 | 31 | 32 | 33 | )); 34 | -------------------------------------------------------------------------------- /documents/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | Learn how to include Quarkify in your project 3 | Don't forget to check out our [landing page!](https://github.com/Trendyol/quarkify/blob/master/README.md) 4 | 5 | ## Installation 6 | * To add quarkify library to your project 7 | 1. Simply run one of the commands below in your project directory. 8 | 9 | ```sh 10 | npm install quarkify@latest --save 11 | ``` 12 | or 13 | ```sh 14 | yarn add quarkify 15 | ``` 16 | 17 | 18 | * To install storybook locally 19 | 1. Clone the repo 20 | ```sh 21 | git clone https://github.com/Trendyol/quarkify.git 22 | ``` 23 | 2. Install NPM packages 24 | ```sh 25 | npm install 26 | ``` 27 | Or alternatively Yarn 28 | ```sh 29 | yarn install 30 | ``` 31 | 3. Run tests 32 | ```sh 33 | npm run test 34 | yarn run test 35 | ``` 36 | 4. Run storybook 37 | ```sh 38 | npm start 39 | yarn start 40 | ``` 41 | 42 | ## Built With 43 | 44 | * [React](https://reactjs.org/) 45 | * [Create-React-App](https://github.com/facebook/create-react-app) 46 | * [SASS](https://sass-lang.com/) 47 | * [Jest](https://jestjs.io/) 48 | * [Enzyme](https://airbnb.io/enzyme/) 49 | * [Classnames](https://github.com/JedWatson/classnames) 50 | * [Typescript](https://www.typescriptlang.org/) 51 | 52 | -------------------------------------------------------------------------------- /documents/components/quantity-selector.md: -------------------------------------------------------------------------------- 1 | # QuantitySelector 2 | 3 | QuantitySelector is an component capable of incrementing and decrementing the count on it 4 | 5 | ```html 6 | { 9 | console.log("onIncrement"); 10 | }} 11 | onDecrement={() => { 12 | console.log("onDecrement"); 13 | }} 14 | iconProps={ } /> 15 | ``` 16 | 17 | ### Props 18 | 19 | | prop | description | 20 | | ------------- |:-------------: | 21 | | _count_ | specifies the initial count to be displayed on element | 22 | | _iconProps_ (*) | common icon properties | 23 | | _onDecrement_(*) | decrement callback | 24 | | _onIncrement_(*) | increment callback | 25 | | _fluid_ | whether to fill full-width of relative container | 26 | | _size_ | "large" | "medium" | "small" | "xsmall" | 27 | 28 | (*) required -------------------------------------------------------------------------------- /src/stories/scroll-to-top.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from "@storybook/react"; 2 | import React, { PureComponent } from "react"; 3 | import Button from "../components/button"; 4 | import ScrollToTop from "../components/scroll-to-top"; 5 | import "./styles/scroll-to-top.scss"; 6 | 7 | const stories = storiesOf("Scroll To Top", module); 8 | 9 | class ScrollToTopWrapper extends PureComponent { 10 | public render() { 11 | return( 12 |
    13 | {Array.from({ length: 60 }, (_, i) =>

    Down {i}

    )} 14 | 15 |
    16 |
    17 |
    18 | Sepette %60 İndirim 19 |
    99,99 TL
    20 |
    21 |
    22 | 23 |
    24 |
    25 |
    26 |
    27 | ); 28 | } 29 | } 30 | 31 | stories.add("Default", () => ); 32 | -------------------------------------------------------------------------------- /documents/components/box.md: -------------------------------------------------------------------------------- 1 | # Box 2 | 3 | Box is a wrapper component used to display elements in a set style. 4 | 5 | ```html 6 | 7 | {children} 8 | 9 | ``` 10 | 11 | ## Spaced 12 | 13 | The spaced prop adds a margin to the box component. 14 | 15 | ```html 16 | 17 | Spaced Box 18 | 19 | ``` 20 | 21 | ## Text Align 22 | 23 | The textAlign prop, like the name suggest, makes box component's children 24 | aligned according to the value. The default value is left. 25 | 26 | ```html 27 | 28 | Right aligned spaced Box 29 | 30 | 31 | Center aligned spaced Box 32 | 33 | 34 | Justify spaced Box Justify spaced Box Justify spaced Box 35 | 36 | ``` 37 | 38 | ## Fitted 39 | 40 | The fitted prop sets the padding of the box to 0. 41 | 42 | ```html 43 | 44 | Fitted box 45 | 46 | ``` 47 | 48 | # Box Group 49 | 50 | Box Group component is a wrapper component for multiple box components. 51 | 52 | ```html 53 | 54 | 55 | Box 56 | 57 | 58 | Box 59 | 60 | 61 | Box 62 | 63 | 64 | ``` 65 | -------------------------------------------------------------------------------- /src/components/layout/test/layout.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 Layout from "../layout"; 7 | 8 | Enzyme.configure({ adapter: new Adapter() }); 9 | 10 | describe("layout specs", () => { 11 | const sandbox = sinon.createSandbox(); 12 | 13 | afterEach(() => { 14 | sandbox.verifyAndRestore(); 15 | }); 16 | 17 | it("should render layout component", () => { 18 | const wrapper = shallow(); 19 | expect(wrapper.find(".q-layout")).toHaveLength(1); 20 | }); 21 | 22 | it("should have className fitted when given fitted prop", () => { 23 | const wrapper = shallow(); 24 | expect(wrapper.hasClass("q-fitted")).toBe(true); 25 | }); 26 | 27 | it("should have className fullscreen when fullScreen prop is given", () => { 28 | const wrapper = shallow(); 29 | expect(wrapper.hasClass("q-fullscreen")).toBe(true); 30 | }); 31 | 32 | it("should accept additional classNames", () => { 33 | const fakeClass = faker.lorem.word(); 34 | const wrapper = shallow(); 35 | 36 | expect(wrapper.hasClass(fakeClass)).toBe(true); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/components/list/test/list.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 List from "../list"; 7 | 8 | Enzyme.configure({ adapter: new Adapter() }); 9 | 10 | describe("list specs", () => { 11 | const sandbox = sinon.createSandbox(); 12 | 13 | afterEach(() => { 14 | sandbox.verifyAndRestore(); 15 | }); 16 | 17 | it("should render list component", () => { 18 | const wrapper = shallow(); 19 | 20 | expect(wrapper.find(".q-list")).toHaveLength(1); 21 | }); 22 | 23 | it("should render list item component", () => { 24 | const wrapper = shallow(); 25 | 26 | expect(wrapper.find(".q-list-item")).toHaveLength(1); 27 | }); 28 | 29 | it("should render list item component with no dots", () => { 30 | const text = faker.lorem.word(); 31 | const wrapper = shallow({text}); 32 | 33 | expect(wrapper.find(".q-no-dot")).toHaveLength(1); 34 | }); 35 | 36 | it("should render item component with icon", () => { 37 | const wrapper = mount(); 38 | 39 | expect(wrapper.exists(".icon-search")).toEqual(true); 40 | }); 41 | 42 | }); 43 | -------------------------------------------------------------------------------- /src/styles/components/_quantity-selector.scss: -------------------------------------------------------------------------------- 1 | @import "../variables"; 2 | @import "../mixins/flex"; 3 | @import "../mixins/_fluid.scss"; 4 | 5 | .q-qs-main { 6 | display: flex; 7 | justify-content: space-between; 8 | position: relative; 9 | border-radius: $border-radius; 10 | border: solid 1px $primary; 11 | 12 | //Loader 13 | &.loading { 14 | .q-loader { 15 | background-color: transparent; 16 | transform: scale(0.4); 17 | } 18 | 19 | //Hide qs contents 20 | .q-loader ~ div { 21 | opacity: 0; 22 | } 23 | } 24 | 25 | &.q-large { 26 | height: $button-height-large; 27 | font-size: $button-font-size-large; 28 | } 29 | 30 | &.q-medium { 31 | height: $button-height-medium; 32 | font-size: $button-font-size-medium; 33 | } 34 | 35 | &.q-small { 36 | height: $button-height-small; 37 | font-size: $button-font-size-small; 38 | } 39 | 40 | &.q-xsmall { 41 | height: $button-height-xsmall; 42 | font-size: $button-font-size-small; 43 | } 44 | 45 | &.q-fluid { 46 | @include fluid; 47 | } 48 | 49 | .q-qs-middle { 50 | @include flex-center; 51 | margin: $space-small; 52 | color: $black; 53 | } 54 | 55 | .q-qs-left, 56 | .q-qs-right { 57 | @include flex-center; 58 | margin: $space-small; 59 | color: $primary; 60 | } 61 | 62 | .q-qs-left:hover, 63 | .q-qs-right:hover { 64 | cursor: pointer; 65 | } 66 | } -------------------------------------------------------------------------------- /src/stories/box.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from "@storybook/react"; 2 | import React from "react"; 3 | import {Box, BoxGroup} from "../components/box"; 4 | import Layout from "../components/layout"; 5 | 6 | const stories = storiesOf("Box", module); 7 | 8 | stories.add("Box", () => ( 9 | 10 | 11 | Box 12 | 13 | 14 | Spaced Box 15 | 16 | 17 | 18 | Right aligned spaced Box 19 | 20 | 21 | 22 | Center aligned spaced Box 23 | 24 | 25 | 26 | Justify spaced Box Justify spaced Box Justify spaced Box 27 | 28 | 29 | 30 | Fitted spaced box 31 | 32 | 33 | )); 34 | 35 | stories.add("Box Group", () => ( 36 | 37 | 38 | 39 | Box 40 | 41 | 42 | Box 43 | 44 | 45 | Box 46 | 47 | 48 | 49 | )); 50 | -------------------------------------------------------------------------------- /src/styles/components/_typography.scss: -------------------------------------------------------------------------------- 1 | @import "../_variables"; 2 | @import "../mixins/text-truncate"; 3 | 4 | h1,h2,h3, 5 | h4,h5,h6 6 | p, span{ 7 | margin: 0; 8 | } 9 | 10 | .q-typography { 11 | margin: 0; 12 | font-family: "Source Sans Pro", "Helvetica", "Arial", sans-serif; 13 | font-style: normal; 14 | font-stretch: normal; 15 | 16 | &.bold { 17 | font-weight: $bold; 18 | } 19 | 20 | &.ellipsis { 21 | @include text-truncate; 22 | } 23 | 24 | &.underline { 25 | text-decoration: underline; 26 | } 27 | 28 | &.q-h1 { 29 | font-size: 32px; 30 | line-height: 40px; 31 | } 32 | 33 | &.q-h2 { 34 | font-size: 24px; 35 | line-height: 30px; 36 | } 37 | 38 | &.q-h3 { 39 | font-size: 18px; 40 | line-height: 24px; 41 | } 42 | 43 | &.q-paragraph { 44 | font-size: 14px; 45 | line-height: 18px; 46 | } 47 | 48 | &.q-smallParagraph { 49 | font-size: 12px; 50 | line-height: 16px; 51 | } 52 | 53 | &.q-body { 54 | font-size: 16px; 55 | line-height: 20px; 56 | } 57 | 58 | &.q-subtitle { 59 | font-size: 13px; 60 | line-height: 16px; 61 | } 62 | 63 | &.q-block{ 64 | display: block; 65 | } 66 | 67 | &.q-inline{ 68 | display: inline; 69 | } 70 | 71 | 72 | @each $color, $value in $colors { 73 | &.#{$color} { 74 | color: $value; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/components/switch/switch.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React, { PureComponent } from "react"; 3 | import IInput from "../../interfaces/input"; 4 | import { switchTypes, variantTypes } from "../../types/switch"; 5 | import classNamesDefault from "../../utils/class-names-default"; 6 | 7 | export default class Switch extends PureComponent { 8 | public render() { 9 | const { error, disabled, checked, label, value, type = "checkbox", className, ...props } = this.props; 10 | const switchClasses = classNames( 11 | "q-switch-wrapper", 12 | classNamesDefault({ error, disabled }), 13 | className); 14 | return ( 15 | 29 | ); 30 | } 31 | } 32 | 33 | interface IProps extends IInput { 34 | type?: switchTypes; 35 | value: string; 36 | variant?: variantTypes; 37 | error?: boolean; 38 | disabled?: boolean; 39 | label?: string; 40 | className?: string; 41 | } 42 | -------------------------------------------------------------------------------- /src/stories/link.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from "@storybook/react"; 2 | import React from "react"; 3 | import Link from "../components/link"; 4 | 5 | const stories = storiesOf("Link", module); 6 | 7 | const url = "https://github.com/Trendyol/quarkify"; 8 | 9 | stories.add("Variants", () => ( 10 |
    11 | Google 12 |   13 | 14 | Github 15 | 16 |   17 | 18 | Github 19 | 20 |
    21 |
    22 | Google 23 |   24 | 25 | Github 26 | 27 |   28 | 29 | Github 30 | 31 |
    32 |
    33 | Github 34 |   35 | 36 | Github 37 | 38 |   39 | 40 | Github 41 | 42 |
    43 | )); 44 | 45 | stories.add("Disabled", () => ( 46 | 47 | Disabled Github 48 | 49 | )); 50 | stories.add("Blank", () => ( 51 | 52 | Github in new tab 53 | 54 | )); 55 | -------------------------------------------------------------------------------- /src/stories/countdown.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from "@storybook/react"; 2 | import React, { PureComponent } from "react"; 3 | import Countdown from "../components/countdown"; 4 | 5 | const stories = storiesOf("Countdown", module); 6 | 7 | const threshold = 1000 * 60 * 60 * 24 * 2; 8 | 9 | const til12Hours = new Date().getTime() + (43200000); 10 | const til1Day12Hours = new Date().getTime() + (129600000); 11 | 12 | stories.add("With days", () => ( 13 |
    14 | 15 |
    16 | )); 17 | 18 | stories.add("With custom days", () => ( 19 |
    20 | 21 |
    22 | )); 23 | 24 | stories.add("With hours", () => ( 25 |
    26 | 27 |
    28 | )); 29 | 30 | stories.add("With custom hours", () => ( 31 |
    32 | 33 |
    34 | )); 35 | 36 | stories.add("With GTM + 3", () => ( 37 |
    38 | 39 |
    40 | )); 41 | 42 | stories.add("With custom GTM + 3", () => ( 43 |
    44 | 45 |
    46 | )); 47 | -------------------------------------------------------------------------------- /src/components/product-slider/test/product-slider.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 ProductSlider from "../index"; 6 | 7 | Enzyme.configure({ adapter: new Adapter() }); 8 | 9 | describe("product-slider specs", () => { 10 | it("should render product slider correctly", () => { 11 | const wrapper = shallow(); 12 | 13 | expect(wrapper.find(".q-product-slider")).toHaveLength(1); 14 | }); 15 | 16 | it("should give every child a item className", () => { 17 | const numberOfChildren = faker.number.int(10); 18 | const wrapper = shallow( 19 | 20 | {Array.from( 21 | { length: numberOfChildren }, (_, key) => (
    ))} 22 | , 23 | ); 24 | 25 | expect(wrapper.find(".q-item")).toHaveLength(numberOfChildren); 26 | }); 27 | 28 | it("should accept additional classNames", () => { 29 | const fakeClass = faker.lorem.word(); 30 | const numberOfChildren = faker.number.int(10); 31 | const wrapper = mount( 32 | 33 | {Array.from({ length: numberOfChildren }, (_, key) => (
    ))} 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 |
    10 | 11 |
    12 |
    13 | 14 |
    15 |
    16 | 17 |
    18 |
    19 | 20 |
    21 |
    22 | 23 | 24 |
    25 | )); 26 | 27 | stories.add("As radio", () => ( 28 |
    29 |
    30 | 31 |
    32 |
    33 | 34 |
    35 |
    36 | 37 |
    38 |
    39 | 40 |
    41 |
    42 | 43 | 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 | 55 | 56 |
    57 | ); 58 | }); 59 | 60 | stories.add("Icon", () => { 61 | 62 | return ( 63 |
    64 | 68 |
    69 |
    70 | 78 |
    79 | )); 80 | 81 | stories.add("Disabled", () => ( 82 |
    83 | 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 | 39 | ``` 40 | 41 | ## Name 42 | Similarly, if you want to dynamically change the name, you can give this prop as a string. By default, it is set to "name". 43 | 44 | ``` 45 | const items = [ 46 | { 47 | myNewName: "Plane", 48 | id: 3, 49 | variant: "secondary", 50 | } 51 | ] 52 | 91 | ``` 92 | ); 93 | 94 | ## Error 95 | Select can be displayed in an error state 96 | 97 | ```html 98 | 25 | 26 |
    27 | 28 | 29 |
    30 | 31 | 32 | 35 | 36 | 37 | ``` 38 | 39 | 40 | 41 | As it can be seen, Modal component is shown or hidden by the prop it takes show. If show prop is true, modal will be shown. Otherwise it will not be rendered. 42 | 43 | ## Animation 44 | 45 | To change the animation type, you can modify the animation prop which takes a string. For now, only 4 different animations are possible: 46 | 47 | - slideInLeft 48 | - slideInRight 49 | - slideInUp 50 | - slideInDown 51 | 52 | ## Sub components 53 | 54 | Header, Content and Actions sub components are only suggested for style issues. It is okay to not use, but it will look nicer if it is been used. 55 | 56 | Bad Example: 57 | ``` 58 | 59 | 60 | My Header 61 |

    First paragraph

    62 |

    Seconda paragraph

    63 |

    Third paragraph

    64 |

    Fourth paragraph

    > 65 |
    66 | ``` 67 | 68 | 69 | 70 | As it can be seen, without giving Content subcomponent, first paragraph will not be shown because of it hides behind of the header sub component. 71 | -------------------------------------------------------------------------------- /src/components/button/test/button.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 Button from "../button"; 7 | 8 | Enzyme.configure({ adapter: new Adapter() }); 9 | 10 | const onClick = () => { 11 | alert("test"); 12 | }; 13 | 14 | describe("button specs", () => { 15 | const sandbox = sinon.createSandbox(); 16 | 17 | afterEach(() => { 18 | sandbox.verifyAndRestore(); 19 | }); 20 | 21 | it("should render button component", () => { 22 | const text = faker.lorem.word(); 23 | const wrapper = shallow(); 24 | 25 | expect(wrapper.find("button")).toHaveLength(1); 26 | }); 27 | 28 | it("should call callback function when clicked", () => { 29 | const spy = sandbox.spy(); 30 | const wrapper = shallow(