-
Notifications
You must be signed in to change notification settings - Fork 4
feat: useFocus 구현 #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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?: number, | ||
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: threshold || 0.1, | ||
rootMargin: rootMargin || '0px 0px 0px 0px', | ||
}); | ||
|
||
observer.observe(current); | ||
|
||
return () => observer && observer.disconnect(); | ||
} | ||
}, [handleScroll]); | ||
|
||
return elementRef; | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
threshold = 0.1
과 같이 한다면 기본값임을 명확히 알 수 있고 밑에서는 조건이 빠져 읽기 쉬울 것 같아요There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
감사합니다
2027151 에서 반영완료했습니다!