Skip to content

Commit

Permalink
Merge pull request #14 from brgndyy/feat/#13
Browse files Browse the repository at this point in the history
일반 유틸함수를 담은 패키지 작성
  • Loading branch information
brgndyy authored Sep 29, 2024
2 parents bc4cca4 + f28193b commit a2466d8
Show file tree
Hide file tree
Showing 16 changed files with 377 additions and 3 deletions.
31 changes: 31 additions & 0 deletions packages/common/utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@brgndy/utils",
"version": "1.0.0",
"sideEffects": false,
"type": "module",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "NODE_ENV=production rollup -c",
"build:dev": "rollup -c",
"dev": "rollup -c -w",
"test": "vitest --run"
},
"author": {
"name": "brgndyy",
"url": "https://github.com/brgndyy"
},
"license": "ISC",
"description": "",
"devDependencies": {
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.6",
"rollup": "^4.13.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"typescript": "^5.6.2",
"vitest": "^2.1.1"
}
}
32 changes: 32 additions & 0 deletions packages/common/utils/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import terser from '@rollup/plugin-terser';

import packageJson from './package.json' assert { type: 'json' };

const isProduction = process.env.NODE_ENV === 'production';

export default {
input: 'src/index.ts',
output: [
{
file: packageJson.main,
format: 'cjs',
sourcemap: !isProduction,
},
{
file: packageJson.module,
format: 'esm',
sourcemap: !isProduction,
},
],
plugins: [
peerDepsExternal(),
resolve(),
commonjs(),
typescript({ tsconfig: './tsconfig.json' }),
terser(),
],
};
62 changes: 62 additions & 0 deletions packages/common/utils/src/batchOfRequestOf.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { describe, it, expect, vi } from 'vitest';
import { batchRequestsOf } from './batchOfRequestOf';

describe('batchRequestsOf에 대한 테스트 코드 작성', () => {
it('동일한 인자를 넣었을때 동일한 프로미스가 반환되어야 한다.', async () => {
const mockFunc = vi.fn((a: number, b: number) => {
return new Promise<number>((resolve) => {
setTimeout(() => resolve(a + b), 100);
});
});

const batchedFunc = batchRequestsOf(mockFunc);

const promise1 = batchedFunc(1, 2);
const promise2 = batchedFunc(1, 2);

expect(promise1).toBe(promise2);
expect(mockFunc).toHaveBeenCalledTimes(1);

const result = await promise1;
expect(result).toBe(3);
});

it('다른 인자를 넣었을때는 다른 프로미스가 반환 되어야 한다.', async () => {
const mockFunc = vi.fn((a: number, b: number) => {
return new Promise<number>((resolve) => {
setTimeout(() => resolve(a + b), 100);
});
});

const batchedFunc = batchRequestsOf(mockFunc);

const promise1 = batchedFunc(1, 2);
const promise2 = batchedFunc(3, 4);

expect(promise1).not.toBe(promise2);
expect(mockFunc).toHaveBeenCalledTimes(2);

const result1 = await promise1;
const result2 = await promise2;

expect(result1).toBe(3);
expect(result2).toBe(7);
});

it('프로미스가 resolve 된 후에는 새로운 프로미스가 반환 되어야 한다.', async () => {
const mockFunc = vi.fn((a: number, b: number) => {
return new Promise<number>((resolve) => {
setTimeout(() => resolve(a + b), 100);
});
});

const batchedFunc = batchRequestsOf(mockFunc);

const promise1 = batchedFunc(1, 2);
await promise1;

const promise2 = batchedFunc(1, 2);
expect(promise1).not.toBe(promise2);
expect(mockFunc).toHaveBeenCalledTimes(2);
});
});
19 changes: 19 additions & 0 deletions packages/common/utils/src/batchOfRequestOf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export function batchRequestsOf<F extends (...args: any[]) => any>(func: F) {
const promiseByKey = new Map<string, Promise<ReturnType<F>>>();

return function (...args: Parameters<F>) {
const key = JSON.stringify(args);

if (promiseByKey.has(key)) {
return promiseByKey.get(key)!;
} else {
const promise = func(...args);
promise.then(() => {
promiseByKey.delete(key);
});
promiseByKey.set(key, promise);

return promise;
}
} as F;
}
1 change: 1 addition & 0 deletions packages/common/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './batchOfRequestOf';
23 changes: 23 additions & 0 deletions packages/common/utils/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"target": "ESNext",
"module": "ESNext",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"rootDir": "./src",
"outDir": "./dist",
"declaration": true,
"declarationDir": "types",
"sourceMap": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"emitDeclarationOnly": true,
"traceResolution": true,
"lib": ["ESNext", "DOM"]
},
"include": ["src/**/*"],
"exclude": ["dist", "types"]
}
9 changes: 9 additions & 0 deletions packages/common/utils/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'vitest/config';

export default defineConfig({
test: {
globals: true,
include: ['**/*.test.ts'],
environment: 'jsdom',
},
});
2 changes: 1 addition & 1 deletion packages/react/react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@brgndy/react",
"version": "1.0.2",
"version": "1.0.3",
"sideEffects": false,
"main": "dist/index.js",
"module": "dist/index.esm.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# ScrollPreventer

해당 컴포넌트로 감싼 컴포넌트가 렌더링시에 스크롤이 적용되지 않습니다.

백드롭이 적용 된 모달 렌더링시에 사용할때 유용하게 적용됩니다.

## - Example

```tsx
import ScrollPreventer from '/@brgndy-react/ScrollPreventer';

export default function App() {
return (
<ScrollPreventer>
<Modal />
</ScrollPreventer>
);
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { render } from '@testing-library/react';
import ScrollPreventer from './ScrollPreventer';

describe('ScrollPreventer 컴포넌트에 대한 테스트 코드 작성', () => {
beforeEach(() => {
document.body.style.overflow = '';
document.body.style.height = '';
document.documentElement.style.overflow = 'hidden';
document.documentElement.style.height = '100%';
window.scrollTo(0, 0);
});

const createLongContent = () => (
<div>
<div style={{ height: '200rem' }}>Very long content</div>
</div>
);

it('만약 isOpen Prop의 상태값이 true라면 body의 overflow와 height에 스타일이 적용된다.', () => {
render(<ScrollPreventer isOpen={true}> {createLongContent()}</ScrollPreventer>);

expect(document.body.style.overflow).toBe('hidden');
expect(document.body.style.height).toBe('100%');
expect(document.documentElement.style.overflow).toBe('hidden');
expect(document.documentElement.style.height).toBe('100%');
});

it('isOpen이 false가 되면 body의 스타일은 초기화 된다.', () => {
render(<ScrollPreventer isOpen={false}> {createLongContent()}</ScrollPreventer>);

expect(document.body.style.overflow).toBe('');
expect(document.body.style.height).toBe('');
expect(document.documentElement.style.overflow).toBe('');
expect(document.documentElement.style.height).toBe('');
});

it('컴포넌트가 언마운트 되면 body 스타일이 초기화 된다.', () => {
const { unmount } = render(
<ScrollPreventer isOpen={true}> {createLongContent()}</ScrollPreventer>,
);

expect(document.body.style.overflow).toBe('hidden');
expect(document.body.style.height).toBe('100%');
expect(document.documentElement.style.overflow).toBe('hidden');
expect(document.documentElement.style.height).toBe('100%');

unmount();
expect(document.body.style.overflow).toBe('');
expect(document.body.style.height).toBe('');
expect(document.documentElement.style.overflow).toBe('');
expect(document.documentElement.style.height).toBe('');
});

it('해당 컴포넌트로 감싼 컨텐츠는 스크롤이 적용되지 않는다.', () => {
render(<ScrollPreventer> {createLongContent()}</ScrollPreventer>);

window.scrollTo(0, 100);
expect(window.scrollY).toBe(0);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useEffect } from 'react';
import type { PropsWithChildren } from 'react';

interface PreventScrollObserverProps extends PropsWithChildren {
isOpen?: boolean;
}

export default function ScrollPreventer({ isOpen = true, children }: PreventScrollObserverProps) {
useEffect(() => {
if (isOpen) {
document.documentElement.style.overflow = 'hidden';
document.documentElement.style.height = '100%';
document.body.style.overflow = 'hidden';
document.body.style.height = '100%';
} else {
document.documentElement.style.overflow = '';
document.documentElement.style.height = '';
document.body.style.overflow = '';
document.body.style.height = '';
}

return () => {
document.documentElement.style.overflow = '';
document.documentElement.style.height = '';
document.body.style.overflow = '';
document.body.style.height = '';
};
}, [isOpen]);

return <>{children}</>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ScrollPreventer';
1 change: 1 addition & 0 deletions packages/react/react/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ScrollPreventer';
1 change: 1 addition & 0 deletions packages/react/react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './hooks';
export * from './utils';
export * from './components';
4 changes: 2 additions & 2 deletions packages/react/react/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"jsx": "react",
"jsx": "react-jsx",
"target": "ESNext",
"module": "ESNext",
"esModuleInterop": true,
Expand All @@ -17,5 +17,5 @@
"emitDeclarationOnly": true,
"traceResolution": true
},
"include": ["src/**/*"]
"include": ["src/**/*", "**/*.tsx"]
}
Loading

0 comments on commit a2466d8

Please sign in to comment.