diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index f06235c..0000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -dist diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index f68e0e7..0000000 --- a/.eslintrc +++ /dev/null @@ -1,55 +0,0 @@ -{ - "parser": "@typescript-eslint/parser", - "extends": [ - "eslint:recommended", - "plugin:import/recommended", - "plugin:react/recommended", - "plugin:prettier/recommended" - // "plugin:@typescript-eslint/recommended" - ], - "settings": { - "react": { - "version": "detect" - }, - "import/ignore": ["react-native"], - "import/resolver": { - "node": { - "extensions": [".js", ".ts", ".tsx"] - } - } - }, - "parserOptions": { - "ecmaVersion": 6, - "sourceType": "module", - "ecmaFeatures": { - "jsx": true, - "experimentalObjectRestSpread": true - } - }, - "env": { - "browser": true, - "mocha": true, - "node": true, - "jest/globals": true - }, - "rules": { - "valid-jsdoc": 2, - "react/jsx-uses-react": 1, - "react/jsx-no-undef": 2, - "react/jsx-wrap-multilines": 2, - "react/no-string-refs": 0, - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": "off", - "no-redeclare": "off", - "@typescript-eslint/no-redeclare": ["error"], - "jest/no-disabled-tests": "warn", - "jest/no-focused-tests": "error", - "jest/no-identical-title": "error", - "jest/prefer-to-have-length": "warn", - "jest/valid-expect": "error" - }, - "plugins": ["jest", "@typescript-eslint", "import", "react"], - "globals": { - "JSX": true - } -} diff --git a/.prettierrc b/.prettierrc index 75a8109..db53a3e 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,5 +3,6 @@ "printWidth": 140, "tabWidth": 4, "singleQuote": true, - "trailingComma": "all" + "trailingComma": "all", + "endOfLine": "lf" } diff --git a/docs/code_of_conduct.md b/CODE_OF_CONDUCT.md similarity index 100% rename from docs/code_of_conduct.md rename to CODE_OF_CONDUCT.md diff --git a/README.md b/README.md index c8e7d0b..ef97ff2 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,23 @@ ![Eventrix](assets/logo_br.svg) -STATE MANAGMENT FOR **REACT** AND **REACT NATIVE** APPS +State Management for **React** and **React Native** Applications [![Build Status](https://travis-ci.org/rstgroup/eventrix.svg?branch=master)](https://travis-ci.org/rstgroup/eventrix) [![npm](https://img.shields.io/npm/l/eventrix.svg)](https://npmjs.org/package/eventrix) [![npm](https://img.shields.io/npm/v/eventrix.svg)](https://npmjs.org/package/eventrix) -### Support +# Support - React - React Native - SSR (Next.js) -### Installation +# Installation ```bash $ npm install eventrix --save ``` -### Documentation +# Documentation [**Get started**](https://eventrix.gitbook.io/eventrix/getting-started) | @@ -27,14 +27,15 @@ $ npm install eventrix --save | [**Demo**](https://eventrix.gitbook.io/eventrix/demo) -### Home page +# Home page We have website dedicated to eventrix. Go to [**eventrix.io**](https://eventrix.io) and see what eventrix has to offer. -### Quickstart +# Quickstart -eventrixStore.js ```js +// eventrixStore.js + import { Eventrix } from 'eventrix'; import receiversList from './receivers'; @@ -46,8 +47,9 @@ const eventrixStore = new Eventrix(initialState, receiversList); export default eventrixStore; ``` -App.jsx ```jsx harmony +// App.tsx + import { StrictMode } from "react"; import ReactDOM from "react-dom"; import { EventrixProvider } from 'eventrix'; @@ -64,8 +66,9 @@ ReactDOM.render( ); ``` -UsersList.jsx ```jsx harmony +// UsersList.jsx + import React, { useCallback } from 'react'; import { useEventrixState, useEmit } from 'eventrix'; @@ -88,8 +91,9 @@ const UsersList = () => { } ``` -receivers.js ```js +// receivers.js + import axios from 'axios'; import { EventsReceiver } from 'eventrix'; @@ -105,7 +109,9 @@ const receiversList = [fetchUsersReceiver]; export default receiversList; ``` -### About +For more usage examples see the `examples` directory. + +# About [Eventrix](https://eventrix.io/) is a scaling and predictable JS library for state managing and centralizing application global state. @@ -131,17 +137,32 @@ Greater control of data flow thanks to additional tools (devtools) and a small t [Eventrix DevTools](https://github.com/rstgroup/eventrix-devtools) -### Contribute +# Contribute - use eslint rules - write clean code - unit tests (min 85% of your code should be tested) -- [code of conduct](https://github.com/rstgroup/eventrix/blob/master/docs/code_of_conduct.md) -### License +## Development + +To start development, clone the repository and install the dependencies. Then you can develop the library using the example application. + +```bash +$ git clone https://github.com/rstgroup/eventrix.git +$ npm install + +# Run the example application +cd examples/todo-list +npm install +npm run dev +``` + +For more example applications see the `examples` directory. + +# License eventrix package are [MIT licensed](https://github.com/rstgroup/eventrix/blob/master/LICENSE) -### Powered by +# Powered by -[RST Software Masters](https://rst.software) look on RST [Github](https://github.com/rstgroup) +[RST Software](https://rst.software), see our [Github](https://github.com/rstgroup) diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..c463772 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,48 @@ +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import jsdoc from 'eslint-plugin-jsdoc'; +import reactPlugin from 'eslint-plugin-react'; + +export default tseslint.config(eslint.configs.recommended, tseslint.configs.recommended, { + settings: { + react: { + version: 'detect', + }, + 'import/ignore': ['react-native'], + 'import/resolver': { + node: { + extensions: ['.js', '.ts', '.tsx'], + }, + }, + }, + files: ['src/**/*.{js,jsx,mjs,cjs,ts,tsx}'], + ignores: ['node_modules', 'dist', 'devtools'], + plugins: { + jsdoc: jsdoc, + react: reactPlugin, + }, + languageOptions: { + parser: tseslint.parser, + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + ecmaFeatures: { + jsx: true, + experimentalObjectRestSpread: true, + }, + }, + }, + rules: { + 'linebreak-style': ['error', 'unix'], + 'react/jsx-uses-react': 1, + 'react/jsx-no-undef': 2, + 'react/jsx-wrap-multilines': 2, + 'react/no-string-refs': 0, + 'jsdoc/no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'off', + 'jsdoc/no-redeclare': 'off', + '@typescript-eslint/no-redeclare': ['error'], + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/ban-ts-comment': 'off', + }, +}); diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..a6df29f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,10 @@ +# Eventrix usage examples + +This directory contains examples of how to use Eventrix in different scenarios. + +To run the example simply run the following commands in each example directory: + +```bash +npm install +npm run dev +``` diff --git a/examples/alert-manager/.gitignore b/examples/alert-manager/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/examples/alert-manager/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/alert-manager/index.html b/examples/alert-manager/index.html new file mode 100644 index 0000000..096ab24 --- /dev/null +++ b/examples/alert-manager/index.html @@ -0,0 +1,12 @@ + + + + + + Eventrix - alert manager + + +
+ + + diff --git a/examples/alert-manager/package.json b/examples/alert-manager/package.json new file mode 100644 index 0000000..8badd7d --- /dev/null +++ b/examples/alert-manager/package.json @@ -0,0 +1,29 @@ +{ + "name": "eexample-alert-manager", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint --ext .ts,.tsx src --fix", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "11.10.5", + "@emotion/styled": "11.10.5", + "@mui/material": "5.10.16", + "@mui/types": "7.2.2", + "axios": "^1.7.7", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.3", + "globals": "^15.11.0", + "typescript": "~5.6.2", + "vite": "^6.0.1" + } +} diff --git a/examples/alert-manager/src/App.tsx b/examples/alert-manager/src/App.tsx new file mode 100644 index 0000000..3e59c74 --- /dev/null +++ b/examples/alert-manager/src/App.tsx @@ -0,0 +1,16 @@ +import Alerts from './components/Alerts'; +import Logo from './components/Logo'; +import Buttons from './components/Buttons'; +import './index.css'; +import React from 'react'; + +export default function App() { + return ( +
+ +

Alerts manager

+ + +
+ ); +} diff --git a/examples/alert-manager/src/appEvents/alerts.ts b/examples/alert-manager/src/appEvents/alerts.ts new file mode 100644 index 0000000..b6c4e1d --- /dev/null +++ b/examples/alert-manager/src/appEvents/alerts.ts @@ -0,0 +1,5 @@ +export const ALERTS_SHOW_SUCCESS = 'Alerts:show.success'; +export const ALERTS_SHOW_ERROR = 'Alerts:show.error'; +export const ALERTS_SHOW_WARNING = 'Alerts:show.warning'; +export const ALERTS_SHOW_INFO = 'Alerts:show.info'; +export const ALERTS_REMOVE = 'Alerts:remove'; diff --git a/examples/alert-manager/src/components/Alerts/Alerts.tsx b/examples/alert-manager/src/components/Alerts/Alerts.tsx new file mode 100644 index 0000000..a6fc685 --- /dev/null +++ b/examples/alert-manager/src/components/Alerts/Alerts.tsx @@ -0,0 +1,42 @@ +import { useEvent } from 'eventrix'; +import React, { useState } from 'react'; +import { Stack } from '@mui/material'; +import { ALERTS_REMOVE, ALERTS_SHOW_ERROR, ALERTS_SHOW_INFO, ALERTS_SHOW_SUCCESS, ALERTS_SHOW_WARNING } from '../../appEvents/alerts'; +import { Alert, AlertEventPayload, AlertTypes } from '../../types/alerts'; +import AlertMessage from './AlertsMessage'; + +const createId = (): string => { + return String(new Date().getTime()); +}; + +const Alerts = () => { + const [alerts, setAlerts] = useState([]); + useEvent(ALERTS_SHOW_SUCCESS, (message: AlertEventPayload) => { + const id = createId(); + setAlerts([{ message, type: AlertTypes.success, id }, ...alerts]); + }); + useEvent(ALERTS_SHOW_WARNING, (message: AlertEventPayload) => { + const id = createId(); + setAlerts([{ message, type: AlertTypes.warning, id }, ...alerts]); + }); + useEvent(ALERTS_SHOW_ERROR, (message: AlertEventPayload) => { + const id = createId(); + setAlerts([{ message, type: AlertTypes.error, id }, ...alerts]); + }); + useEvent(ALERTS_SHOW_INFO, (message: AlertEventPayload) => { + const id = createId(); + setAlerts([{ message, type: AlertTypes.info, id }, ...alerts]); + }); + useEvent(ALERTS_REMOVE, (id: string) => { + setAlerts(alerts.filter((alert) => alert.id !== id)); + }); + return ( + + {alerts.map(({ message, type, id }) => ( + + ))} + + ); +}; + +export default Alerts; diff --git a/examples/alert-manager/src/components/Alerts/AlertsMessage.tsx b/examples/alert-manager/src/components/Alerts/AlertsMessage.tsx new file mode 100644 index 0000000..195f95d --- /dev/null +++ b/examples/alert-manager/src/components/Alerts/AlertsMessage.tsx @@ -0,0 +1,26 @@ +import Alert from '@mui/material/Alert'; +import { useEmit } from 'eventrix'; +import React, { useEffect } from 'react'; +import { ALERTS_REMOVE } from '../../appEvents/alerts'; +import { AlertTypes } from '../../types/alerts'; + +type AlertMessageProps = { + message: string; + type: AlertTypes; + id: string; +}; + +const AlertMessage = ({ message, type, id }: AlertMessageProps) => { + const emit = useEmit(); + useEffect(() => { + const timeout = setTimeout(() => { + emit(ALERTS_REMOVE, id); + }, 5000); + return () => { + clearTimeout(timeout); + }; + }, [emit, id]); + return {message}; +}; + +export default AlertMessage; diff --git a/examples/alert-manager/src/components/Alerts/index.ts b/examples/alert-manager/src/components/Alerts/index.ts new file mode 100644 index 0000000..41c97d3 --- /dev/null +++ b/examples/alert-manager/src/components/Alerts/index.ts @@ -0,0 +1 @@ +export { default } from './Alerts'; diff --git a/examples/alert-manager/src/components/Buttons.tsx b/examples/alert-manager/src/components/Buttons.tsx new file mode 100644 index 0000000..1dc2117 --- /dev/null +++ b/examples/alert-manager/src/components/Buttons.tsx @@ -0,0 +1,48 @@ +import { useEmit } from 'eventrix'; +import Stack from '@mui/material/Stack'; +import Button from '@mui/material/Button'; +import { AlertEventPayload } from '../types/alerts'; +import { ALERTS_SHOW_ERROR, ALERTS_SHOW_INFO, ALERTS_SHOW_SUCCESS, ALERTS_SHOW_WARNING } from '../appEvents/alerts'; +import React from 'react'; + +const Buttons = () => { + const emit = useEmit(); + return ( + + + + + + + ); +}; + +export default Buttons; diff --git a/examples/alert-manager/src/components/Logo.tsx b/examples/alert-manager/src/components/Logo.tsx new file mode 100644 index 0000000..84de0d4 --- /dev/null +++ b/examples/alert-manager/src/components/Logo.tsx @@ -0,0 +1,9 @@ +import React from 'react'; + +const Logo = () => ( +
+ Eventrix +
+); + +export default Logo; diff --git a/examples/alert-manager/src/eventrixStore.ts b/examples/alert-manager/src/eventrixStore.ts new file mode 100644 index 0000000..7e18b07 --- /dev/null +++ b/examples/alert-manager/src/eventrixStore.ts @@ -0,0 +1,5 @@ +import Eventrix from 'eventrix'; + +const eventrixStore = new Eventrix(); + +export default eventrixStore; diff --git a/examples/alert-manager/src/index.css b/examples/alert-manager/src/index.css new file mode 100644 index 0000000..c9ed2d8 --- /dev/null +++ b/examples/alert-manager/src/index.css @@ -0,0 +1,4 @@ +.App { + font-family: sans-serif; + text-align: center; +} diff --git a/examples/alert-manager/src/main.tsx b/examples/alert-manager/src/main.tsx new file mode 100644 index 0000000..55de41e --- /dev/null +++ b/examples/alert-manager/src/main.tsx @@ -0,0 +1,14 @@ +import React, { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import App from './App'; +import { EventrixProvider } from 'eventrix'; +import eventrixStore from './eventrixStore'; + +createRoot(document.getElementById('root')!).render( + + + + + , +); diff --git a/examples/alert-manager/src/types/alerts.ts b/examples/alert-manager/src/types/alerts.ts new file mode 100644 index 0000000..8c432f2 --- /dev/null +++ b/examples/alert-manager/src/types/alerts.ts @@ -0,0 +1,14 @@ +export enum AlertTypes { + error = 'error', + warning = 'warning', + success = 'success', + info = 'info', +} + +export type Alert = { + message: string; + type: AlertTypes; + id: string; +}; + +export type AlertEventPayload = string; diff --git a/examples/alert-manager/tsconfig.json b/examples/alert-manager/tsconfig.json new file mode 100644 index 0000000..45a7d45 --- /dev/null +++ b/examples/alert-manager/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "paths": { + "eventrix": ["../../src/index.ts"] + }, + "baseUrl": "./", + "rootDir": "../../", + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Node", + "jsx": "react-jsx", + "types": ["vite/client", "node", "react"], + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "allowJs": true, + "allowSyntheticDefaultImports": true, + "useDefineForClassFields": true, + "strict": true, + "skipLibCheck": false, + "sourceMap": true, + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "noEmit": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": false, + "forceConsistentCasingInFileNames": true + + }, +} diff --git a/examples/alert-manager/vite.config.ts b/examples/alert-manager/vite.config.ts new file mode 100644 index 0000000..c7f2182 --- /dev/null +++ b/examples/alert-manager/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + eventrix: path.join(__dirname, '../../src/index.ts'), + }, + }, +}); diff --git a/examples/forms/.gitignore b/examples/forms/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/examples/forms/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/forms/index.html b/examples/forms/index.html new file mode 100644 index 0000000..1dd75e4 --- /dev/null +++ b/examples/forms/index.html @@ -0,0 +1,12 @@ + + + + + + Eventrix - Form + + +
+ + + diff --git a/examples/forms/package.json b/examples/forms/package.json new file mode 100644 index 0000000..a21e227 --- /dev/null +++ b/examples/forms/package.json @@ -0,0 +1,29 @@ +{ + "name": "example-forms", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint --ext .ts,.tsx src --fix", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "11.10.5", + "@emotion/styled": "11.10.5", + "@mui/material": "5.10.16", + "@mui/types": "7.2.2", + "axios": "^1.7.7", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.3", + "globals": "^15.11.0", + "typescript": "~5.6.2", + "vite": "^6.0.1" + } +} diff --git a/examples/forms/src/App.tsx b/examples/forms/src/App.tsx new file mode 100644 index 0000000..914b410 --- /dev/null +++ b/examples/forms/src/App.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { EventrixProvider } from 'eventrix'; +import eventrix from './services/Eventrix'; +import Logo from './Logo'; +import './index.css'; +import UsersModule from './modules/Users/Users'; + +export default function App() { + return ( + +
+ + +
+
+ ); +} diff --git a/examples/forms/src/Logo.tsx b/examples/forms/src/Logo.tsx new file mode 100644 index 0000000..84de0d4 --- /dev/null +++ b/examples/forms/src/Logo.tsx @@ -0,0 +1,9 @@ +import React from 'react'; + +const Logo = () => ( +
+ Eventrix +
+); + +export default Logo; diff --git a/examples/forms/src/index.css b/examples/forms/src/index.css new file mode 100644 index 0000000..65e7708 --- /dev/null +++ b/examples/forms/src/index.css @@ -0,0 +1,82 @@ +.App { + font-family: sans-serif; + text-align: center; +} + +.logo { + text-align: left; + margin-bottom: 15px; +} + +.logo img { + height: 80px; +} + +.usersList { + display: block; + margin-top: 15px; +} + +.usersItem { + display: flex; + border-top: 1px solid #ddd; +} + +.userItemColl { + padding: 15px; + width: 120px; +} + +.usersListHeader { + display: flex; +} + +.usersListHeaderColl { + padding: 15px; + background: #333; + color: #fff; + width: 120px; +} + +.usersListPlaceholder { + padding: 50px; +} + +.inputWrapper { + text-align: left; + margin-top: 10px; +} + +.inputWrapper label { + display: inline-block; + margin-right: 10px; + text-align: left; + padding-left: 5px; + width: 100px; +} + +.inputWrapper input { + padding: 6px 10px; + border: 1px solid #ddd; + border-radius: 4px; +} + +.formContainer { + display: flex; +} + +.formContainer button { + margin-top: 15px; +} + +.formContainer > div { + width: 45%; +} + +.userPreview { + padding: 20px; + background: #ddd; + width: 100%; + height: 90%; + text-align: left; +} diff --git a/examples/forms/src/main.tsx b/examples/forms/src/main.tsx new file mode 100644 index 0000000..1fdb340 --- /dev/null +++ b/examples/forms/src/main.tsx @@ -0,0 +1,10 @@ +import React, { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import App from './App'; + +createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/forms/src/modules/Users/Form/Form.tsx b/examples/forms/src/modules/Users/Form/Form.tsx new file mode 100644 index 0000000..3363210 --- /dev/null +++ b/examples/forms/src/modules/Users/Form/Form.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { useEmit } from 'eventrix'; +import Input from './Input'; +import UserPreview from './UserPreview'; + +const CREATE_USER_EVENT_NAME = 'users:createUser'; +export const CHANGE_PREVIEW_COLOR = 'changePreviewColor'; + +const UserForm = () => { + const emit = useEmit(); + const onSubmit = (e) => { + e.preventDefault(); + emit(CREATE_USER_EVENT_NAME); + }; + return ( +
+
+
+ + + + + + + + +
+ + +
+
+ +
+
+ ); +}; + +export default UserForm; diff --git a/examples/forms/src/modules/Users/Form/Input.tsx b/examples/forms/src/modules/Users/Form/Input.tsx new file mode 100644 index 0000000..1560402 --- /dev/null +++ b/examples/forms/src/modules/Users/Form/Input.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { useEventrixState } from 'eventrix'; + +interface InputProps { + name: string; + label?: string; + placeholder: string; +} + +const Input: React.FC = ({ name, label, placeholder }) => { + const [value, setValue] = useEventrixState(`user.${name}`); + return ( +
+ + setValue(e.target.value)} placeholder={placeholder} /> +
+ ); +}; + +export default Input; diff --git a/examples/forms/src/modules/Users/Form/UserPreview.tsx b/examples/forms/src/modules/Users/Form/UserPreview.tsx new file mode 100644 index 0000000..59a176f --- /dev/null +++ b/examples/forms/src/modules/Users/Form/UserPreview.tsx @@ -0,0 +1,22 @@ +import React, { useState } from 'react'; +import { useEventrixState, useEvent } from 'eventrix'; +import { CHANGE_PREVIEW_COLOR } from './Form'; + +const UserPreview = () => { + const [color, setColor] = useState('black'); + const [user] = useEventrixState('user'); + useEvent(CHANGE_PREVIEW_COLOR, (data) => { + setColor(data.color); + }); + return ( +
+ + "user" + {' '} + state in eventrix store: +
{JSON.stringify(user, null, 2)}
+
+ ); +}; + +export default UserPreview; diff --git a/examples/forms/src/modules/Users/List/Item.tsx b/examples/forms/src/modules/Users/List/Item.tsx new file mode 100644 index 0000000..55cc615 --- /dev/null +++ b/examples/forms/src/modules/Users/List/Item.tsx @@ -0,0 +1,28 @@ +import React from 'react'; + +interface UsersItemProps { + id: number; + username: string; + name: string; + surname: string; + country: string; + city: string; + street: string; + phone: string; +} + +const UsersItem: React.FC = ({ username, name, surname, country, city, street, phone }) => { + return ( +
+
{username}
+
{name}
+
{surname}
+
{country}
+
{city}
+
{street}
+
{phone}
+
+ ); +}; + +export default UsersItem; diff --git a/examples/forms/src/modules/Users/List/List.tsx b/examples/forms/src/modules/Users/List/List.tsx new file mode 100644 index 0000000..d6c12b1 --- /dev/null +++ b/examples/forms/src/modules/Users/List/List.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { useEventrixState } from 'eventrix'; +import UserItem from './Item'; + +const UsersList = () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const [users] = useEventrixState('users'); + return ( +
+
+
username
+
name
+
surname
+
country
+
city
+
street
+
phone
+
+ {users.length === 0 &&
Users list is empty
} + {users.map((user) => ( + + ))} +
+ ); +}; + +export default UsersList; diff --git a/examples/forms/src/modules/Users/Users.tsx b/examples/forms/src/modules/Users/Users.tsx new file mode 100644 index 0000000..3592091 --- /dev/null +++ b/examples/forms/src/modules/Users/Users.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import UsersList from './List/List'; +import UserForm from './Form/Form'; + +const UsersModule = () => { + return ( +
+ + +
+ ); +}; + +export default UsersModule; diff --git a/examples/forms/src/services/Eventrix/index.ts b/examples/forms/src/services/Eventrix/index.ts new file mode 100644 index 0000000..4c03b0e --- /dev/null +++ b/examples/forms/src/services/Eventrix/index.ts @@ -0,0 +1,11 @@ +import { Eventrix } from 'eventrix'; +import usersEventsReceiver from './users'; + +const initialState = { + user: {}, + users: [], +}; + +const eventsReceivers = [usersEventsReceiver]; + +export default new Eventrix(initialState, eventsReceivers); diff --git a/examples/forms/src/services/Eventrix/users.ts b/examples/forms/src/services/Eventrix/users.ts new file mode 100644 index 0000000..fd8c98d --- /dev/null +++ b/examples/forms/src/services/Eventrix/users.ts @@ -0,0 +1,12 @@ +import { EventsReceiver } from 'eventrix'; + +const CREATE_USER_EVENT_NAME = 'users:createUser'; + +const usersEventsReceiver = new EventsReceiver(CREATE_USER_EVENT_NAME, (eventName, eventData, storeManager) => { + const user = storeManager.getState('user'); + const users = storeManager.getState('users'); + storeManager.setState('users', [user, ...users]); + storeManager.setState('user', {}); +}); + +export default usersEventsReceiver; diff --git a/examples/forms/tsconfig.json b/examples/forms/tsconfig.json new file mode 100644 index 0000000..45a7d45 --- /dev/null +++ b/examples/forms/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "paths": { + "eventrix": ["../../src/index.ts"] + }, + "baseUrl": "./", + "rootDir": "../../", + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Node", + "jsx": "react-jsx", + "types": ["vite/client", "node", "react"], + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "allowJs": true, + "allowSyntheticDefaultImports": true, + "useDefineForClassFields": true, + "strict": true, + "skipLibCheck": false, + "sourceMap": true, + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "noEmit": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": false, + "forceConsistentCasingInFileNames": true + + }, +} diff --git a/examples/forms/vite.config.ts b/examples/forms/vite.config.ts new file mode 100644 index 0000000..c7f2182 --- /dev/null +++ b/examples/forms/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + eventrix: path.join(__dirname, '../../src/index.ts'), + }, + }, +}); diff --git a/examples/todo-list/.gitignore b/examples/todo-list/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/examples/todo-list/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/todo-list/index.html b/examples/todo-list/index.html new file mode 100644 index 0000000..88d775a --- /dev/null +++ b/examples/todo-list/index.html @@ -0,0 +1,12 @@ + + + + + + Eventrix - To Do List + + +
+ + + diff --git a/examples/todo-list/package.json b/examples/todo-list/package.json new file mode 100644 index 0000000..b086c73 --- /dev/null +++ b/examples/todo-list/package.json @@ -0,0 +1,29 @@ +{ + "name": "example-todo-list", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint --ext .ts,.tsx src --fix", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "11.10.5", + "@emotion/styled": "11.10.5", + "@mui/material": "5.10.16", + "@mui/types": "7.2.2", + "axios": "^1.7.7", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.3", + "globals": "^15.11.0", + "typescript": "~5.6.2", + "vite": "^6.0.1" + } +} diff --git a/examples/todo-list/src/App.tsx b/examples/todo-list/src/App.tsx new file mode 100644 index 0000000..5388546 --- /dev/null +++ b/examples/todo-list/src/App.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { EventrixProvider } from 'eventrix'; +import eventrix from './eventrix/index'; +import './index.css'; +import Logo from './components/Logo'; +import CreateTaskForm from './components/CreateTaskForm/CreateTaskForm'; +import TodoList from './components/TodoList/TodoList'; +import TodoFooter from './components/TodoFooter'; + +export default function App() { + return ( + +
+ +

TO DO LIST

+
+ + + +
+
+
+ ); +} diff --git a/examples/todo-list/src/components/CreateTaskForm/CreateTaskForm.css b/examples/todo-list/src/components/CreateTaskForm/CreateTaskForm.css new file mode 100644 index 0000000..a49a9c3 --- /dev/null +++ b/examples/todo-list/src/components/CreateTaskForm/CreateTaskForm.css @@ -0,0 +1,36 @@ +.taskFormWrapper { + display: block; + width: 100%; + background: #efefef; + padding: 10px; + text-align: left; + margin-left: auto; + margin-right: auto; + border-radius: 4px 4px 0 0; +} + +.taskFormWrapper form { + display: flex; +} + +.taskFormWrapper input { + width: 100%; + padding: 8px 10px; + border-radius: 4px 0 0 4px; + border: 1px solid #909090; + border-right: 0; +} + +.taskFormWrapper button { + width: 120px; + padding: 8px 10px; + border-radius: 0 4px 4px 0; + background: #333; + color: #fff; + border: 1px solid #333; + cursor: pointer; +} + +.taskFormWrapper button:hover { + background: #000; +} diff --git a/examples/todo-list/src/components/CreateTaskForm/CreateTaskForm.tsx b/examples/todo-list/src/components/CreateTaskForm/CreateTaskForm.tsx new file mode 100644 index 0000000..f90ab3c --- /dev/null +++ b/examples/todo-list/src/components/CreateTaskForm/CreateTaskForm.tsx @@ -0,0 +1,35 @@ +import React, { useState } from 'react'; +import { CREATE_TASK_EVENT_NAME } from '../../eventrix/tasks'; +import { makeid } from '../../utils/helpers'; +import './CreateTaskForm.css'; +import { useEmit } from 'eventrix'; + +const CreateTaskForm: React.FC = () => { + const emit = useEmit(); + const [title, setTitle] = useState(''); + + const onSubmit = (e) => { + e.preventDefault(); + if (title) { + const task = { + id: makeid(), + title, + status: 'todo', + }; + emit(CREATE_TASK_EVENT_NAME, { task }); + setTitle(''); + return; + } + }; + + return ( +
+
+ setTitle(e.target.value)} value={title} /> + +
+
+ ); +}; + +export default CreateTaskForm; diff --git a/examples/todo-list/src/components/Logo.tsx b/examples/todo-list/src/components/Logo.tsx new file mode 100644 index 0000000..84de0d4 --- /dev/null +++ b/examples/todo-list/src/components/Logo.tsx @@ -0,0 +1,9 @@ +import React from 'react'; + +const Logo = () => ( +
+ Eventrix +
+); + +export default Logo; diff --git a/examples/todo-list/src/components/Task.tsx b/examples/todo-list/src/components/Task.tsx new file mode 100644 index 0000000..4ee93a8 --- /dev/null +++ b/examples/todo-list/src/components/Task.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { MARK_TASK_AS_DONE_EVENT_NAME, MARK_TASK_AS_TODO_EVENT_NAME, REMOVE_TASK_EVENT_NAME } from '../eventrix/tasks'; +import { useEmit } from 'eventrix'; + +interface TaskProps { + task: { + id: string; + title: string; + status: string; + }; +} + +const Task: React.FC = ({ task }) => { + const emit = useEmit(); + return ( +
+ emit(task.status === 'todo' ? MARK_TASK_AS_DONE_EVENT_NAME : MARK_TASK_AS_TODO_EVENT_NAME, { id: task.id })} + /> +
{task.title}
+
{task.status}
+ +
+ ); +}; + +export default Task; diff --git a/examples/todo-list/src/components/TodoFooter.tsx b/examples/todo-list/src/components/TodoFooter.tsx new file mode 100644 index 0000000..bdbb30b --- /dev/null +++ b/examples/todo-list/src/components/TodoFooter.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import './TodoList/TodoList.css'; +import { useEventrixState } from 'eventrix'; + +const TodoFooter: React.FC = () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const [tasks] = useEventrixState('tasks'); + const [statusFilter] = useEventrixState('filter.status'); + const tasksList = statusFilter ? tasks.filter((task) => task.status === statusFilter) : tasks; + return
Tasks count: {tasksList.length}
; +}; + +export default TodoFooter; diff --git a/examples/todo-list/src/components/TodoList/TodoList.css b/examples/todo-list/src/components/TodoList/TodoList.css new file mode 100644 index 0000000..ef7bd68 --- /dev/null +++ b/examples/todo-list/src/components/TodoList/TodoList.css @@ -0,0 +1,102 @@ +.list { + width: 350px; + margin-left: auto; + margin-right: auto; +} + +.list-item { + text-align: left; + display: flex; + align-items: center; + padding: 10px; + background: #ddd; + width: 100%; + border-top: 1px solid #c1c1c1; +} + +.task-title { + width: 100%; + display: inline-block; +} + +.list-item input { + display: inline-block; + width: 40px; + height: 25px; + margin-right: 10px; +} + +.list-item button { + width: 120px; + margin-right: 15px; +} + +.status { + background: #333; + color: #fff; + display: inline-block; + padding: 5px 8px; + border-radius: 4px; + margin-left: 10px; + width: 100px; + text-align: center; +} + +.todo { + background: #333; + color: #fff; +} + +.done { + background: green; + color: #fff; +} + +.removeButton { + width: 25px !important; + height: 25px; + display: inline-block; + text-align: center; + background: red; + color: #fff; + border: 1px solid #333; + border-radius: 100%; + margin-left: 10px; + cursor: pointer; + opacity: 0.7; +} + +.removeButton:hover { + opacity: 1; +} + +.filters { + display: flex; + flex-wrap: nowrap; + background: #efefef; + width: 100%; + padding: 0 10px; +} + +.status-button { + display: inline-block; + width: 100%; + padding: 8px 0; + cursor: pointer; +} + +.status-button.active { + border-bottom: 3px solid #4796ff; +} + +.status-button:hover { + background: #ddd; +} + +.list-placeholder { + border-top: 1px solid #c1c1c1; + padding: 20px 10px; + width: 100%; + background: #ddd; + color: #8a8a8a; +} diff --git a/examples/todo-list/src/components/TodoList/TodoList.tsx b/examples/todo-list/src/components/TodoList/TodoList.tsx new file mode 100644 index 0000000..765533a --- /dev/null +++ b/examples/todo-list/src/components/TodoList/TodoList.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import Task from '../Task'; +import './TodoList.css'; +import { useEventrixState } from 'eventrix'; + +const TodoList: React.FC = () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const [tasks] = useEventrixState('tasks'); + const [statusFilter, setStatusFilter] = useEventrixState('filter.status'); + const tasksList = statusFilter ? tasks.filter((task) => task.status === statusFilter) : tasks; + return ( +
+
+
setStatusFilter('')}> + All +
+
setStatusFilter('todo')}> + To do +
+
setStatusFilter('done')}> + Done +
+
+
+ {tasksList.length === 0 &&
Tasks list empty
} + {tasksList.map((task) => ( + + ))} +
+
+ ); +}; + +export default TodoList; diff --git a/examples/todo-list/src/eventrix/index.ts b/examples/todo-list/src/eventrix/index.ts new file mode 100644 index 0000000..d37c800 --- /dev/null +++ b/examples/todo-list/src/eventrix/index.ts @@ -0,0 +1,8 @@ +import { Eventrix } from 'eventrix'; +import taskEventsReceivers from './tasks'; + +const initialState = { + tasks: [], +}; + +export default new Eventrix(initialState, taskEventsReceivers); diff --git a/examples/todo-list/src/eventrix/tasks.ts b/examples/todo-list/src/eventrix/tasks.ts new file mode 100644 index 0000000..8a4d945 --- /dev/null +++ b/examples/todo-list/src/eventrix/tasks.ts @@ -0,0 +1,34 @@ +import findIndex from 'lodash/findIndex'; +import { EventsReceiver } from 'eventrix'; + +export const CREATE_TASK_EVENT_NAME = 'tasks:create'; +export const REMOVE_TASK_EVENT_NAME = 'tasks:remove'; +export const MARK_TASK_AS_DONE_EVENT_NAME = 'tasks:markAsDone'; +export const MARK_TASK_AS_TODO_EVENT_NAME = 'tasks:markAsTodo'; + +const createTaskReceiver = new EventsReceiver(CREATE_TASK_EVENT_NAME, (eventName, { task }, stateManager) => { + const tasks = stateManager.getState('tasks'); + stateManager.setState('tasks', [task, ...tasks]); +}); + +const removeTaskReceiver = new EventsReceiver(REMOVE_TASK_EVENT_NAME, (eventName, { id }, stateManager) => { + const tasks = stateManager.getState('tasks'); + const newTasks = tasks.filter((task) => task.id !== id); + stateManager.setState('tasks', newTasks); +}); + +const markAsDoneReceiver = new EventsReceiver(MARK_TASK_AS_DONE_EVENT_NAME, (eventName, { id }, stateManager) => { + const tasks = stateManager.getState('tasks'); + const taskIndex = findIndex(tasks, (task) => task.id === id); + const task = { ...tasks[taskIndex], status: 'done' }; + stateManager.setState(`tasks.${taskIndex}`, task); +}); + +const markAsTodoReceiver = new EventsReceiver(MARK_TASK_AS_TODO_EVENT_NAME, (eventName, { id }, stateManager) => { + const tasks = stateManager.getState('tasks'); + const taskIndex = findIndex(tasks, (task) => task.id === id); + const task = { ...tasks[taskIndex], status: 'todo' }; + stateManager.setState(`tasks.${taskIndex}`, task); +}); + +export default [markAsDoneReceiver, markAsTodoReceiver, createTaskReceiver, removeTaskReceiver]; diff --git a/examples/todo-list/src/index.css b/examples/todo-list/src/index.css new file mode 100644 index 0000000..3c5c70b --- /dev/null +++ b/examples/todo-list/src/index.css @@ -0,0 +1,27 @@ +.App { + font-family: sans-serif; + text-align: center; +} + +.logo { + text-align: left; + margin-bottom: 15px; +} + +.logo img { + height: 80px; +} + +.todo-app { + width: 350px; + margin-left: auto; + margin-right: auto; +} + +.todo-footer { + width: 100%; + background: #efefef; + border-radius: 0 0 4px 4px; + border-top: 1px solid #c1c1c1; + padding: 10px; +} diff --git a/examples/todo-list/src/main.tsx b/examples/todo-list/src/main.tsx new file mode 100644 index 0000000..1fdb340 --- /dev/null +++ b/examples/todo-list/src/main.tsx @@ -0,0 +1,10 @@ +import React, { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import App from './App'; + +createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/todo-list/src/utils/helpers.ts b/examples/todo-list/src/utils/helpers.ts new file mode 100644 index 0000000..c894a4c --- /dev/null +++ b/examples/todo-list/src/utils/helpers.ts @@ -0,0 +1,4 @@ +let id = 0; +export const makeid = () => { + return id++; +}; diff --git a/examples/todo-list/tsconfig.json b/examples/todo-list/tsconfig.json new file mode 100644 index 0000000..45a7d45 --- /dev/null +++ b/examples/todo-list/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "paths": { + "eventrix": ["../../src/index.ts"] + }, + "baseUrl": "./", + "rootDir": "../../", + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Node", + "jsx": "react-jsx", + "types": ["vite/client", "node", "react"], + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "allowJs": true, + "allowSyntheticDefaultImports": true, + "useDefineForClassFields": true, + "strict": true, + "skipLibCheck": false, + "sourceMap": true, + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "noEmit": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": false, + "forceConsistentCasingInFileNames": true + + }, +} diff --git a/examples/todo-list/vite.config.ts b/examples/todo-list/vite.config.ts new file mode 100644 index 0000000..c7f2182 --- /dev/null +++ b/examples/todo-list/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + eventrix: path.join(__dirname, '../../src/index.ts'), + }, + }, +}); diff --git a/package.json b/package.json index b071094..7f69c3d 100644 --- a/package.json +++ b/package.json @@ -2,18 +2,27 @@ "name": "eventrix", "version": "2.10.2", "description": "A system for managing the state of the application based on the events broadcast in the application", - "main": "dist/index.js", - "repository": "https://github.com/rstgroup/eventrix", + "main": "dist/index.cjs.js", + "module": "dist/index.es.js", + "types": "dist/index.d.ts", + "type": "module", + "repository": { + "type": "git", + "url": "git+https://github.com/rstgroup/eventrix.git" + }, + "files": [ + "dist" + ], "homepage": "https://eventrix.js.org", "scripts": { - "test": "jest --runInBand", - "test:watch": "jest --watch", - "test:clean": "jest --clearCache", - "build": "webpack --config webpack-prod.config.js --stats-error-details", - "coverage": "jest --coverage --runInBand", + "test": "vitest run", + "test:watch": "vitest", + "test:clean": "vitest --clearCache", + "build": "vite build", + "coverage": "vitest run --coverage", "coverage:report": "cat ./coverage/lcov.info | coveralls", - "lint": "eslint src --ext ts,tsx,js", - "lint:fix": "eslint src --ext ts,tsx,js --fix " + "lint": "eslint", + "lint:fix": "eslint --fix" }, "pre-commit": [ "lint", @@ -31,6 +40,7 @@ "use-sync-external-store": "^1.0.0" }, "devDependencies": { + "@eslint/js": "^9.16.0", "@babel/cli": "^7.7.0", "@babel/core": "^7.7.0", "@babel/plugin-proposal-class-properties": "^7.2.1", @@ -51,34 +61,35 @@ "@babel/traverse": "^7.7.0", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^12.1.15 || ^13.1.1", - "@types/jest": "^27.4.1", "@types/lodash": "^4.14.170", "@types/react": "^17.0.11 || ^18.0.0", "@types/use-sync-external-store": "0.0.3", - "@typescript-eslint/eslint-plugin": "^4.28.0", - "@typescript-eslint/parser": "^4.28.0", + "@typescript-eslint/eslint-plugin": "^8.17.0", + "@typescript-eslint/parser": "^8.17.0", + "@vitejs/plugin-react": "^4.3.4", + "@vitest/coverage-v8": "^2.1.8", "babel-eslint": "^10.1.0", "babel-jest": "^27.5.1", "babel-loader": "^8.0.6", "clean-webpack-plugin": "^4.0.0", "coveralls": "^3.1.0", - "eslint": "^7.32.0", - "eslint-config-prettier": "^6.14.0", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-jest": "^25.0.1", - "eslint-plugin-prettier": "^3.1.4", - "eslint-plugin-react": "^7.21.5", + "eslint": "^9.16.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsdoc": "^50.6.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-react": "^7.37.2", "jest": "^27.5.1", "loader-utils": "^2.0.0", "pre-commit": "^1.2.2", - "prettier": "^2.4.1", - "ts-jest": "^27.1.4", + "prettier": "^3.4.2", "ts-loader": "^9.2.3", - "typescript": "^4.2.4", - "webpack": "^5.31.2", - "webpack-cli": "^4.6.0" + "typescript": "~5.6.2", + "typescript-eslint": "^8.17.0", + "vite": "^6.0.1", + "vite-plugin-dts": "^4.3.0", + "vitest": "^2.1.8" }, - "types": "./types/index.d.ts", "jest": { "testEnvironment": "jsdom", "moduleFileExtensions": [ @@ -96,5 +107,12 @@ } }, "collectCoverage": true + }, + "directories": { + "lib": "dist", + "test": "__tests__" + }, + "bugs": { + "url": "https://github.com/rstgroup/eventrix/issues" } } diff --git a/src/Eventrix.test.ts b/src/Eventrix.test.ts index 9d39386..d5ac954 100644 --- a/src/Eventrix.test.ts +++ b/src/Eventrix.test.ts @@ -24,9 +24,9 @@ describe('Eventrix', () => { }, }, }; - mockReceiver = jest.fn(() => 'testReceiverData'); + mockReceiver = vi.fn(() => 'testReceiverData'); eventsReceivers = [new EventsReceiver('getFoo', mockReceiver)]; - mockListener = jest.fn(); + mockListener = vi.fn(); eventrix = new Eventrix(initialState, eventsReceivers); }); it('should getState from store', () => { @@ -84,14 +84,14 @@ describe('Eventrix', () => { expect(eventrix.getState('bar')).toEqual(newbarState); }); it('should create new eventrix instance with event scope', () => { - const mockedListener = jest.fn(); + const mockedListener = vi.fn(); eventrix.listen('Test:setTest', mockedListener); const testInstance = eventrix.create({ eventScope: 'Test' }); testInstance.emit('setTest', 'test'); expect(mockedListener).toHaveBeenCalledWith('test', []); }); it('should create new second level eventrix instance with event scope', () => { - const mockedListener = jest.fn(); + const mockedListener = vi.fn(); eventrix.listen('Test:List:setTest', mockedListener); const testInstance = eventrix.create({ eventScope: 'Test' }); const testListInstance = testInstance.create({ eventScope: 'List' }); @@ -141,7 +141,7 @@ describe('Eventrix', () => { const eventsReceiver = new EventsReceiver(eventName, (name, data, store) => { throw 'failedReceiver'; }); - const errorCallback = jest.fn(); + const errorCallback = vi.fn(); eventrix.onError(errorCallback); eventrix.useReceiver(eventsReceiver); return eventrix.emit(eventName, eventData).catch(() => { @@ -157,7 +157,7 @@ describe('Eventrix', () => { const failedListener = () => { throw 'failedListener'; }; - const errorCallback = jest.fn(); + const errorCallback = vi.fn(); eventrix.onError(errorCallback); eventrix.listen(eventName, failedListener); return eventrix.emit(eventName, eventData).catch(() => { diff --git a/src/EventrixDebugger.test.ts b/src/EventrixDebugger.test.ts index 8cf1a4b..3e63488 100644 --- a/src/EventrixDebugger.test.ts +++ b/src/EventrixDebugger.test.ts @@ -17,7 +17,7 @@ describe('EventrixDebugger', () => { foo: 'bar', }, }; - mockReceiver = jest.fn(() => 'testReceiverData'); + mockReceiver = vi.fn(() => 'testReceiverData'); eventsReceivers = [new EventsReceiver('getFoo', mockReceiver)]; eventrix = new Eventrix(initialState, eventsReceivers); eDebugger = new EventrixDebugger(eventrix); diff --git a/src/EventsEmitter.test.ts b/src/EventsEmitter.test.ts index 7c20e35..2e5c2e7 100644 --- a/src/EventsEmitter.test.ts +++ b/src/EventsEmitter.test.ts @@ -3,11 +3,11 @@ import EventsEmitter from './EventsEmitter'; describe('EventsEmitter', () => { describe('listen', () => { const eventsEmitter = new EventsEmitter(); - const mockListener = jest.fn(); - const mockListener2 = jest.fn(); + const mockListener = vi.fn(); + const mockListener2 = vi.fn(); it('should not register listener if is not a function', () => { - console.warn = jest.fn(); + console.warn = vi.fn(); // @ts-ignore eventsEmitter.listen('testEvent', {}); expect(eventsEmitter.listeners.testEvent).toHaveLength(0); @@ -15,21 +15,21 @@ describe('EventsEmitter', () => { }); it('should register listener', () => { - console.warn = jest.fn(); + console.warn = vi.fn(); eventsEmitter.listen('testEvent', mockListener); expect(eventsEmitter.listeners.testEvent).toHaveLength(1); expect(console.warn).not.toBeCalled(); }); it('should not register the same listener two times', () => { - console.warn = jest.fn(); + console.warn = vi.fn(); eventsEmitter.listen('testEvent', mockListener); expect(eventsEmitter.listeners.testEvent).toHaveLength(1); expect(console.warn).toBeCalledWith('EventsEmitter->listen - "testEvent" events listener is already registered'); }); it('should register next listeners if is not the same', () => { - console.warn = jest.fn(); + console.warn = vi.fn(); eventsEmitter.listen('testEvent', mockListener2); expect(eventsEmitter.listeners.testEvent).toHaveLength(2); expect(console.warn).not.toBeCalled(); @@ -37,19 +37,19 @@ describe('EventsEmitter', () => { }); describe('unlisten', () => { const eventsEmitter = new EventsEmitter(); - const mockListener = jest.fn(); - const mockListener2 = jest.fn(); + const mockListener = vi.fn(); + const mockListener2 = vi.fn(); eventsEmitter.listen('testEvent', mockListener); it('should not unregistered listener if event not exist', () => { - console.warn = jest.fn(); + console.warn = vi.fn(); eventsEmitter.unlisten('testEvent3', mockListener); expect(eventsEmitter.listeners.testEvent3).toBeUndefined(); expect(console.warn).toBeCalledWith('EventsEmitter->unlisten - "testEvent3" event not registered'); }); it('should not unregistered listener if event dont have registred listeners', () => { - console.warn = jest.fn(); + console.warn = vi.fn(); // @ts-ignore eventsEmitter.listen('testEvent2', {}); eventsEmitter.unlisten('testEvent2', mockListener); @@ -58,14 +58,14 @@ describe('EventsEmitter', () => { }); it('should not unregistered listener if not exists', () => { - console.warn = jest.fn(); + console.warn = vi.fn(); eventsEmitter.unlisten('testEvent', mockListener2); expect(eventsEmitter.listeners.testEvent).toHaveLength(1); expect(console.warn).toBeCalledWith('EventsEmitter->unlisten - "testEvent" listener not exists'); }); it('should unregistered listener', () => { - console.warn = jest.fn(); + console.warn = vi.fn(); eventsEmitter.unlisten('testEvent', mockListener); expect(eventsEmitter.listeners.testEvent).toBe(undefined); expect(console.warn).not.toBeCalled(); @@ -74,34 +74,34 @@ describe('EventsEmitter', () => { describe('emit', () => { let eventsEmitter = new EventsEmitter(); - let mockListener = jest.fn(); + let mockListener = vi.fn(); let store: any = { state: {}, receivers: {}, - setState: jest.fn(), - getState: jest.fn(), - useReceiver: jest.fn(), - removeReceiver: jest.fn(), - getEventData: jest.fn(), - runListeners: jest.fn(), - emitWild: jest.fn(), + setState: vi.fn(), + getState: vi.fn(), + useReceiver: vi.fn(), + removeReceiver: vi.fn(), + getEventData: vi.fn(), + runListeners: vi.fn(), + emitWild: vi.fn(), runReceivers: () => ['test'], }; beforeEach(() => { store = { state: {}, receivers: {}, - setState: jest.fn(), - getState: jest.fn(), - useReceiver: jest.fn(), - removeReceiver: jest.fn(), - getEventData: jest.fn(), - runListeners: jest.fn(), - emitWild: jest.fn(), + setState: vi.fn(), + getState: vi.fn(), + useReceiver: vi.fn(), + removeReceiver: vi.fn(), + getEventData: vi.fn(), + runListeners: vi.fn(), + emitWild: vi.fn(), runReceivers: () => ['test'], }; eventsEmitter = new EventsEmitter(); - mockListener = jest.fn(); + mockListener = vi.fn(); eventsEmitter.listen('testEvent', mockListener); }); @@ -121,9 +121,9 @@ describe('EventsEmitter', () => { describe('emitWild', () => { it('should call all matched events listeners with data and set matched listeners cache', () => { eventsEmitter.useStore(store); - const fooMockListener = jest.fn(); - const fooBarMockListener = jest.fn(); - const fooBarFooMockListener = jest.fn(); + const fooMockListener = vi.fn(); + const fooBarMockListener = vi.fn(); + const fooBarFooMockListener = vi.fn(); eventsEmitter.listen('foo', fooMockListener); eventsEmitter.listen('foo.bar', fooBarMockListener); eventsEmitter.listen('foo.bar.foo', fooBarFooMockListener); @@ -142,9 +142,9 @@ describe('EventsEmitter', () => { it('should use matched listeners cache when is available', () => { eventsEmitter.useStore(store); - const fooMockListener = jest.fn(); - const fooBarMockListener = jest.fn(); - const fooBarFooMockListener = jest.fn(); + const fooMockListener = vi.fn(); + const fooBarMockListener = vi.fn(); + const fooBarFooMockListener = vi.fn(); eventsEmitter.listen('foo', fooMockListener); eventsEmitter.listen('foo.bar', fooBarMockListener); eventsEmitter.listen('foo.bar.foo', fooBarFooMockListener); @@ -165,13 +165,13 @@ describe('EventsEmitter', () => { const storeWithPromiseReceivers: any = { state: {}, receivers: {}, - setState: jest.fn(), - getState: jest.fn(), - useReceiver: jest.fn(), - removeReceiver: jest.fn(), - getEventData: jest.fn(), - runListeners: jest.fn(), - emitWild: jest.fn(), + setState: vi.fn(), + getState: vi.fn(), + useReceiver: vi.fn(), + removeReceiver: vi.fn(), + getEventData: vi.fn(), + runListeners: vi.fn(), + emitWild: vi.fn(), runReceivers: () => Promise.resolve(['testResponse']), }; eventsEmitter.useStore(storeWithPromiseReceivers); diff --git a/src/EventsReceiver.test.ts b/src/EventsReceiver.test.ts index a541b28..c35fce7 100644 --- a/src/EventsReceiver.test.ts +++ b/src/EventsReceiver.test.ts @@ -11,7 +11,7 @@ describe('EventsReceiver', () => { expect(eventsReceiver.getEventsNames()).toEqual(['testEvent']); }); it('should handle event by receiver', () => { - const receiver = jest.fn(); + const receiver = vi.fn(); const stateManager: any = {}; const eventsReceiver = new EventsReceiver('testEvent', receiver); eventsReceiver.handleEvent('testEvent', {}, stateManager); @@ -23,10 +23,10 @@ describe('fetchToStateReceiver', () => { let stateManager: any = {}; beforeEach(() => { stateManager = { - setState: jest.fn(), - getState: jest.fn(() => ({})), + setState: vi.fn(), + getState: vi.fn(() => ({})), eventsEmitter: { - emit: jest.fn(), + emit: vi.fn(), }, }; }); @@ -72,10 +72,10 @@ describe('fetchStateReceiver', () => { let stateManager: any = {}; beforeEach(() => { stateManager = { - setState: jest.fn(), - getState: jest.fn(() => ({})), + setState: vi.fn(), + getState: vi.fn(() => ({})), eventsEmitter: { - emit: jest.fn(), + emit: vi.fn(), }, }; }); @@ -127,7 +127,7 @@ describe('fetchStateReceiver', () => { describe('fetchHandler', () => { let emit: EmitI; beforeEach(() => { - emit = jest.fn(); + emit = vi.fn(); }); it('should fetch data and emit success event when promise resolved', () => { const fetchResponse = { test: 'test' }; diff --git a/src/StateManager.test.ts b/src/StateManager.test.ts index f5e0d59..6599e54 100644 --- a/src/StateManager.test.ts +++ b/src/StateManager.test.ts @@ -4,13 +4,13 @@ import EventsReceiver from './EventsReceiver'; describe('StateManager', () => { let eventsEmitter: any = { listeners: {}, - listen: jest.fn(), - unlisten: jest.fn(), - useStore: jest.fn(), - emit: jest.fn(), - emitWild: jest.fn(), - getEventData: jest.fn(), - runListeners: jest.fn(), + listen: vi.fn(), + unlisten: vi.fn(), + useStore: vi.fn(), + emit: vi.fn(), + emitWild: vi.fn(), + getEventData: vi.fn(), + runListeners: vi.fn(), }; let stateManager = new StateManager(eventsEmitter); let initialState: object; @@ -23,15 +23,15 @@ describe('StateManager', () => { }; eventsEmitter = { listeners: {}, - listen: jest.fn(), - unlisten: jest.fn(), - useStore: jest.fn(), - emit: jest.fn(), - emitWild: jest.fn(), - getEventData: jest.fn(), - runListeners: jest.fn(), + listen: vi.fn(), + unlisten: vi.fn(), + useStore: vi.fn(), + emit: vi.fn(), + emitWild: vi.fn(), + getEventData: vi.fn(), + runListeners: vi.fn(), }; - eventsReceiver = jest.fn(() => ({ testData: {} })); + eventsReceiver = vi.fn(() => ({ testData: {} })); eventsReceivers = [new EventsReceiver(['testEvent', 'secondTestEvent'], eventsReceiver)]; stateManager = new StateManager(eventsEmitter, initialState, eventsReceivers); }); @@ -52,15 +52,15 @@ describe('StateManager', () => { }); }); it('should register events receiver only one time', () => { - console.warn = jest.fn(); + console.warn = vi.fn(); stateManager.useReceiver(eventsReceivers[0]); expect(console.warn).toHaveBeenCalledWith(`Store->registerReceiver - "testEvent" events receiver is already registered`); expect(console.warn).toHaveBeenCalledWith(`Store->registerReceiver - "secondTestEvent" events receiver is already registered`); }); it('should not register events receiver when dont have handleEvent function', () => { - console.warn = jest.fn(); + console.warn = vi.fn(); // @ts-ignore - stateManager.useReceiver({ getEventsNames: () => ['testEvent', 'secondTestEvent'], receiver: jest.fn(), eventsNames: [] }); + stateManager.useReceiver({ getEventsNames: () => ['testEvent', 'secondTestEvent'], receiver: vi.fn(), eventsNames: [] }); expect(console.warn).toHaveBeenCalledWith(`Store->registerReceiver - "testEvent" receiver is not a function`); expect(console.warn).toHaveBeenCalledWith(`Store->registerReceiver - "secondTestEvent" receiver is not a function`); }); @@ -73,12 +73,12 @@ describe('StateManager', () => { }); }); it('should not unregister events receiver when event not exists', () => { - console.warn = jest.fn(); - stateManager.removeReceiver({ getEventsNames: () => ['testEvent2'], handleEvent: jest.fn(), receiver: jest.fn(), eventsNames: [] }); + console.warn = vi.fn(); + stateManager.removeReceiver({ getEventsNames: () => ['testEvent2'], handleEvent: vi.fn(), receiver: vi.fn(), eventsNames: [] }); expect(console.warn).toHaveBeenCalledWith(`Store->unregisterReceiver - "testEvent2" event not registered`); }); it('should not unregister events receiver when event receivers are empty array', () => { - console.warn = jest.fn(); + console.warn = vi.fn(); stateManager.receivers = { testEvent: [], secondTestEvent: [], @@ -88,8 +88,8 @@ describe('StateManager', () => { expect(console.warn).toHaveBeenCalledWith(`Store->unregisterReceiver - "secondTestEvent" event dont have registered receiver`); }); it('should not unregister events receiver when event receiver not registered', () => { - console.warn = jest.fn(); - stateManager.removeReceiver({ getEventsNames: () => ['testEvent'], handleEvent: jest.fn(), receiver: jest.fn(), eventsNames: [] }); + console.warn = vi.fn(); + stateManager.removeReceiver({ getEventsNames: () => ['testEvent'], handleEvent: vi.fn(), receiver: vi.fn(), eventsNames: [] }); expect(console.warn).toHaveBeenCalledWith(`Store->unregisterReceiver - "testEvent" receiver not exists`); }); it('should emit change state event for parents and children', () => { @@ -114,7 +114,7 @@ describe('StateManager', () => { expect(stateManager.getParentPath('a')).toEqual(''); }); it('should handle promise from events receivers', () => { - const receiver = jest.fn(() => Promise.resolve({ asyncData: {} })); + const receiver = vi.fn(() => Promise.resolve({ asyncData: {} })); const asyncReceiver = new EventsReceiver('testEvent', receiver); stateManager.useReceiver(asyncReceiver); return stateManager.runReceivers('testEvent', { eventData: {} }).then((results: any) => { diff --git a/src/decorators/listener.test.ts b/src/decorators/listener.test.ts index 7f8e3ec..dda5b3d 100644 --- a/src/decorators/listener.test.ts +++ b/src/decorators/listener.test.ts @@ -8,7 +8,7 @@ describe('listener', () => { const GET_LIST_EVENT_NAME = 'Test:loadList'; const EXTEND_LIST_EVENT_NAME = 'Test:extendList'; it('should invoke listener when get list event is emitted', () => { - const callback = jest.fn(); + const callback = vi.fn(); const data: string[] = ['test']; @useEventrix @@ -33,8 +33,8 @@ describe('listener', () => { }); it('should invoke listener when extend list event is emitted', () => { - const callback = jest.fn(); - const extendCallback = jest.fn(); + const callback = vi.fn(); + const extendCallback = vi.fn(); const extendEventData: string[] = ['test', 'test2']; @useEventrix diff --git a/src/decorators/useEventrix.test.ts b/src/decorators/useEventrix.test.ts index 4dee3ad..8b42464 100644 --- a/src/decorators/useEventrix.test.ts +++ b/src/decorators/useEventrix.test.ts @@ -14,7 +14,7 @@ describe('useEventrix', () => { } } const eventrix = new Eventrix({}); - const testClassInstance = new FetchToStateTestClass({ eventrix, ajax: jest.fn() }); + const testClassInstance = new FetchToStateTestClass({ eventrix, ajax: vi.fn() }); expect(testClassInstance.eventrix).toEqual(eventrix); }); }); diff --git a/src/decorators/useEventrix.ts b/src/decorators/useEventrix.ts index bc5d858..d1ee429 100644 --- a/src/decorators/useEventrix.ts +++ b/src/decorators/useEventrix.ts @@ -23,6 +23,7 @@ export interface ServicesI { } export interface EventrixClassI { + // eslint-disable-next-line @typescript-eslint/no-misused-new new (services: ServicesI, ...args: any[]): EventrixClassI; eventrix?: EventrixI; eventrixReceiver?: ReceiverRegister[]; diff --git a/src/helpers.test.ts b/src/helpers.test.ts index 5ecc6b1..1c1f02d 100644 --- a/src/helpers.test.ts +++ b/src/helpers.test.ts @@ -246,7 +246,7 @@ describe('helpers', () => { describe('registerListeners', () => { it('should register listeners all wild listeners', () => { const eventrix = new Eventrix(); - const mockListener = jest.fn(); + const mockListener = vi.fn(); registerListeners(eventrix, 'a.b.c.d', mockListener); expect(eventrix.eventsEmitter.listeners['setState:a.*']).toHaveLength(1); expect(eventrix.eventsEmitter.listeners['setState:a.b.*']).toHaveLength(1); @@ -255,7 +255,7 @@ describe('helpers', () => { }); it('should unregister all register listeners', () => { const eventrix = new Eventrix(); - const mockListener = jest.fn(); + const mockListener = vi.fn(); const unregisterListeners = registerListeners(eventrix, 'a.b.c.d', mockListener); unregisterListeners(); expect(eventrix.eventsEmitter.listeners['setState:a.*']).toEqual(undefined); diff --git a/src/persistStore.test.ts b/src/persistStore.test.ts index ec02020..493da87 100644 --- a/src/persistStore.test.ts +++ b/src/persistStore.test.ts @@ -21,8 +21,8 @@ describe('persistStore', () => { beforeEach(() => { storage = { - setItem: jest.fn(), - getItem: jest.fn(() => ''), + setItem: vi.fn(), + getItem: vi.fn(() => ''), }; initialState = { a: 'a state', @@ -117,8 +117,8 @@ describe('persistStore', () => { beforeEach(() => { storage = { - setItem: jest.fn(() => Promise.resolve()), - getItem: jest.fn(() => Promise.resolve('')), + setItem: vi.fn(() => Promise.resolve()), + getItem: vi.fn(() => Promise.resolve('')), }; initialState = { a: 'a state', diff --git a/src/persistStore.ts b/src/persistStore.ts index 8e42b54..7ae51bb 100644 --- a/src/persistStore.ts +++ b/src/persistStore.ts @@ -14,10 +14,12 @@ const getStorageData = (storage: SyncStorage | AsyncStorage, storageKey: string) return JSON.parse(storageData || defaultData).data; }; +// eslint-disable-next-line @typescript-eslint/no-empty-object-type const getStateKeys = (storeState: StateI): StateKeysList => { return Object.keys(storeState) as StateKeysList; }; +// eslint-disable-next-line @typescript-eslint/no-empty-object-type const connectPersistStore = (eventrix: EventrixI, config: PersistStoreConfig): void => { const { blackList, diff --git a/src/react/context/PersistStoreGate.test.tsx b/src/react/context/PersistStoreGate.test.tsx index 3a98be1..5245180 100644 --- a/src/react/context/PersistStoreGate.test.tsx +++ b/src/react/context/PersistStoreGate.test.tsx @@ -12,8 +12,8 @@ describe('PersistStoreGate', () => { let storage: AsyncStorage; beforeEach(() => { storage = { - setItem: jest.fn(() => Promise.resolve()), - getItem: jest.fn(() => Promise.resolve('')), + setItem: vi.fn(() => Promise.resolve()), + getItem: vi.fn(() => Promise.resolve('')), }; }); @@ -58,8 +58,8 @@ describe('PersistStoreGate', () => { let storage: SyncStorage; beforeEach(() => { storage = { - setItem: jest.fn(), - getItem: jest.fn(() => ''), + setItem: vi.fn(), + getItem: vi.fn(() => ''), }; }); diff --git a/src/react/context/context.test.tsx b/src/react/context/context.test.tsx index 41ae02c..0cdc18f 100644 --- a/src/react/context/context.test.tsx +++ b/src/react/context/context.test.tsx @@ -15,7 +15,7 @@ describe('context with default eventrix instance', () => { it('should invoke callback when event emitted', () => { const eventrixInstance = defaultEventrixInstance; - const callbackMock = jest.fn(); + const callbackMock = vi.fn(); render( @@ -28,7 +28,7 @@ describe('context with default eventrix instance', () => { it('should invoke callback when event emitted with scope', () => { const eventrixInstance = defaultEventrixInstance; - const callbackMock = jest.fn(); + const callbackMock = vi.fn(); render( @@ -43,7 +43,7 @@ describe('context with default eventrix instance', () => { it('should invoke callback when event emitted with deep scope', () => { const eventrixInstance = defaultEventrixInstance; - const callbackMock = jest.fn(); + const callbackMock = vi.fn(); render( diff --git a/src/react/decorators/eventrixComponent.test.tsx b/src/react/decorators/eventrixComponent.test.tsx index ba4d79a..691c37a 100644 --- a/src/react/decorators/eventrixComponent.test.tsx +++ b/src/react/decorators/eventrixComponent.test.tsx @@ -25,7 +25,7 @@ describe('listener', () => { it('should use eventrix context and extend component by eventrix', () => { const eventrixInstance = new Eventrix({}); - const callbackMock = jest.fn(); + const callbackMock = vi.fn(); render( diff --git a/src/react/decorators/eventrixState.test.tsx b/src/react/decorators/eventrixState.test.tsx index 2a3929d..4ced8e3 100644 --- a/src/react/decorators/eventrixState.test.tsx +++ b/src/react/decorators/eventrixState.test.tsx @@ -49,7 +49,7 @@ describe('eventrixState', () => { bar: 'empty', }, }); - const callbackMock = jest.fn(); + const callbackMock = vi.fn(); const { getByTestId } = render( @@ -69,7 +69,7 @@ describe('eventrixState', () => { bar: 'empty', }, }); - const callbackMock = jest.fn(); + const callbackMock = vi.fn(); const { getByTestId } = render( diff --git a/src/react/decorators/eventrixState.ts b/src/react/decorators/eventrixState.ts index 5e268d1..58f8f26 100644 --- a/src/react/decorators/eventrixState.ts +++ b/src/react/decorators/eventrixState.ts @@ -10,7 +10,7 @@ function eventrixState(statePath: string, stateName: string): StateDecor return class extends Class { eventrixStates?: DecoratorEventrixStateI[]; eventrixListeners?: DecoratorEventrixListenerI[]; - // eslint-disable-next-line no-undef + [key: string]: any; constructor(props: any, context: any) { diff --git a/src/react/decorators/listener.test.tsx b/src/react/decorators/listener.test.tsx index 33a801c..21e012b 100644 --- a/src/react/decorators/listener.test.tsx +++ b/src/react/decorators/listener.test.tsx @@ -70,7 +70,7 @@ describe('listener', () => { it('should invoke callback when event emitted', () => { const eventrixInstance = new Eventrix({}); - const callbackMock = jest.fn(); + const callbackMock = vi.fn(); render( @@ -83,8 +83,8 @@ describe('listener', () => { it('should invoke callback when event emitted and extend componentDidMount method', () => { const eventrixInstance = new Eventrix({}); - const callbackMock = jest.fn(); - const didMountCallbackMock = jest.fn(); + const callbackMock = vi.fn(); + const didMountCallbackMock = vi.fn(); render( @@ -98,8 +98,8 @@ describe('listener', () => { it('should invoke callback when event emitted and extend componentWillUnmount method', () => { const eventrixInstance = new Eventrix({}); - const callbackMock = jest.fn(); - const willUnmountCallbackMock = jest.fn(); + const callbackMock = vi.fn(); + const willUnmountCallbackMock = vi.fn(); const { unmount } = render( diff --git a/src/react/decorators/stateListener.test.tsx b/src/react/decorators/stateListener.test.tsx index 4ae88b3..08e48c0 100644 --- a/src/react/decorators/stateListener.test.tsx +++ b/src/react/decorators/stateListener.test.tsx @@ -24,7 +24,7 @@ describe('stateListener', () => { it('should invoke callback when state changed', () => { const eventrixInstance = new Eventrix({}); - const callbackMock = jest.fn(); + const callbackMock = vi.fn(); render( diff --git a/src/react/hocs/withEventrix.test.tsx b/src/react/hocs/withEventrix.test.tsx index bcd840f..ca6b5c8 100644 --- a/src/react/hocs/withEventrix.test.tsx +++ b/src/react/hocs/withEventrix.test.tsx @@ -19,7 +19,7 @@ describe('withEventrix', () => { const TestContainer = ({ eventrix, children }: any) => {children}; it('should run callback when component render', async () => { - const mockedCallback = jest.fn(); + const mockedCallback = vi.fn(); const eventrixInstance = new Eventrix({}); eventrixInstance.listen('testEvent', mockedCallback); render( diff --git a/src/react/hocs/withEventrix.tsx b/src/react/hocs/withEventrix.tsx index 1afafff..9c3d2e7 100644 --- a/src/react/hocs/withEventrix.tsx +++ b/src/react/hocs/withEventrix.tsx @@ -3,7 +3,7 @@ import { EventrixContext } from '../context'; import { EventrixI } from '../../interfaces'; function withEventrix(BaseComponent: React.ComponentType | React.FC): React.FC { - // eslint-disable-next-line react/display-name + return (props: PropsI & { eventrix: EventrixI }): JSX.Element => { const context = useContext(EventrixContext); return ; diff --git a/src/react/hooks/useEmit.test.tsx b/src/react/hooks/useEmit.test.tsx index 28ceaee..51bcaac 100644 --- a/src/react/hooks/useEmit.test.tsx +++ b/src/react/hooks/useEmit.test.tsx @@ -19,7 +19,7 @@ describe('useEmit', () => { it('should emit event when component did mount', () => { const eventrixInstance = new Eventrix({}); - const mockListener = jest.fn(); + const mockListener = vi.fn(); eventrixInstance.listen('testEvent', mockListener); render( @@ -32,7 +32,7 @@ describe('useEmit', () => { it('should emit event when component did mount with scope', () => { const eventrixInstance = new Eventrix({}); - const mockListener = jest.fn(); + const mockListener = vi.fn(); eventrixInstance.listen('Test:testEvent', mockListener); render( @@ -47,7 +47,7 @@ describe('useEmit', () => { it('should emit event when component did mount with deep scope', () => { const eventrixInstance = new Eventrix({}); - const mockListener = jest.fn(); + const mockListener = vi.fn(); eventrixInstance.listen('Test:List:testEvent', mockListener); render( diff --git a/src/react/hooks/useEvent.test.tsx b/src/react/hooks/useEvent.test.tsx index 8021890..2229a7a 100644 --- a/src/react/hooks/useEvent.test.tsx +++ b/src/react/hooks/useEvent.test.tsx @@ -14,7 +14,7 @@ describe('useEvent', () => { it('should invoke callback when event emitted', () => { const eventrixInstance = new Eventrix({}); - const callbackMock = jest.fn(); + const callbackMock = vi.fn(); render( @@ -27,7 +27,7 @@ describe('useEvent', () => { it('should invoke callback when event emitted with scope', () => { const eventrixInstance = new Eventrix({}); - const callbackMock = jest.fn(); + const callbackMock = vi.fn(); render( @@ -42,7 +42,7 @@ describe('useEvent', () => { it('should invoke callback when event emitted with deep scope', () => { const eventrixInstance = new Eventrix({}); - const callbackMock = jest.fn(); + const callbackMock = vi.fn(); render( diff --git a/src/react/hooks/useFetchState.test.tsx b/src/react/hooks/useFetchState.test.tsx index 98dabd4..722c269 100644 --- a/src/react/hooks/useFetchState.test.tsx +++ b/src/react/hooks/useFetchState.test.tsx @@ -49,7 +49,7 @@ describe('useFetchState', () => { it('should start fetch users and set loading', () => { const mockedUsersList: UserI[] = []; - const mockedFetchMetchod = jest.fn(() => Promise.resolve(mockedUsersList)); + const mockedFetchMetchod = vi.fn(() => Promise.resolve(mockedUsersList)); const usersFetchReceiver = fetchStateReceiver('users', mockedFetchMetchod); const eventrixInstance = new Eventrix({}, [usersFetchReceiver]); const { getByTestId } = render( @@ -69,7 +69,7 @@ describe('useFetchState', () => { { name: 'Jan', surname: 'Kowalski' }, { name: 'Jan', surname: 'Nowak' }, ]; - const mockedFetchMetchod = jest.fn(() => Promise.resolve(mockedUsersList)); + const mockedFetchMetchod = vi.fn(() => Promise.resolve(mockedUsersList)); const usersFetchReceiver = fetchStateReceiver('users', mockedFetchMetchod); const eventrixInstance = new Eventrix({}, [usersFetchReceiver]); const { getByTestId } = render( @@ -87,7 +87,7 @@ describe('useFetchState', () => { }); it('should fetch users and show fetch error message', async () => { const fetchErrorMessage = 'fetch error'; - const mockedFetchMetchod = jest.fn(() => Promise.reject({ message: fetchErrorMessage })); + const mockedFetchMetchod = vi.fn(() => Promise.reject({ message: fetchErrorMessage })); const usersFetchReceiver = fetchStateReceiver('users', mockedFetchMetchod); const eventrixInstance = new Eventrix({}, [usersFetchReceiver]); const { getByTestId } = render( @@ -106,7 +106,7 @@ describe('useFetchState', () => { it('should start fetch users and set loading with scope', () => { const mockedUsersList: UserI[] = []; - const mockedFetchMetchod = jest.fn(() => Promise.resolve(mockedUsersList)); + const mockedFetchMetchod = vi.fn(() => Promise.resolve(mockedUsersList)); const usersFetchReceiver = fetchStateReceiver('selectData.users', mockedFetchMetchod); const eventrixInstance = new Eventrix({}, [usersFetchReceiver]); const { getByTestId } = render( diff --git a/tsconfig.json b/tsconfig.json index ed492e3..4579ddd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,7 @@ "dom", "es2016" ], + "types": ["vitest/globals"], "experimentalDecorators": true, "suppressImplicitAnyIndexErrors": false, "baseUrl": "./", diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..4241f06 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,42 @@ +import { defineConfig } from 'vite'; +import dts from 'vite-plugin-dts'; +import { resolve } from 'node:path'; +import { existsSync, readdirSync, rmSync } from 'node:fs'; +import path from 'path'; +import react from '@vitejs/plugin-react'; + +emptyDir(resolve(__dirname, 'dist')); + +export default defineConfig({ + plugins: [dts({ rollupTypes: true }), react()], + base: './', + build: { + sourcemap: true, + outDir: './dist', + lib: { + entry: path.resolve(__dirname, 'src/index.ts'), + name: 'eventrix', + formats: ['es', 'cjs', 'umd', 'iife'], + fileName: (format) => `index.${format}.js`, + }, + rollupOptions: { + external: ['react', 'react-dom'], + output: { + globals: { + react: 'React', + 'react-dom': 'ReactDOM', + }, + }, + }, + }, +}); + +function emptyDir(dir: string) { + if (!existsSync(dir)) { + return; + } + + for (const file of readdirSync(dir)) { + rmSync(resolve(dir, file), { recursive: true, force: true }); + } +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..386659d --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'jsdom', + coverage: { + reporter: ['html', 'lcov', 'text'], + }, + }, +}); diff --git a/webpack-prod.config.js b/webpack-prod.config.js deleted file mode 100644 index 1c046f1..0000000 --- a/webpack-prod.config.js +++ /dev/null @@ -1,48 +0,0 @@ -const package = require('./package.json'); - -const externals = new Set([...Object.keys(package.dependencies || {}), ...Object.keys(package.peerDependencies || {})]); - -const config = { - mode: 'production', - devtool: 'inline-source-map', - entry: { - index: './src/index.ts', - 'redux-adapter': './src/redux/index.ts', - }, - output: { - path: __dirname + '/dist', - filename: '[name].js', - sourceMapFilename: '[name].map', - library: 'eventrix', - libraryTarget: 'umd', - globalObject: 'this', - umdNamedDefine: true, - }, - externals(context, request, callback) { - if (externals.has(request)) { - return callback(null, `${config.output.libraryTarget} ${request}`); - } - return callback(); - }, - resolve: { - extensions: ['.tsx', '.ts', '.js'], - }, - module: { - rules: [ - { - test: /\.ts(x?)$/, - exclude: /node_modules/, - use: [ - { - loader: 'babel-loader', - }, - { - loader: 'ts-loader', - }, - ], - }, - ], - }, -}; - -module.exports = config;