From 09b0bb4bbd0d4875199de89914b514673c9ba764 Mon Sep 17 00:00:00 2001 From: tolms Date: Wed, 16 Oct 2024 11:26:06 +0500 Subject: [PATCH 1/5] feat(checkbox): added CheckBox component --- .gitignore | 18 ++- .jest/svg.tsx | 2 + components/checkbox/.npmignore | 1 + components/checkbox/README.md | 21 ++++ components/checkbox/package.json | 39 ++++++ components/checkbox/rollup.config.js | 15 +++ components/checkbox/src/CheckBox.module.css | 77 ++++++++++++ components/checkbox/src/CheckBox.stories.mdx | 8 ++ components/checkbox/src/CheckBox.tests.tsx | 114 ++++++++++++++++++ components/checkbox/src/CheckBox.tsx | 31 +++++ components/checkbox/src/CheckBox.types.ts | 40 ++++++ components/checkbox/src/Checkbox.stories.tsx | 111 +++++++++++++++++ components/checkbox/src/assets/check.svg | 3 + .../checkbox/src/assets/indeterminate.svg | 3 + components/checkbox/src/index.ts | 8 ++ .../src/partials/CheckBoxContainer.module.css | 4 + .../src/partials/CheckBoxContainer.tsx | 65 ++++++++++ .../checkbox/src/partials/CheckBoxContext.tsx | 22 ++++ .../src/partials/CheckBoxIndicator.tsx | 38 ++++++ .../checkbox/src/partials/CheckBoxLabel.tsx | 21 ++++ .../utilities/getDefaultCheckBoxClassNames.ts | 22 ++++ components/checkbox/tsconfig.build.json | 4 + components/checkbox/tsconfig.json | 10 ++ jest.config.js | 7 +- package-lock.json | 47 ++++++++ 25 files changed, 724 insertions(+), 7 deletions(-) create mode 100644 .jest/svg.tsx create mode 100644 components/checkbox/.npmignore create mode 100644 components/checkbox/README.md create mode 100644 components/checkbox/package.json create mode 100644 components/checkbox/rollup.config.js create mode 100644 components/checkbox/src/CheckBox.module.css create mode 100644 components/checkbox/src/CheckBox.stories.mdx create mode 100644 components/checkbox/src/CheckBox.tests.tsx create mode 100644 components/checkbox/src/CheckBox.tsx create mode 100644 components/checkbox/src/CheckBox.types.ts create mode 100644 components/checkbox/src/Checkbox.stories.tsx create mode 100644 components/checkbox/src/assets/check.svg create mode 100644 components/checkbox/src/assets/indeterminate.svg create mode 100644 components/checkbox/src/index.ts create mode 100644 components/checkbox/src/partials/CheckBoxContainer.module.css create mode 100644 components/checkbox/src/partials/CheckBoxContainer.tsx create mode 100644 components/checkbox/src/partials/CheckBoxContext.tsx create mode 100644 components/checkbox/src/partials/CheckBoxIndicator.tsx create mode 100644 components/checkbox/src/partials/CheckBoxLabel.tsx create mode 100644 components/checkbox/src/utilities/getDefaultCheckBoxClassNames.ts create mode 100644 components/checkbox/tsconfig.build.json create mode 100644 components/checkbox/tsconfig.json diff --git a/.gitignore b/.gitignore index b48fcf6c..fbc401c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,19 @@ -*.log +# dependencies +node_modules + +# build +dist + +# testing +.coverage + +# editors .idea +.vscode + +# misc +*.log .DS_Store -node_modules .cache -dist lib -coverage yarn.lock diff --git a/.jest/svg.tsx b/.jest/svg.tsx new file mode 100644 index 00000000..adfd9115 --- /dev/null +++ b/.jest/svg.tsx @@ -0,0 +1,2 @@ +export default 'svg'; +export const ReactComponent = 'div'; diff --git a/components/checkbox/.npmignore b/components/checkbox/.npmignore new file mode 100644 index 00000000..85de9cf9 --- /dev/null +++ b/components/checkbox/.npmignore @@ -0,0 +1 @@ +src diff --git a/components/checkbox/README.md b/components/checkbox/README.md new file mode 100644 index 00000000..5dc7283d --- /dev/null +++ b/components/checkbox/README.md @@ -0,0 +1,21 @@ +# `@byndyusoft-ui/checkbox` + +A React CheckBox component. + +### Installation + +```sh +npm i @byndyusoft-ui/checkbox +# or +yarn add @byndyusoft-ui/checkbox +``` + +### Usage + +```ts +// Usage examples coming soon +``` + +### License + +Apache-2.0 diff --git a/components/checkbox/package.json b/components/checkbox/package.json new file mode 100644 index 00000000..60986c7d --- /dev/null +++ b/components/checkbox/package.json @@ -0,0 +1,39 @@ +{ + "name": "@byndyusoft-ui/checkbox", + "version": "0.0.1", + "description": "Byndyusoft UI CheckBox React Component", + "keywords": [ + "byndyusoft", + "byndyusoft-ui", + "react", + "checkbox" + ], + "author": "Tolmachev Serega ", + "homepage": "https://github.com/Byndyusoft/ui/tree/master/components/checkbox#readme", + "license": "Apache-2.0", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/Byndyusoft/ui.git" + }, + "scripts": { + "build": "tsc --project tsconfig.build.json", + "clean": "rimraf dist", + "lint": "eslint src --config ../../eslint.config.js", + "test": "jest --config ../../jest.config.js --roots components/checkbox/src" + }, + "bugs": { + "url": "https://github.com/Byndyusoft/ui/issues" + }, + "publishConfig": { + "access": "public" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + }, + "devDependencies": { + "react-hook-form": "^7.43.9" + } +} diff --git a/components/checkbox/rollup.config.js b/components/checkbox/rollup.config.js new file mode 100644 index 00000000..b14566e7 --- /dev/null +++ b/components/checkbox/rollup.config.js @@ -0,0 +1,15 @@ +import typescript from '@rollup/plugin-typescript'; +import baseConfig from '../../rollup.base.config'; + +export default { + ...baseConfig, + input: ['src/index.ts'], + plugins: [ + ...baseConfig.plugins, + typescript({ + tsconfig: './tsconfig.json', + module: 'ESNext', + exclude: ['src/*.stories.tsx', 'src/*.tests.tsx', 'node_modules'] + }) + ] +}; diff --git a/components/checkbox/src/CheckBox.module.css b/components/checkbox/src/CheckBox.module.css new file mode 100644 index 00000000..34404209 --- /dev/null +++ b/components/checkbox/src/CheckBox.module.css @@ -0,0 +1,77 @@ +/** + * Container + */ +.container { + position: relative; + + display: flex; + align-items: flex-start; + gap: 0.5rem; + width: fit-content; + + user-select: none; + cursor: pointer; +} + +.containerDisabled { + cursor: default; +} + +/** + * Label + */ +.label { + flex-grow: 1; + flex-shrink: 1; +} + +.labelDisabled { + color: #7a7f82; +} + +/** + * Indicator + */ +.indicator { + box-sizing: border-box; + display: flex; + align-items: center; + flex-grow: 0; + flex-shrink: 0; + justify-content: center; + + width: 1rem; + height: 1rem; + + border: 1px solid #21272b; + border-radius: 0.125rem; + color: #fff; +} + +.indicator:not(.indicatorDisabled):hover { + background-color: #e0e0e0; +} + +.indicatorDisabled { + border-color: #adb0b2; +} + +.indicatorChecked, +.indicatorIndeterminate { + border: none; + background-color: #2b83ba; +} + +.indicatorChecked:not(.indicatorDisabled):hover, +.indicatorIndeterminate:not(.indicatorDisabled):hover { + background-color: #3b99d4; +} + +.indicatorChecked.indicatorDisabled, +.indicatorIndeterminate.indicatorDisabled { + background-color: #adb0b2; +} + +.input:focus-visible + .indicator { + box-shadow: 0 0 0 2px #ffd86a; +} diff --git a/components/checkbox/src/CheckBox.stories.mdx b/components/checkbox/src/CheckBox.stories.mdx new file mode 100644 index 00000000..3ade97d4 --- /dev/null +++ b/components/checkbox/src/CheckBox.stories.mdx @@ -0,0 +1,8 @@ +import { Meta } from '@storybook/addon-docs'; +import { Markdown } from '@storybook/blocks'; + +import Readme from '../README.md'; + + + +{Readme} diff --git a/components/checkbox/src/CheckBox.tests.tsx b/components/checkbox/src/CheckBox.tests.tsx new file mode 100644 index 00000000..2ae9fb63 --- /dev/null +++ b/components/checkbox/src/CheckBox.tests.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { useForm } from 'react-hook-form'; +// eslint-disable-next-line import/no-named-as-default +import userEvent from '@testing-library/user-event'; + +import CheckBox from './CheckBox'; +import CheckBoxLabel from './partials/CheckBoxLabel'; + +interface IFormValues { + isChecked: boolean; +} + +interface IFormProps { + defaultValues: IFormValues; + onSubmit: (values: IFormValues) => void; +} + +const Form = ({ defaultValues, onSubmit }: IFormProps): JSX.Element => { + const { register, handleSubmit } = useForm({ defaultValues }); + + return ( + // eslint-disable-next-line @typescript-eslint/no-misused-promises +
+ {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} + {/* @ts-expect-error */} + Label + +
+ ); +}; + +describe('components/CheckBox', () => { + test('should render children', () => { + const onChange = jest.fn(); + + render( + + Check box label + + ); + + expect(screen.getByText('Check box label')).toBeInTheDocument(); + }); + + test('should render checked checkbox', () => { + const onChange = jest.fn(); + + render( + + Check box label + + ); + + expect(screen.getByRole('checkbox')).toBeChecked(); + }); + + test('should render unchecked checkbox', () => { + const onChange = jest.fn(); + + render( + + Check box label + + ); + + expect(screen.getByRole('checkbox')).not.toBeChecked(); + }); + + test('should render disabled checkbox', () => { + const onChange = jest.fn(); + + render( + + Disabled check box label + + ); + + expect(screen.getByRole('checkbox')).toBeDisabled(); + }); + + test('should render indeterminate checkbox', () => { + const onChange = jest.fn(); + + render( + + Indeterminate check box label + + ); + + expect(screen.getByRole('checkbox')).toBePartiallyChecked(); + }); + + test('should works with react-hook-form correctly', async () => { + const onSubmit = jest.fn(); + const defaultValues = { + isChecked: true + }; + + render(
); + + expect(screen.getByRole('checkbox')).toBeChecked(); + + await userEvent.click(screen.getByRole('checkbox')); + expect(screen.getByRole('checkbox')).not.toBeChecked(); + + await userEvent.click(screen.getByText('Submit')); + expect(onSubmit).toHaveBeenCalledWith({ isChecked: false }, expect.any(Object)); + }); + + test('should throw error without using context', () => { + expect(() => render(some label text)).toThrow(); + }); +}); diff --git a/components/checkbox/src/CheckBox.tsx b/components/checkbox/src/CheckBox.tsx new file mode 100644 index 00000000..a5a6efe8 --- /dev/null +++ b/components/checkbox/src/CheckBox.tsx @@ -0,0 +1,31 @@ +import React, { forwardRef } from 'react'; + +import { ICheckBoxProps } from './CheckBox.types'; +import getDefaultCheckBoxClassNames from './utilities/getDefaultCheckBoxClassNames'; + +import CheckBoxContainer from './partials/CheckBoxContainer'; +import CheckBoxIndicator from './partials/CheckBoxIndicator'; +import CheckBoxLabel from './partials/CheckBoxLabel'; + +const CheckBox = forwardRef( + ( + { children, classNames = getDefaultCheckBoxClassNames(), labelPosition = 'right', renderIndicator, ...props }, + ref + ): JSX.Element => ( + + {labelPosition === 'left' && {children}} + + {renderIndicator ? ( + renderIndicator(classNames.indicator) + ) : ( + + )} + + {labelPosition === 'right' && {children}} + + ) +); + +CheckBox.displayName = 'CheckBox'; + +export default CheckBox; diff --git a/components/checkbox/src/CheckBox.types.ts b/components/checkbox/src/CheckBox.types.ts new file mode 100644 index 00000000..8ad8de67 --- /dev/null +++ b/components/checkbox/src/CheckBox.types.ts @@ -0,0 +1,40 @@ +import { InputHTMLAttributes } from 'react'; + +export interface ICheckBoxClassNames { + container?: ICheckBoxContainerClassNames; + indicator?: ICheckBoxIndicatorClassNames; + label?: ICheckBoxLabelClassNames; +} + +export interface ICheckBoxContainerClassNames { + main?: string; + disabled?: string; + input?: string; +} + +export interface ICheckBoxIndicatorClassNames { + main?: string; + checked?: string; + disabled?: string; + indeterminate?: string; +} + +export interface ICheckBoxLabelClassNames { + main?: string; + disabled?: string; +} + +export interface ICheckBoxInputProps + extends Omit, 'checked' | 'disabled' | 'id' | 'type'> { + id?: string; + isChecked: boolean; + isDisabled?: boolean; + isIndeterminate?: boolean; +} + +export interface ICheckBoxProps extends ICheckBoxInputProps { + className?: string; + classNames?: ICheckBoxClassNames; + labelPosition?: 'left' | 'right'; + renderIndicator?: (classNames?: ICheckBoxIndicatorClassNames) => JSX.Element; +} diff --git a/components/checkbox/src/Checkbox.stories.tsx b/components/checkbox/src/Checkbox.stories.tsx new file mode 100644 index 00000000..a25ee533 --- /dev/null +++ b/components/checkbox/src/Checkbox.stories.tsx @@ -0,0 +1,111 @@ +import React, { ComponentType, useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { ICheckBoxProps } from './CheckBox.types'; +import CheckBox from './CheckBox'; + +const meta: Meta = { + component: CheckBox, + title: 'Components/CheckBox', + args: { + children: 'some text', + isChecked: false, + isDisabled: false, + isIndeterminate: false, + labelPosition: 'right', + onChange() {} + } +}; + +type TStory = StoryObj; + +export const Default: TStory = {}; + +export const Checked: TStory = { + args: { + isChecked: true + } +}; + +export const Indeterminate: TStory = { + args: { + isIndeterminate: true + } +}; + +export const Disabled: TStory = { + args: { + isDisabled: true + } +}; + +export const WithoutText: TStory = { + args: { + children: null + } +}; + +export const MultilineText: TStory = { + decorators: [ + (CurrentStory: ComponentType): JSX.Element => ( + // eslint-disable-next-line react/forbid-dom-props +
+ +
+ ) + ], + args: { + children: + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam explicabo maiores mollitia nesciunt, nisi non quam voluptatibus. Debitis dolorem earum eius, esse eum facere iste quo temporibus vero voluptatem voluptatum.' + } +}; + +export const TextLeftPosition: TStory = { + args: { + labelPosition: 'left' + } +}; + +const CheckBoxPlayground = (): JSX.Element => { + const [state1, setState1] = useState({ isChecked: false }); + const [state2, setState2] = useState({ isChecked: true }); + const [state3, setState3] = useState({ isChecked: false, isIndeterminate: true }); + + const prepareState = (isChecked: boolean): ICheckBoxProps => + isChecked ? { isChecked, isIndeterminate: false } : { isChecked }; + + return ( +
+ setState1(prevState => ({ ...prevState, ...prepareState(event.target.checked) }))} + > + Чекбокс 1 + +
+ setState2(prevState => ({ ...prevState, ...prepareState(event.target.checked) }))} + > + Чекбокс 2 + +
+ setState3(prevState => ({ ...prevState, ...prepareState(event.target.checked) }))} + > + Чекбокс 3 + +
+ ); +}; + +export const Playground: TStory = { + args: {}, + render() { + return ; + } +}; + +export default meta; diff --git a/components/checkbox/src/assets/check.svg b/components/checkbox/src/assets/check.svg new file mode 100644 index 00000000..574e7d18 --- /dev/null +++ b/components/checkbox/src/assets/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/components/checkbox/src/assets/indeterminate.svg b/components/checkbox/src/assets/indeterminate.svg new file mode 100644 index 00000000..1e639217 --- /dev/null +++ b/components/checkbox/src/assets/indeterminate.svg @@ -0,0 +1,3 @@ + + + diff --git a/components/checkbox/src/index.ts b/components/checkbox/src/index.ts new file mode 100644 index 00000000..4e68b34c --- /dev/null +++ b/components/checkbox/src/index.ts @@ -0,0 +1,8 @@ +export { default as CheckBoxContainer } from './partials/CheckBoxContainer'; +export { default as CheckBoxContext, useCheckBox } from './partials/CheckBoxContext'; +export { default as CheckBoxIndicator } from './partials/CheckBoxIndicator'; +export { default as CheckBoxLabel } from './partials/CheckBoxLabel'; +export { default } from './CheckBox'; + +export * from './CheckBox.types'; +export { default as getDefaultCheckBoxClassNames } from './utilities/getDefaultCheckBoxClassNames'; diff --git a/components/checkbox/src/partials/CheckBoxContainer.module.css b/components/checkbox/src/partials/CheckBoxContainer.module.css new file mode 100644 index 00000000..cacb50b5 --- /dev/null +++ b/components/checkbox/src/partials/CheckBoxContainer.module.css @@ -0,0 +1,4 @@ +.input { + appearance: none; + position: absolute; +} diff --git a/components/checkbox/src/partials/CheckBoxContainer.tsx b/components/checkbox/src/partials/CheckBoxContainer.tsx new file mode 100644 index 00000000..a41a06cf --- /dev/null +++ b/components/checkbox/src/partials/CheckBoxContainer.tsx @@ -0,0 +1,65 @@ +import React, { ReactNode, forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; +import { v4 as guid } from 'uuid'; +import cn from 'classnames'; + +import { ICheckBoxContainerClassNames, ICheckBoxInputProps } from '../CheckBox.types'; + +import CheckBoxContext from './CheckBoxContext'; +import styles from './CheckBoxContainer.module.css'; + +export interface ICheckBoxContainerProps extends ICheckBoxInputProps { + children: ReactNode; + className?: string; + classNames?: ICheckBoxContainerClassNames; +} + +const CheckBoxContainer = forwardRef( + ( + { children, className, classNames, id, isChecked, isDisabled = false, isIndeterminate = false, ...otherProps }, + ref + ): JSX.Element => { + const contextValue = useMemo( + () => ({ + id: id ?? guid(), + isChecked, + isDisabled, + isIndeterminate + }), + [id, isChecked, isDisabled, isIndeterminate] + ); + + const inputRef = useRef(null); + + useImperativeHandle(ref, () => inputRef.current as HTMLInputElement); + + useEffect(() => { + if (inputRef.current !== null) { + inputRef.current.indeterminate = contextValue.isIndeterminate; + } + }, [contextValue.isIndeterminate]); + + return ( + + + + ); + } +); + +CheckBoxContainer.displayName = 'CheckBoxContainer'; + +export default CheckBoxContainer; diff --git a/components/checkbox/src/partials/CheckBoxContext.tsx b/components/checkbox/src/partials/CheckBoxContext.tsx new file mode 100644 index 00000000..908c7094 --- /dev/null +++ b/components/checkbox/src/partials/CheckBoxContext.tsx @@ -0,0 +1,22 @@ +import { createContext, useContext } from 'react'; + +interface ICheckBoxContext { + id: string; + isChecked: boolean; + isDisabled: boolean; + isIndeterminate: boolean; +} + +const CheckBoxContext = createContext(null); + +export function useCheckBox(): ICheckBoxContext | null { + const context = useContext(CheckBoxContext); + + if (!context) { + throw new Error('useCheckBox must be used within the CheckBoxContext.Provider'); + } + + return context; +} + +export default CheckBoxContext; diff --git a/components/checkbox/src/partials/CheckBoxIndicator.tsx b/components/checkbox/src/partials/CheckBoxIndicator.tsx new file mode 100644 index 00000000..5007a9b7 --- /dev/null +++ b/components/checkbox/src/partials/CheckBoxIndicator.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import cn from 'classnames'; + +import { ICheckBoxIndicatorClassNames } from '../CheckBox.types'; +import { useCheckBox } from './CheckBoxContext'; + +import { ReactComponent as CheckIcon } from '../assets/check.svg'; +import { ReactComponent as IndeterminateIcon } from '../assets/indeterminate.svg'; + +export interface ICheckBoxIndicatorProps { + className?: string; + classNames?: ICheckBoxIndicatorClassNames; +} + +const CheckBoxIndicator = ({ className, classNames }: ICheckBoxIndicatorProps): JSX.Element => { + const context = useCheckBox(); + + const isChecked = Boolean(context?.isChecked); + const isDisabled = Boolean(context?.isDisabled); + const isIndeterminate = !isChecked && Boolean(context?.isIndeterminate); + + const containerClassNames = cn( + classNames?.main, + isDisabled && classNames?.disabled, + isChecked && classNames?.checked, + isIndeterminate && classNames?.indeterminate, + className + ); + + return ( +
+ {isChecked && } + {isIndeterminate && } +
+ ); +}; + +export default CheckBoxIndicator; diff --git a/components/checkbox/src/partials/CheckBoxLabel.tsx b/components/checkbox/src/partials/CheckBoxLabel.tsx new file mode 100644 index 00000000..b8678645 --- /dev/null +++ b/components/checkbox/src/partials/CheckBoxLabel.tsx @@ -0,0 +1,21 @@ +import React, { ReactNode } from 'react'; +import cn from 'classnames'; + +import { ICheckBoxLabelClassNames } from '../CheckBox.types'; +import { useCheckBox } from './CheckBoxContext'; + +export interface ICheckBoxLabelProps { + children: ReactNode; + className?: string; + classNames?: ICheckBoxLabelClassNames; +} + +const CheckBoxLabel = ({ children, className, classNames }: ICheckBoxLabelProps): JSX.Element => { + const context = useCheckBox(); + + const classes = cn(classNames?.main, context?.isDisabled && classNames?.disabled, className); + + return
{children}
; +}; + +export default CheckBoxLabel; diff --git a/components/checkbox/src/utilities/getDefaultCheckBoxClassNames.ts b/components/checkbox/src/utilities/getDefaultCheckBoxClassNames.ts new file mode 100644 index 00000000..a7ff9f6c --- /dev/null +++ b/components/checkbox/src/utilities/getDefaultCheckBoxClassNames.ts @@ -0,0 +1,22 @@ +import { ICheckBoxClassNames } from '../CheckBox.types'; +import styles from '../CheckBox.module.css'; + +const getDefaultCheckBoxClassNames = (): ICheckBoxClassNames => ({ + container: { + main: styles.container, + disabled: styles.containerDisabled, + input: styles.input + }, + indicator: { + main: styles.indicator, + checked: styles.indicatorChecked, + disabled: styles.indicatorDisabled, + indeterminate: styles.indicatorIndeterminate + }, + label: { + main: styles.label, + disabled: styles.labelDisabled + } +}); + +export default getDefaultCheckBoxClassNames; diff --git a/components/checkbox/tsconfig.build.json b/components/checkbox/tsconfig.build.json new file mode 100644 index 00000000..2c767fd6 --- /dev/null +++ b/components/checkbox/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src/*.tests.ts", "src/*.stories.tsx"] +} diff --git a/components/checkbox/tsconfig.json b/components/checkbox/tsconfig.json new file mode 100644 index 00000000..98f42aee --- /dev/null +++ b/components/checkbox/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationDir": "dist", + "outDir": "dist", + "module": "commonjs" + }, + "include": ["../../types.d.ts", "src"] +} diff --git a/jest.config.js b/jest.config.js index 9c7f8d3c..c773d584 100644 --- a/jest.config.js +++ b/jest.config.js @@ -9,10 +9,11 @@ module.exports = { moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx'], moduleNameMapper: { '\\.css$': 'identity-obj-proxy', - '\\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|pdf)$': - '/.jest/file.ts' + '\\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|pdf)$': + '/.jest/file.ts', + "\\.svg": ".jest/svg.tsx" }, coverageDirectory: '/.coverage', - coveragePathIgnorePatterns: ['index.ts'], + coveragePathIgnorePatterns: ['.jest', 'index.ts'], clearMocks: true }; diff --git a/package-lock.json b/package-lock.json index 2e19fa2a..4d5fc018 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,6 +74,34 @@ "react-dom": "^17.0.2" } }, + "components/checkbox": { + "name": "@byndyusoft-ui/checkbox", + "version": "0.0.1", + "license": "Apache-2.0", + "devDependencies": { + "react-hook-form": "^7.43.9" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "components/checkbox/node_modules/react-hook-form": { + "version": "7.43.9", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.9.tgz", + "integrity": "sha512-AUDN3Pz2NSeoxQ7Hs6OhQhDr6gtF9YRuutGDwPQqhSUAHJSgGl2VeY3qN19MG0SucpjgDiuMJ4iC5T5uB+eaNQ==", + "dev": true, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, "components/modals-provider": { "name": "@byndyusoft-ui/modals-provider", "version": "0.1.1", @@ -2386,6 +2414,10 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@byndyusoft-ui/checkbox": { + "resolved": "components/checkbox", + "link": true + }, "node_modules/@byndyusoft-ui/keyframes-css": { "resolved": "styles/keyframes-css", "link": true @@ -36833,6 +36865,21 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@byndyusoft-ui/checkbox": { + "version": "file:components/checkbox", + "requires": { + "react-hook-form": "^7.43.9" + }, + "dependencies": { + "react-hook-form": { + "version": "7.43.9", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.9.tgz", + "integrity": "sha512-AUDN3Pz2NSeoxQ7Hs6OhQhDr6gtF9YRuutGDwPQqhSUAHJSgGl2VeY3qN19MG0SucpjgDiuMJ4iC5T5uB+eaNQ==", + "dev": true, + "requires": {} + } + } + }, "@byndyusoft-ui/keyframes-css": { "version": "file:styles/keyframes-css" }, From 4ef3c6f7217bf0d592d731e68e5e369c121b7683 Mon Sep 17 00:00:00 2001 From: tolms Date: Fri, 30 May 2025 12:45:07 +0500 Subject: [PATCH 2/5] feat(checkbox): corrected render svg files --- components/checkbox/src/partials/CheckBoxIndicator.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/checkbox/src/partials/CheckBoxIndicator.tsx b/components/checkbox/src/partials/CheckBoxIndicator.tsx index 5007a9b7..428ffd7f 100644 --- a/components/checkbox/src/partials/CheckBoxIndicator.tsx +++ b/components/checkbox/src/partials/CheckBoxIndicator.tsx @@ -4,8 +4,8 @@ import cn from 'classnames'; import { ICheckBoxIndicatorClassNames } from '../CheckBox.types'; import { useCheckBox } from './CheckBoxContext'; -import { ReactComponent as CheckIcon } from '../assets/check.svg'; -import { ReactComponent as IndeterminateIcon } from '../assets/indeterminate.svg'; +import CheckIcon from '../assets/check.svg?react'; +import IndeterminateIcon from '../assets/indeterminate.svg?react'; export interface ICheckBoxIndicatorProps { className?: string; From a9db4b89e61645a3be3c2d0f85b4668310f38095 Mon Sep 17 00:00:00 2001 From: tolms Date: Fri, 30 May 2025 12:47:23 +0500 Subject: [PATCH 3/5] feat(checkbox): updated types --- components/checkbox/src/CheckBox.tsx | 2 +- components/checkbox/src/Checkbox.stories.tsx | 2 +- .../src/partials/CheckBoxContainer.tsx | 2 +- .../src/partials/CheckBoxIndicator.tsx | 2 +- .../checkbox/src/partials/CheckBoxLabel.tsx | 2 +- package-lock.json | 33 +++++++++++++++++++ 6 files changed, 38 insertions(+), 5 deletions(-) diff --git a/components/checkbox/src/CheckBox.tsx b/components/checkbox/src/CheckBox.tsx index a5a6efe8..acb84e9c 100644 --- a/components/checkbox/src/CheckBox.tsx +++ b/components/checkbox/src/CheckBox.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef } from 'react'; +import React, { JSX, forwardRef } from 'react'; import { ICheckBoxProps } from './CheckBox.types'; import getDefaultCheckBoxClassNames from './utilities/getDefaultCheckBoxClassNames'; diff --git a/components/checkbox/src/Checkbox.stories.tsx b/components/checkbox/src/Checkbox.stories.tsx index a25ee533..4dc2810a 100644 --- a/components/checkbox/src/Checkbox.stories.tsx +++ b/components/checkbox/src/Checkbox.stories.tsx @@ -1,4 +1,4 @@ -import React, { ComponentType, useState } from 'react'; +import React, { ComponentType, JSX, useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { ICheckBoxProps } from './CheckBox.types'; diff --git a/components/checkbox/src/partials/CheckBoxContainer.tsx b/components/checkbox/src/partials/CheckBoxContainer.tsx index a41a06cf..29186b80 100644 --- a/components/checkbox/src/partials/CheckBoxContainer.tsx +++ b/components/checkbox/src/partials/CheckBoxContainer.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; +import React, { JSX, ReactNode, forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; import { v4 as guid } from 'uuid'; import cn from 'classnames'; diff --git a/components/checkbox/src/partials/CheckBoxIndicator.tsx b/components/checkbox/src/partials/CheckBoxIndicator.tsx index 428ffd7f..4badbad2 100644 --- a/components/checkbox/src/partials/CheckBoxIndicator.tsx +++ b/components/checkbox/src/partials/CheckBoxIndicator.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { JSX } from 'react'; import cn from 'classnames'; import { ICheckBoxIndicatorClassNames } from '../CheckBox.types'; diff --git a/components/checkbox/src/partials/CheckBoxLabel.tsx b/components/checkbox/src/partials/CheckBoxLabel.tsx index b8678645..0258b904 100644 --- a/components/checkbox/src/partials/CheckBoxLabel.tsx +++ b/components/checkbox/src/partials/CheckBoxLabel.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { JSX, ReactNode } from 'react'; import cn from 'classnames'; import { ICheckBoxLabelClassNames } from '../CheckBox.types'; diff --git a/package-lock.json b/package-lock.json index 42fe857c..3b3249f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,6 +66,18 @@ "react-dom": "^17.0.2" } }, + "components/checkbox": { + "name": "@byndyusoft-ui/checkbox", + "version": "0.0.1", + "license": "Apache-2.0", + "devDependencies": { + "react-hook-form": "^7.43.9" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "components/flex": { "name": "@byndyusoft-ui/flex", "version": "0.0.1", @@ -650,6 +662,10 @@ "node": ">=6.9.0" } }, + "node_modules/@byndyusoft-ui/checkbox": { + "resolved": "components/checkbox", + "link": true + }, "node_modules/@byndyusoft-ui/css-utilities": { "resolved": "styles/utilities", "link": true @@ -8577,6 +8593,23 @@ "react-dom": ">= 16.3" } }, + "node_modules/react-hook-form": { + "version": "7.56.4", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.56.4.tgz", + "integrity": "sha512-Rob7Ftz2vyZ/ZGsQZPaRdIefkgOSrQSPXfqBdvOPwJfoGnjwRJUs7EM7Kc1mcoDv3NOtqBzPGbcMB8CGn9CKgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "17.0.2", "dev": true, From de4e1ef256e282b70140bef439aa55d1fb42f5a5 Mon Sep 17 00:00:00 2001 From: tolms Date: Fri, 30 May 2025 12:51:34 +0500 Subject: [PATCH 4/5] feat(checkbox): corrected package.json --- components/checkbox/package.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/checkbox/package.json b/components/checkbox/package.json index 60986c7d..93fad912 100644 --- a/components/checkbox/package.json +++ b/components/checkbox/package.json @@ -20,8 +20,14 @@ "scripts": { "build": "tsc --project tsconfig.build.json", "clean": "rimraf dist", - "lint": "eslint src --config ../../eslint.config.js", - "test": "jest --config ../../jest.config.js --roots components/checkbox/src" + "lint:check": "npm run eslint:check && npm run prettier:check && npm run stylelint:check", + "lint:fix": "npm run eslint:fix && npm run prettier:fix && npm run stylelint:fix", + "eslint:check": "eslint src --config ../../eslint.config.js", + "eslint:fix": "eslint src --config ../../eslint.config.js --fix", + "prettier:check": "prettier --check '**/*.{ts,tsx,css,scss,json}' '!**/dist/**'", + "prettier:fix": "prettier --write '**/*.{ts,tsx,css,scss,json}' '!**/dist/**'", + "stylelint:check": "stylelint '**/*.{css,scss}' --allow-empty-input", + "stylelint:fix": "stylelint '**/*.{css,scss}' --fix --allow-empty-input" }, "bugs": { "url": "https://github.com/Byndyusoft/ui/issues" From 0d8bb00f235cee2ac5a73881fc11a5493b6fd9d9 Mon Sep 17 00:00:00 2001 From: tolms Date: Fri, 30 May 2025 12:52:37 +0500 Subject: [PATCH 5/5] feat(checkbox): corrected tests --- components/checkbox/src/CheckBox.tests.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/checkbox/src/CheckBox.tests.tsx b/components/checkbox/src/CheckBox.tests.tsx index 2ae9fb63..4e0e60d6 100644 --- a/components/checkbox/src/CheckBox.tests.tsx +++ b/components/checkbox/src/CheckBox.tests.tsx @@ -32,7 +32,7 @@ const Form = ({ defaultValues, onSubmit }: IFormProps): JSX.Element => { describe('components/CheckBox', () => { test('should render children', () => { - const onChange = jest.fn(); + const onChange = vi.fn(); render( @@ -44,7 +44,7 @@ describe('components/CheckBox', () => { }); test('should render checked checkbox', () => { - const onChange = jest.fn(); + const onChange = vi.fn(); render( @@ -56,7 +56,7 @@ describe('components/CheckBox', () => { }); test('should render unchecked checkbox', () => { - const onChange = jest.fn(); + const onChange = vi.fn(); render( @@ -68,7 +68,7 @@ describe('components/CheckBox', () => { }); test('should render disabled checkbox', () => { - const onChange = jest.fn(); + const onChange = vi.fn(); render( @@ -80,7 +80,7 @@ describe('components/CheckBox', () => { }); test('should render indeterminate checkbox', () => { - const onChange = jest.fn(); + const onChange = vi.fn(); render( @@ -92,7 +92,7 @@ describe('components/CheckBox', () => { }); test('should works with react-hook-form correctly', async () => { - const onSubmit = jest.fn(); + const onSubmit = vi.fn(); const defaultValues = { isChecked: true };