Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
a64510c
feat: rollup-plugin-styles
mltsk May 7, 2024
9072963
fix: pritter
mltsk May 7, 2024
300877b
feat: implement hook useIsMounted
mltsk May 7, 2024
5593056
feat: implement component Ovarlay
mltsk May 7, 2024
cc75e87
feat: implement component ModalsProvider
mltsk May 7, 2024
b566ad0
feat: implement component Modal
mltsk May 7, 2024
e66ad1c
fix: description
mltsk May 7, 2024
7b10129
fix: pritter
mltsk May 7, 2024
2033dc7
feat: implement component Ovarlay
mltsk May 7, 2024
6e5cdcd
feat: rollup-plugin-styles
mltsk May 7, 2024
2c3f941
feat: add color, color, blur, zIndex, backgroundOpacity, center props…
mltsk Sep 1, 2024
87bfdbe
Merge branch 'master' into feature/Overlay
mltsk Sep 1, 2024
a866818
feat: delete rollup-plugin-styles
mltsk Sep 1, 2024
2909650
WIP
mltsk Oct 15, 2024
2c0e3de
Revert "WIP"
mltsk Oct 29, 2024
991d5a2
fix Overlay
mltsk Oct 29, 2024
3507210
fix build
mltsk Oct 29, 2024
82a7912
lint
mltsk Oct 29, 2024
7b69549
implement useBodyScrollLock hook
mltsk Nov 13, 2024
0f26d04
update Overlay
mltsk Nov 13, 2024
02ba5c3
Merge branch 'master' into feature/Overlay
mltsk Nov 13, 2024
ba98ff1
feat: update docs and small improvements
mltsk Nov 21, 2024
ed7d172
refactoring hexToRgba
mltsk Nov 28, 2024
321d6fc
fix build
mltsk Nov 29, 2024
3ca6cc1
feat: implement useBodyScrollLock hook
mltsk Nov 29, 2024
9447d62
feat(use-body-scroll-lock): restore original oveflow
sadcitizen Dec 2, 2024
15453cf
Merge branch 'master' into feature/Overlay
mltsk Dec 11, 2024
58703be
refactor(feature/Overlay): WIP
mltsk Dec 18, 2024
e835b3e
Merge branch 'master' into feature/useBodyScrollLock
mltsk Dec 18, 2024
22ab82b
fix(feature/useBodyScrollLock): fix story
mltsk Dec 18, 2024
4909a55
feat(useScrollLock): add passWithNoTests to jest config
mltsk Jan 15, 2025
37f6758
feat(useScrollLock): add target params, update docs, story
mltsk Jan 15, 2025
72abb9a
fix: tsconfig
mltsk Jan 27, 2025
bbae74a
fix: hook name
mltsk Jan 28, 2025
4807e4c
Merge branch 'feature/useBodyScrollLock' into feature/overlay
mltsk Jan 28, 2025
75fca1c
feat: merge useScrollLock
mltsk Jan 28, 2025
4740d4e
fix: fix modal in story
mltsk Jan 29, 2025
5cb693a
fix: remove body
mltsk Jan 30, 2025
0359048
Merge branch 'feature/useBodyScrollLock' into feature/overlay
mltsk Jan 30, 2025
d223637
fix(feature/overlay): fix blur
mltsk Jan 30, 2025
ab03a38
fix(feature/overlay): update doc
mltsk Jan 30, 2025
9024797
fix(feature/overlay): hexToRgba
mltsk Jan 30, 2025
f59007f
fix(feature/overlay): fix fadeIn, fadeOut and refactoring
mltsk Jan 31, 2025
8bd0c3d
fix(feature/overlay): add mergedClassNames
mltsk Jan 31, 2025
116cf03
feat(feature/overlay): add fixed prop
mltsk Jan 31, 2025
844a224
feat(feature/overlay): add refElement prop
mltsk Jan 31, 2025
550dce1
feat(feature/overlay): add story
mltsk Jan 31, 2025
621c04f
feat(feature/overlay): add .vscode to gitignore
mltsk Feb 3, 2025
900c14b
fix(feature/overlay): lint css
mltsk Feb 3, 2025
c1c0384
Merge branch 'master' into feature/Modals
mltsk Feb 3, 2025
8a71f90
Merge branch 'feature/useBodyScrollLock' into feature/Modals
mltsk Feb 3, 2025
ba91479
feat(feature/modals): remove Overlay and ModalsProvider
mltsk Feb 3, 2025
75d2af7
Merge branch 'feature/Overlay' into feature/Modals
mltsk Feb 3, 2025
2179c91
feat(Modals): fix imports
mltsk Feb 4, 2025
00da6e1
fix(Modals): fix layout
mltsk Feb 4, 2025
cf1519e
feat(Modals): update stories and refactoring
mltsk Feb 4, 2025
2e877d5
feat(Modals): add story
mltsk Feb 5, 2025
6963635
feat(Modals): refactoring
mltsk Feb 5, 2025
4783b46
fix(Modals): fix id
mltsk Feb 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
*.log
.idea
.DS_Store
.vscode
node_modules
.cache
dist
Expand Down
1 change: 1 addition & 0 deletions components/Modal/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src
48 changes: 48 additions & 0 deletions components/Modal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# `@byndyusoft-ui/modals`

The `Modal` component provides a modal window to display content on top of other interface elements. The `ModalsProvider` is necessary to provide context and manage the state of modals.

## Installation

```bash
npm install @byndyusoft-ui/modal
```

## Usage

```tsx
import React from 'react';
import { ModalsProvider } from '@byndyusoft-ui/modals-provider';
import { Modal } from '@byndyusoft-ui/modals';

function ComponentWithModalProvider() {
return (
<ModalsProvider>
<ComponentWithModal />
</ModalsProvider>
);
}

function ComponentWithModal() {
const { open } = useModals();

return (
<div>
<button onClick={() => open('modal-id')}>Open modal</button>
<div>Main content</div>
<Modal id="modal-id" onOpen={() => console.log('Modal opened')} onClose={() => console.log('Modal closed')}>
<div>Modal content</div>
</Modal>
</div>
);
}
```

## Props

| Prop | Type | Default | Description |
| -------- | --------- | ------- | ------------------------------------------------ |
| children | ReactNode | - | Content to be rendered inside the modal |
| id | string | - | Unique identifier for the modal |
| onOpen | function | - | Callback function triggered when modal is opened |
| onClose | function | - | Callback function triggered when modal is closed |
41 changes: 41 additions & 0 deletions components/Modal/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "@byndyusoft-ui/Modal",
"version": "0.1.0",
"description": "Byndyusoft UI Modal React Component",
"keywords": [
"byndyusoft",
"byndyusoft-ui",
"react",
"modal"
],
"author": "Dmitrii Maletskii <maleckij@byndyusoft.com>",
"homepage": "https://github.com/Byndyusoft/ui/tree/master/components/modal#readme",
"license": "ISC",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/Byndyusoft/ui.git"
},
"scripts": {
"build": "rollup --config",
"clean": "rimraf dist",
"lint": "eslint src --config ../../eslint.config.js",
"test": "jest --config ../../jest.config.js --roots components/modal/src"
},
"bugs": {
"url": "https://github.com/Byndyusoft/ui/issues"
},
"publishConfig": {
"access": "public"
},
"peerDependencies": {
"react": ">=17",
"react-dom": ">=17"
},
"dependencies": {
"@byndyusoft-ui/use-is-mounted": "*",
"@byndyusoft-ui/modals-provider": "*",
"@byndyusoft-ui/Overlay": "*"
}
}
15 changes: 15 additions & 0 deletions components/Modal/rollup.config.js
Original file line number Diff line number Diff line change
@@ -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']
})
]
};
121 changes: 121 additions & 0 deletions components/Modal/src/Modal.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
.container {
z-index: 1999999999;
position: fixed;
inset: 0;
display: flex;
justify-content: center;
align-items: center;
overflow-x: hidden;
overflow-y: auto;
pointer-events: none;
}

.overlay {
cursor: pointer;
}

.dialog {
position: relative;
display: flex;
flex-direction: column;
max-width: 328px;
max-height: calc(100dvh - 2rem);
margin: 1rem;
color: #FFFFFF;
background: #252528;
opacity: 0;
transform: translateY(-25%);
transition:
visibility 0s linear 0.4s,
transform 0.4s ease,
opacity 0.4s ease;
visibility: hidden;
}

.dialog.isOpen {
opacity: 1;
pointer-events: all;
transform: translateY(0);
transition:
visibility 0s linear 0s,
transform 0.4s ease,
opacity 0.4s ease;
visibility: visible;
}

.header {
position: relative;
bottom: 0;
left: 0;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1rem;
padding: 0.75rem 0.75rem 0.75rem 1rem;
}

.title {
margin-bottom: 0;
margin-top: 0;
}

.close {
cursor: pointer;
position: absolute;
top: 0.75rem;
right: 0.75rem;
flex-shrink: 0;
height: 1rem;
width: 1rem;
background: transparent url('./partials/assets/CrossIcon.svg') no-repeat center center;
border: none;
background-size: 0.75rem;
}

.closeText {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
visibility: visible;
white-space: nowrap;
}

.close:focus-visible {
outline: 5px auto -webkit-focus-ring-color;
}

.body {
flex: 1 1;
overflow: hidden;
overflow-y: auto;
padding-left: 1.5rem;
padding-right: 1.5rem;
scrollbar-color: #FFFFFF #252528;
scrollbar-width: thin;
}

.content {
flex-grow: 1;
max-height: 100%;
overflow-y: auto;
overflow-x: hidden;
}

.heading {
margin-bottom: 0;
margin-top: 0;
}

.footer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 1.5rem;
padding-top: 1rem;
}
90 changes: 90 additions & 0 deletions components/Modal/src/Modal.tests.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import ModalsProvider, { useModals } from '@byndyusoft-ui/modals-provider';
import Modal from './Modal';

interface ISceneProps {
onOpen?: () => void;
onClose?: () => void;
}

const Scene = ({ onOpen, onClose }: ISceneProps): JSX.Element => {
const { open } = useModals();

return (
<>
<button onClick={() => open('modal')}>Open</button>
<Modal id="modal" onOpen={onOpen} onClose={onClose}>
Test
</Modal>
</>
);
};

describe('components/Modal', () => {
test('renders correctly', async () => {
render(
<ModalsProvider>
<Scene />
</ModalsProvider>
);

expect(screen.getByRole('document')).not.toHaveClass('is-open');

await userEvent.click(screen.getByText(/Open/));

expect(screen.getByText('Test')).toBeInTheDocument();
expect(screen.getByRole('document')).toHaveClass('is-open');
});

test('calls `onOpen` callback', async () => {
const onOpen = jest.fn();

render(
<ModalsProvider>
<Scene onOpen={onOpen} />
</ModalsProvider>
);

await userEvent.click(screen.getByText(/Open/));

expect(onOpen).toBeCalledTimes(1);
});

test('calls `onClose` callback by button click', async () => {
const onClose = jest.fn();

render(
<ModalsProvider>
<Scene onClose={onClose} />
</ModalsProvider>
);

await userEvent.click(screen.getByText(/Open/));

expect(screen.getByRole('document')).toHaveClass('is-open');

await userEvent.click(screen.getByRole('button', { name: /Close/ }));

expect(onClose).toBeCalledTimes(1);
});

test('calls `onClose` callback by overlay click', async () => {
const onClose = jest.fn();

render(
<ModalsProvider>
<Scene onClose={onClose} />
</ModalsProvider>
);

await userEvent.click(screen.getByText(/Open/));

expect(screen.getByRole('document')).toHaveClass('is-open');

await userEvent.click(screen.getAllByRole('button')[1]);

expect(onClose).toBeCalledTimes(1);
});
});
20 changes: 20 additions & 0 deletions components/Modal/src/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React, { FC } from 'react';
import { ModalContainer, ModalCloseButton } from './partials';
import { IModalProps } from './Modal.types';
import styles from './Modal.module.css';

const Modal: FC<IModalProps> = ({ children, classNames, id, ...props }): JSX.Element => {
const mergedClassNames = Object.assign(
{ container: styles.container, dialog: styles.dialog, overlay: styles.overlay, isOpen: styles.isOpen },
classNames
);

return (
<ModalContainer id={id} classNames={mergedClassNames} {...props}>
{children}
<ModalCloseButton id={id} />
</ModalContainer>
);
};

export default Modal;
35 changes: 35 additions & 0 deletions components/Modal/src/Modal.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ButtonHTMLAttributes, HTMLAttributes } from 'react';

export const tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;

export type THeadingTag = (typeof tags)[number];

export interface IModalClassNames {
container: string;
dialog: string;
overlay: string;
isOpen: string;
}

export interface IModalPartialProps extends HTMLAttributes<HTMLDivElement> {
className?: string;
}

export interface IModalCloseButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
className?: string;
id: string;
}

export interface IModalContainerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'role' | 'className'> {
classNames?: IModalClassNames;
id: string;
onOpen?: () => void;
onClose?: () => void;
}

export interface IModalTitleProps extends HTMLAttributes<HTMLHeadingElement> {
className?: string;
as?: THeadingTag;
}

export interface IModalProps extends IModalContainerProps {}
Loading