Skip to content

[AI] feat: 이슈 #36, #37, #20 구현#38

Merged
bae080311 merged 6 commits into
mainfrom
ai/issue-resolve
Apr 26, 2026
Merged

[AI] feat: 이슈 #36, #37, #20 구현#38
bae080311 merged 6 commits into
mainfrom
ai/issue-resolve

Conversation

@bae080311

Copy link
Copy Markdown
Owner

Summary

Changes

  • focusActiveSection 옵션: 섹션 전환 후 포커스 이동
  • stickyElements 옵션: sticky 헤더/푸터 높이 자동 계산
  • easing 옵션: 커스텀 easing 함수 지원
  • ARIA attributes 자동 적용 (role, aria-labelledby)
  • 테스트 케이스新增

Test Results

  • 23 tests passed
  • TypeScript check passed

🤖 Generated by jump-section AI Pipeline

- 추가: 접근성 (ARIA roles + focusActiveSection option)
- 추가: sticky elements 지원 (stickyElements option)
- 추가: 커스텀 easing 함수 지원 (easing option)
- 테스트: 새 기능에 대한 테스트 cases 추가
@vercel

vercel Bot commented Apr 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
jump-section-docs Ready Ready Preview, Comment Apr 26, 2026 8:28am

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the ScrollManager with focus management, sticky element offsets, and custom easing functions. Key feedback includes fixing a string comparison bug in sticky height calculation and correcting the scroll target calculation to subtract sticky height instead of adding it. Suggestions were also made to improve accessibility by refining ARIA attributes and to enhance type safety for the easing function to avoid potential runtime errors.

Comment thread packages/core/src/ScrollManager.ts Outdated
if (element) {
const rect = element.getBoundingClientRect();
const style = window.getComputedStyle(element);
if (style.position === ' sticky' || style.position === 'fixed') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

문자열 비교 시 ' sticky'와 같이 앞에 공백이 포함되어 있습니다. window.getComputedStyle(element).position은 공백 없이 'sticky'를 반환하므로, 이 조건문은 항상 거짓이 되어 sticky 요소의 높이가 계산되지 않는 버그가 발생합니다.

Suggested change
if (style.position === ' sticky' || style.position === 'fixed') {
if (style.position === 'sticky' || style.position === 'fixed') {

Comment thread packages/core/src/ScrollManager.ts Outdated

const targetScrollTop =
elementRect.top + this.currentScrollTop - rootRect.top + this.options.offset;
elementRect.top + this.currentScrollTop - rootRect.top + this.options.offset + stickyHeight;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

stickyHeight를 타겟 스크롤 위치에 더하고 있는데, 이는 빼야 합니다. sticky 헤더가 있는 경우 해당 높이만큼 덜 스크롤해야 타겟 요소가 헤더에 가려지지 않습니다.

Suggested change
elementRect.top + this.currentScrollTop - rootRect.top + this.options.offset + stickyHeight;
elementRect.top + this.currentScrollTop - rootRect.top + this.options.offset - stickyHeight;

Comment thread packages/core/src/ScrollManager.ts Outdated
}

element.setAttribute('role', 'region');
element.setAttribute('aria-labelledby', id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

aria-labelledby를 요소 자신의 ID로 설정하면 자기 자신을 참조하게 됩니다. aria-labelledby는 보통 해당 섹션의 제목(예: h2)을 가리키는 데 사용됩니다. 적절한 제목 요소가 없다면 aria-label을 사용하거나, 사용자가 직접 설정하도록 비워두는 것이 접근성 측면에서 더 바람직합니다.

Comment thread packages/core/src/ScrollManager.ts Outdated
/** sticky 요소들의 ID 또는 element 배열입니다. 스크롤 위치 계산 시これらの 높이가 오프셋에 추가됩니다 */
stickyElements?: string[] | HTMLElement[];
/** 커스텀 easing 함수입니다. t: 0~1 사이의 진행률, 반환값: 변환된 진행률 */
easing?: (t: number) => number | undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

easing 함수의 반환 타입을 number | undefined가 아닌 number로 정의하는 것이 좋습니다. undefined가 반환될 경우 스크롤 계산 시 NaN이 발생하여 애니메이션이 비정상적으로 동작할 수 있습니다.

Suggested change
easing?: (t: number) => number | undefined;
easing?: (t: number) => number;

Comment thread packages/core/src/ScrollManager.ts Outdated
rootMargin: '-20% 0px -60% 0px',
focusActiveSection: false,
stickyElements: [],
easing: undefined as unknown as (t: number) => number | undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

수정된 easing 함수 타입에 맞춰 타입을 변경해야 합니다.

Suggested change
easing: undefined as unknown as (t: number) => number | undefined,
easing: undefined as unknown as (t: number) => number,

Comment thread packages/core/src/ScrollManager.ts Outdated
const animate = (currentTime: number) => {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const easedProgress = easing(progress)!;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

easing 함수의 반환 타입이 number로 수정되면 Non-null assertion(!)이 필요하지 않습니다.

Suggested change
const easedProgress = easing(progress)!;
const easedProgress = easing(progress);

- focusActiveSection, stickyElements, easing 옵션 설명 추가
- 새로운 메서드들 (scrollToNext, scrollToPrev 등) 추가
- Advanced Usage 예제 추가
- EN/KO 문서 모두 업데이트
@bae080311

Copy link
Copy Markdown
Owner Author

리뷰

변경 사항들을 확인했습니다.

구현된 기능

테스트

  • 23 tests passed

제안

  • 테스트 커버리지 44.64% -> 80% 목표 달성 위해 추가 테스트 권장

결론: 승인 가능합니다.

- scrollToNext/Prev 테스트 추가
- onProgressChange 테스트 추가
-各种 엣지 케이스 테스트 추가
- 51 tests로 증가
- scrollToFirst/Last 테스트
- stickyElements HTMLElement 배열 테스트
- 52 tests로 증가
- 포맷 수정
@bae080311

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly expands the ScrollManager functionality, adding support for custom easing, sticky element offsets, focus management, and accessibility attributes like role="region". It also introduces new methods for navigation and state tracking, along with updated documentation and tests. Review feedback identified critical bugs, including an incorrect string comparison for sticky positions and a logic error where sticky heights were added instead of subtracted from the scroll target. Additionally, improvements were suggested for ARIA attribute implementation, type safety in easing functions, and more precise sticky element height calculations.

Comment thread packages/core/src/ScrollManager.ts Outdated
if (element) {
const rect = element.getBoundingClientRect();
const style = window.getComputedStyle(element);
if (style.position === ' sticky' || style.position === 'fixed') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

style.position 값을 확인할 때 ' sticky'와 같이 문자열 앞에 불필요한 공백이 포함되어 있습니다. 이로 인해 sticky 요소의 높이가 합산되지 않는 버그가 발생합니다. 공백을 제거해야 합니다.

Suggested change
if (style.position === ' sticky' || style.position === 'fixed') {
if (style.position === 'sticky' || style.position === 'fixed') {

Comment thread packages/core/src/ScrollManager.ts Outdated

const targetScrollTop =
elementRect.top + this.currentScrollTop - rootRect.top + this.options.offset;
elementRect.top + this.currentScrollTop - rootRect.top + this.options.offset + stickyHeight;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

stickyHeight를 더하는 대신 빼야 합니다. 스티키 헤더가 있는 경우, 섹션이 헤더에 가려지지 않도록 스크롤 위치를 헤더 높이만큼 위로(작은 값으로) 조정해야 하기 때문입니다. 현재처럼 더하게 되면 섹션이 헤더 아래로 더 가려지게 됩니다.

Suggested change
elementRect.top + this.currentScrollTop - rootRect.top + this.options.offset + stickyHeight;
elementRect.top + this.currentScrollTop - rootRect.top + this.options.offset - stickyHeight;

Comment thread packages/core/src/ScrollManager.ts Outdated
/** sticky 요소들의 ID 또는 element 배열입니다. 스크롤 위치 계산 시これらの 높이가 오프셋에 추가됩니다 */
stickyElements?: string[] | HTMLElement[];
/** 커스텀 easing 함수입니다. t: 0~1 사이의 진행률, 반환값: 변환된 진행률 */
easing?: (t: number) => number | undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

easing 함수의 반환 타입에 undefined가 포함되어 있습니다. customScrollTo 메서드에서 이 값을 계산에 직접 사용하므로, undefined가 반환될 경우 NaN이 발생하여 스크롤이 오작동할 수 있습니다. 반환 타입을 number로 제한하는 것이 안전합니다.

Suggested change
easing?: (t: number) => number | undefined;
easing?: (t: number) => number;

Comment thread packages/core/src/ScrollManager.ts Outdated
rootMargin: '-20% 0px -60% 0px',
focusActiveSection: false,
stickyElements: [],
easing: undefined as unknown as (t: number) => number | undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

easing 옵션의 타입 정의와 일치하도록 캐스팅 타입을 수정해야 합니다.

Suggested change
easing: undefined as unknown as (t: number) => number | undefined,
easing: undefined as unknown as (t: number) => number,

Comment on lines +91 to +100
for (const el of this.options.stickyElements) {
const element = typeof el === 'string' ? document.getElementById(el) : el;
if (element) {
const rect = element.getBoundingClientRect();
const style = window.getComputedStyle(element);
if (style.position === ' sticky' || style.position === 'fixed') {
totalHeight += rect.height;
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

stickyElements에 등록된 모든 요소의 높이를 단순히 합산하고 있습니다. 만약 상단 헤더와 하단 푸터가 모두 sticky인 경우, 푸터의 높이까지 스크롤 오프셋에 포함되어 상단 스크롤 위치가 어긋나게 됩니다. 요소의 실제 위치(top/bottom)를 확인하여 상단에 고정된 요소의 높이만 계산에 반영하도록 개선이 필요합니다.

Comment thread packages/core/src/ScrollManager.ts Outdated
}

element.setAttribute('role', 'region');
element.setAttribute('aria-labelledby', id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

aria-labelledby 속성에 섹션 자체의 id를 할당하고 있습니다. aria-labelledby는 해당 요소를 설명하는 별도의 요소(예: 제목)의 ID를 참조해야 합니다. 자기 자신의 ID를 참조하는 것은 접근성 표준에 맞지 않으므로, aria-label을 사용하거나 사용자가 제목 요소의 ID를 지정할 수 있는 방식을 권장합니다.

Comment thread packages/core/src/ScrollManager.ts Outdated
const animate = (currentTime: number) => {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const easedProgress = easing(progress)!;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

easing 함수의 반환 타입을 number로 수정했다면, 여기서 불필요한 non-null assertion(!)을 제거할 수 있습니다.

Suggested change
const easedProgress = easing(progress)!;
const easedProgress = easing(progress);

- ' sticky' → 'sticky': 앞 공백으로 인해 항상 false였던 문자열 비교 버그 수정
- stickyHeight 덧셈 → 뺄셈: sticky 헤더 높이는 타겟 스크롤 위치에서 빼야 함
- 상단 고정 요소만 계산: rect.top <= 0 조건으로 하단 sticky 요소 제외
- aria-labelledby → aria-label: 자기 참조는 접근성 표준에 부적합
- easing 반환 타입: number | undefined → number (NaN 방지)
- easing non-null assertion 제거: 타입 수정으로 불필요해진 ! 제거
- JSDoc 한국어로 수정 (일본어 혼입 제거)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bae080311
bae080311 marked this pull request as ready for review April 26, 2026 15:16
@bae080311
bae080311 merged commit ccfad31 into main Apr 26, 2026
3 checks passed
@bae080311
bae080311 deleted the ai/issue-resolve branch April 26, 2026 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant