Merge pull request #3157 from overleaf/ta-outline-item-tests
Outline Item Tests GitOrigin-RevId: fa8c564ce75e3abfa9b977691635392e87cdde81
This commit is contained in:
@@ -7,14 +7,18 @@
|
||||
}]
|
||||
],
|
||||
"plugins": ["angularjs-annotate"],
|
||||
// Disable core-js polyfilling in test environment, to prevent interference
|
||||
// with mocking
|
||||
// Target our current Node version in test environment, to transform and
|
||||
// polyfill only what's necessary
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": [
|
||||
"@babel/react",
|
||||
"@babel/env"
|
||||
["@babel/env", {
|
||||
"targets": {"node": "10.21"},
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": { "version": 3 }
|
||||
}]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,5 +2,7 @@
|
||||
"file_outline",
|
||||
"the_file_outline_is_a_new_feature_click_the_icon_to_learn_more",
|
||||
"we_cant_find_any_sections_or_subsections_in_this_file",
|
||||
"find_out_more_about_the_file_outline"
|
||||
"find_out_more_about_the_file_outline",
|
||||
"expand",
|
||||
"collapse"
|
||||
]
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState, useEffect, createRef, useRef } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'
|
||||
import classNames from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import OutlineList from './outline-list'
|
||||
|
||||
function getChildrenLines(children) {
|
||||
@@ -13,6 +14,8 @@ function getChildrenLines(children) {
|
||||
}
|
||||
|
||||
function OutlineItem({ outlineItem, jumpToLine, highlightedLine }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [expanded, setExpanded] = useState(true)
|
||||
const titleElementRef = createRef()
|
||||
const isHighlightedRef = useRef(false)
|
||||
@@ -30,9 +33,11 @@ function OutlineItem({ outlineItem, jumpToLine, highlightedLine }) {
|
||||
!expanded &&
|
||||
getChildrenLines(outlineItem.children).includes(highlightedLine)
|
||||
|
||||
const isHighlighted =
|
||||
highlightedLine === outlineItem.line || hasHighlightedChild
|
||||
|
||||
const itemLinkClasses = classNames('outline-item-link', {
|
||||
'outline-item-link-highlight':
|
||||
highlightedLine === outlineItem.line || hasHighlightedChild
|
||||
'outline-item-link-highlight': isHighlighted
|
||||
})
|
||||
|
||||
function handleExpandCollapseClick() {
|
||||
@@ -64,13 +69,23 @@ function OutlineItem({ outlineItem, jumpToLine, highlightedLine }) {
|
||||
[highlightedLine, titleElementRef, isHighlightedRef]
|
||||
)
|
||||
|
||||
// don't set the aria-expanded attribute when there are no children
|
||||
const ariaExpandedValue = outlineItem.children ? expanded : undefined
|
||||
|
||||
return (
|
||||
<li className={mainItemClasses}>
|
||||
<li
|
||||
className={mainItemClasses}
|
||||
aria-expanded={ariaExpandedValue}
|
||||
role="treeitem"
|
||||
aria-current={isHighlighted}
|
||||
aria-label={outlineItem.title}
|
||||
>
|
||||
<div className="outline-item-row">
|
||||
{outlineItem.children ? (
|
||||
<button
|
||||
className="outline-item-expand-collapse-btn"
|
||||
onClick={handleExpandCollapseClick}
|
||||
aria-label={expanded ? t('collapse') : t('expand')}
|
||||
>
|
||||
<i className={expandCollapseIconClasses} />
|
||||
</button>
|
||||
@@ -100,7 +115,7 @@ OutlineItem.propTypes = {
|
||||
outlineItem: PropTypes.exact({
|
||||
line: PropTypes.number.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
level: PropTypes.number.isRequired,
|
||||
level: PropTypes.number,
|
||||
children: PropTypes.array
|
||||
}).isRequired,
|
||||
jumpToLine: PropTypes.func.isRequired,
|
||||
|
||||
@@ -8,7 +8,7 @@ function OutlineList({ outline, jumpToLine, isRoot, highlightedLine }) {
|
||||
'outline-item-list-root': isRoot
|
||||
})
|
||||
return (
|
||||
<ul className={listClasses}>
|
||||
<ul className={listClasses} role={isRoot ? 'tree' : 'group'}>
|
||||
{outline.map((outlineItem, idx) => {
|
||||
return (
|
||||
<OutlineItem
|
||||
|
||||
+3
@@ -8,3 +8,6 @@ require('jsdom-global/register')
|
||||
// has a nicer failure messages
|
||||
const chai = require('chai')
|
||||
chai.use(require('sinon-chai'))
|
||||
|
||||
window.i18n = { currentLangCode: 'en' }
|
||||
require('../../frontend/js/i18n')
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { expect } from 'chai'
|
||||
import React from 'react'
|
||||
import sinon from 'sinon'
|
||||
import { screen, render, fireEvent } from '@testing-library/react'
|
||||
|
||||
import OutlineItem from '../../../../../frontend/js/features/outline/components/outline-item'
|
||||
|
||||
describe('<OutlineItem />', function() {
|
||||
before(function() {
|
||||
this.jumpToLine = sinon.stub()
|
||||
})
|
||||
|
||||
afterEach(function() {
|
||||
this.jumpToLine.reset()
|
||||
})
|
||||
|
||||
it('renders basic item', function() {
|
||||
const outlineItem = {
|
||||
title: 'Test Title',
|
||||
line: 1
|
||||
}
|
||||
render(
|
||||
<OutlineItem outlineItem={outlineItem} jumpToLine={this.jumpToLine} />
|
||||
)
|
||||
|
||||
screen.getByRole('treeitem', { current: false })
|
||||
screen.getByRole('button', { name: outlineItem.title })
|
||||
expect(screen.queryByRole('button', { name: 'Collapse' })).to.not.exist
|
||||
})
|
||||
|
||||
it('collapses and expands', function() {
|
||||
const outlineItem = {
|
||||
title: 'Parent',
|
||||
line: 1,
|
||||
children: [{ title: 'Child', line: 2 }]
|
||||
}
|
||||
render(
|
||||
<OutlineItem outlineItem={outlineItem} jumpToLine={this.jumpToLine} />
|
||||
)
|
||||
|
||||
const collapseButton = screen.getByRole('button', { name: 'Collapse' })
|
||||
|
||||
// test that children are rendered
|
||||
screen.getByRole('button', { name: 'Child' })
|
||||
|
||||
fireEvent.click(collapseButton)
|
||||
|
||||
screen.getByRole('button', { name: 'Expand' })
|
||||
|
||||
expect(screen.queryByRole('button', { name: 'Child' })).to.not.exist
|
||||
})
|
||||
|
||||
it('highlights', function() {
|
||||
const outlineItem = {
|
||||
title: 'Parent',
|
||||
line: 1
|
||||
}
|
||||
|
||||
render(
|
||||
<OutlineItem
|
||||
outlineItem={outlineItem}
|
||||
jumpToLine={this.jumpToLine}
|
||||
highlightedLine={1}
|
||||
/>
|
||||
)
|
||||
|
||||
screen.getByRole('treeitem', { current: true })
|
||||
})
|
||||
|
||||
it('highlights when has collapsed highlighted child', function() {
|
||||
const outlineItem = {
|
||||
title: 'Parent',
|
||||
line: 1,
|
||||
children: [{ title: 'Child', line: 2 }]
|
||||
}
|
||||
render(
|
||||
<OutlineItem
|
||||
outlineItem={outlineItem}
|
||||
jumpToLine={this.jumpToLine}
|
||||
highlightedLine={2}
|
||||
/>
|
||||
)
|
||||
|
||||
screen.getByRole('treeitem', { name: 'Parent', current: false })
|
||||
screen.getByRole('treeitem', { name: 'Child', current: true })
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Collapse' }))
|
||||
|
||||
screen.getByRole('treeitem', { name: 'Parent', current: true })
|
||||
})
|
||||
|
||||
it('click and double-click jump to location', function() {
|
||||
const outlineItem = {
|
||||
title: 'Parent',
|
||||
line: 1
|
||||
}
|
||||
render(
|
||||
<OutlineItem outlineItem={outlineItem} jumpToLine={this.jumpToLine} />
|
||||
)
|
||||
|
||||
const titleButton = screen.getByRole('button', { name: outlineItem.title })
|
||||
|
||||
fireEvent.click(titleButton)
|
||||
sinon.assert.calledOnce(this.jumpToLine)
|
||||
sinon.assert.calledWith(this.jumpToLine, 1, false)
|
||||
|
||||
this.jumpToLine.reset()
|
||||
fireEvent.doubleClick(titleButton)
|
||||
sinon.assert.calledOnce(this.jumpToLine)
|
||||
sinon.assert.calledWith(this.jumpToLine, 1, true)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user