-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: useFocus 기능 구현 * test: useFocus 기능 테스트 추가 * docs: Storybook, README업데이트 * chore: parameter에서 기본값 정의하도록 변경 (#27)
- Loading branch information
Showing
8 changed files
with
185 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { Canvas, Meta, Description } from '@storybook/blocks'; | ||
import * as Focus from './Focus.stories'; | ||
|
||
<Meta of={Focus} /> | ||
|
||
# useFocus | ||
|
||
DOM Element가 화면에 노출되었을때, callback으로 전달된 함수를 실행합니다. | ||
|
||
## 함수인자 | ||
|
||
`onFocusCallback` : 요소가 focus되었을때, 수행할 함수입니다. | ||
|
||
`threshold` : 요소가 화면에 보일때의 기준을 결정합니다. 1이면 전체요소가 모두 화면에 들어왔을때 입니다. | ||
|
||
`rootMargin` : 화면의 너비, 높이를 조정할 수 있습니다. "top right bottom left" 형태로 기재하며 반드시 단위를 명시해야합니다. | ||
|
||
## 반환값 | ||
|
||
`ref`: focus될 DOM요소에 할당해줄 ref 객체입니다. | ||
|
||
<Canvas of={Focus.defaultStory} /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { style } from '@vanilla-extract/css'; | ||
|
||
export const backgroundDiv = style({ | ||
height: 100, | ||
}); | ||
|
||
export const scrollDiv = style({ | ||
position: 'absolute', | ||
backgroundColor: 'black', | ||
color: 'white', | ||
borderRadius: 10, | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
display: 'flex', | ||
padding: 16, | ||
top: 200, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Meta, StoryObj } from '@storybook/react'; | ||
import Focus from './Focus'; | ||
|
||
const meta = { | ||
title: 'hooks/useFocus', | ||
component: Focus, | ||
parameters: { | ||
layout: 'centered', | ||
docs: { | ||
canvas: {}, | ||
}, | ||
}, | ||
} satisfies Meta<typeof Focus>; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof meta>; | ||
|
||
export const defaultStory: Story = { | ||
args: {}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import React, { useState } from 'react'; | ||
import useFocus from '@/useFocus/useFocus'; | ||
import { backgroundDiv, scrollDiv } from './Focus.css'; | ||
|
||
export default function Focus() { | ||
const [message, setMessage] = useState('NOT FOCUS'); | ||
const ref = useFocus<HTMLDivElement>(() => setMessage('FOCUS!'), 1, '-10px'); | ||
|
||
return ( | ||
<div className={backgroundDiv}> | ||
<div ref={ref} className={scrollDiv}> | ||
{message} | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { fireEvent, render } from '@testing-library/react'; | ||
import useFocus from './useFocus'; | ||
import React from 'react'; | ||
|
||
interface Entrie { | ||
target: Element; | ||
isIntersecting: boolean; | ||
} | ||
|
||
const mockIntersectionObserver = class { | ||
entries: Entrie[]; | ||
constructor(callback) { | ||
this.entries = []; | ||
window.addEventListener('scroll', () => { | ||
if (window.scrollY > 50) { | ||
this.entries.map((entry) => { | ||
entry.isIntersecting = this.isInViewPort(); | ||
}); | ||
} | ||
callback(this.entries, this); | ||
}); | ||
} | ||
|
||
isInViewPort() { | ||
return true; | ||
} | ||
|
||
observe(target: Element) { | ||
this.entries.push({ isIntersecting: false, target }); | ||
} | ||
|
||
unobserve(target) { | ||
this.entries = this.entries.filter((ob) => ob.target !== target); | ||
} | ||
|
||
disconnect() { | ||
this.entries = []; | ||
} | ||
}; | ||
|
||
describe('useFocus 기능 테스트', () => { | ||
beforeEach(() => { | ||
// eslint-disable-next-line | ||
global.IntersectionObserver = mockIntersectionObserver as any; | ||
}); | ||
|
||
it('focus되었을때 onFocusCallback을 실행시킬 수 있다.', async () => { | ||
const mock = jest.fn(); | ||
const Test = () => { | ||
const ref = useFocus<HTMLDivElement>(mock); | ||
return <div ref={ref}>Test</div>; | ||
}; | ||
render(<Test />); | ||
fireEvent.scroll(window, { target: { scrollY: 100 } }); | ||
expect(mock).toHaveBeenCalled(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { useCallback, useEffect, useRef } from 'react'; | ||
|
||
export default function useFocus<T extends HTMLElement>( | ||
onFocusCallback: (() => void) | (() => Promise<void>), | ||
threshold = 0.1, | ||
rootMargin?: string, | ||
) { | ||
const elementRef = useRef<T>(null); | ||
|
||
const handleScroll: IntersectionObserverCallback = useCallback(([entry]) => { | ||
const { current } = elementRef; | ||
if (current) { | ||
if (entry.isIntersecting) { | ||
onFocusCallback(); | ||
} | ||
} | ||
}, []); | ||
|
||
useEffect(() => { | ||
let observer: IntersectionObserver; | ||
const { current } = elementRef; | ||
|
||
if (current) { | ||
observer = new IntersectionObserver(handleScroll, { | ||
threshold, | ||
rootMargin: rootMargin || '0px 0px 0px 0px', | ||
}); | ||
|
||
observer.observe(current); | ||
|
||
return () => observer && observer.disconnect(); | ||
} | ||
}, [handleScroll]); | ||
|
||
return elementRef; | ||
} |