Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lang/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,10 @@
"defaultMessage": "invites you to join the circle {circleName} , and you can experience it for {freePeriod} days for free",
"description": "src/components/Notice/CircleNotice/CircleInvitationNotice.tsx"
},
"Ix3e3Q": {
"defaultMessage": "Comment",
"description": "src/components/Forms/CommentForm/index.tsx"
},
"J6f6iN": {
"defaultMessage": "Number of comments",
"description": "src/components/ArticleDigest/Published/FooterActions/index.tsx"
Expand Down Expand Up @@ -1252,6 +1256,10 @@
"defaultMessage": "Most comments",
"description": "src/views/Me/Works/Published/SortTabs.tsx"
},
"aZ6EYx": {
"defaultMessage": "Send",
"description": "src/components/Forms/CommentForm/index.tsx"
},
"aa0nss": {
"defaultMessage": "Unpin from Trending",
"description": "src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx"
Expand Down
8 changes: 8 additions & 0 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,10 @@
"defaultMessage": "invites you to join the circle {circleName} , and you can experience it for {freePeriod} days for free",
"description": "src/components/Notice/CircleNotice/CircleInvitationNotice.tsx"
},
"Ix3e3Q": {
"defaultMessage": "Comment",
"description": "src/components/Forms/CommentForm/index.tsx"
},
"J6f6iN": {
"defaultMessage": "Number of comments",
"description": "src/components/ArticleDigest/Published/FooterActions/index.tsx"
Expand Down Expand Up @@ -1252,6 +1256,10 @@
"defaultMessage": "Most comments",
"description": "src/views/Me/Works/Published/SortTabs.tsx"
},
"aZ6EYx": {
"defaultMessage": "Send",
"description": "src/components/Forms/CommentForm/index.tsx"
},
"aa0nss": {
"defaultMessage": "Unpin from Trending",
"description": "src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx"
Expand Down
8 changes: 8 additions & 0 deletions lang/zh-Hans.json
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,10 @@
"defaultMessage": "邀你加入围炉 {circleName} ,你可以免费体验 {freePeriod} 天",
"description": "src/components/Notice/CircleNotice/CircleInvitationNotice.tsx"
},
"Ix3e3Q": {
"defaultMessage": "发布评论",
"description": "src/components/Forms/CommentForm/index.tsx"
},
"J6f6iN": {
"defaultMessage": "评论数量",
"description": "src/components/ArticleDigest/Published/FooterActions/index.tsx"
Expand Down Expand Up @@ -1252,6 +1256,10 @@
"defaultMessage": "最多评论",
"description": "src/views/Me/Works/Published/SortTabs.tsx"
},
"aZ6EYx": {
"defaultMessage": "送出",
"description": "src/components/Forms/CommentForm/index.tsx"
},
"aa0nss": {
"defaultMessage": "取消精选",
"description": "src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx"
Expand Down
8 changes: 8 additions & 0 deletions lang/zh-Hant.json
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,10 @@
"defaultMessage": "邀你加入圍爐 {circleName} ,你可以免費體驗 {freePeriod} 天",
"description": "src/components/Notice/CircleNotice/CircleInvitationNotice.tsx"
},
"Ix3e3Q": {
"defaultMessage": "發布評論",
"description": "src/components/Forms/CommentForm/index.tsx"
},
"J6f6iN": {
"defaultMessage": "評論數量",
"description": "src/components/ArticleDigest/Published/FooterActions/index.tsx"
Expand Down Expand Up @@ -1252,6 +1256,10 @@
"defaultMessage": "最多評論",
"description": "src/views/Me/Works/Published/SortTabs.tsx"
},
"aZ6EYx": {
"defaultMessage": "送出",
"description": "src/components/Forms/CommentForm/index.tsx"
},
"aa0nss": {
"defaultMessage": "取消精選",
"description": "src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx"
Expand Down
41 changes: 41 additions & 0 deletions src/components/Forms/CommentForm/CommentForm.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import userEvent from '@testing-library/user-event'
import { describe, expect, it, vi } from 'vitest'

import { render, screen } from '~/common/utils/test'

import { CommentForm } from './'

describe('<Forms/CommentForm>', () => {
it('should render a CommentForm', async () => {
const placeholder = 'Test placeholder'
const handleSubmit = vi.fn()

const { container } = render(
<CommentForm
type="article"
submitCallback={handleSubmit}
placeholder={placeholder}
/>
)

// eslint-disable-next-line
const $editor = container.querySelector('.ProseMirror')!
expect($editor).toBeInTheDocument()

// eslint-disable-next-line
const $placeholder = container.querySelector('[data-placeholder]')!
expect($placeholder).toHaveAttribute('data-placeholder', placeholder)

const comment = 'Test comment'
await userEvent.type($editor, comment)
expect($editor).toHaveTextContent(comment)

// submit & loading
const $submit = screen.getByRole('button', { name: 'Send' })
expect($submit).toBeInTheDocument()
$submit.click()
expect(
screen.queryByRole('button', { name: 'Send' })
).not.toBeInTheDocument()
})
})
21 changes: 14 additions & 7 deletions src/components/Forms/CommentForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { useQuery } from '@apollo/react-hooks'
import dynamic from 'next/dynamic'
import { useContext, useState } from 'react'
import { useState } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'

import { dom, stripHtml, translate } from '~/common/utils'
import { dom, stripHtml } from '~/common/utils'
import {
Button,
IconSpinner16,
LanguageContext,
Spinner,
TextIcon,
Translate,
useMutation,
} from '~/components'
import PUT_COMMENT from '~/components/GQL/mutations/putComment'
Expand Down Expand Up @@ -52,7 +51,7 @@ export const CommentForm: React.FC<CommentFormProps> = ({

placeholder,
}) => {
const { lang } = useContext(LanguageContext)
const intl = useIntl()

// retrieve comment draft
const commentDraftId = `${articleId || circleId}-${type}-${commentId || 0}-${
Expand Down Expand Up @@ -132,7 +131,11 @@ export const CommentForm: React.FC<CommentFormProps> = ({
className={styles.form}
id={formId}
onSubmit={handleSubmit}
aria-label={translate({ id: 'putComment', lang })}
aria-label={intl.formatMessage({
defaultMessage: 'Comment',
id: 'Ix3e3Q',
description: 'src/components/Forms/CommentForm/index.tsx',
})}
>
<section className={styles.content}>
<CommentEditor
Expand All @@ -158,7 +161,11 @@ export const CommentForm: React.FC<CommentFormProps> = ({
icon={isSubmitting && <IconSpinner16 size="sm" />}
>
{isSubmitting ? null : (
<Translate zh_hant="送出" zh_hans="送出" en="Send" />
<FormattedMessage
defaultMessage="Send"
id="aZ6EYx"
description="src/components/Forms/CommentForm/index.tsx"
/>
)}
</TextIcon>
</Button>
Expand Down
62 changes: 62 additions & 0 deletions src/components/Search/SearchBar/SearchBar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'

import { INPUT_DEBOUNCE } from '~/common/enums'
import { act, fireEvent, render, screen } from '~/common/utils/test'
import { SearchBar } from '~/components'

beforeEach(() => {
// Tests should run in serial for improved isolation
// To prevent collision with global state, reset all toasts for each test
vi.useFakeTimers()
})

afterEach(() => {
act(() => {
vi.runAllTimers()
vi.useRealTimers()
})
})

const waitTime = (time: number) => {
act(() => {
vi.advanceTimersByTime(time)
})
}

describe('<SearchBar>', () => {
it('should render a SeachBar', () => {
render(<SearchBar />)

// search button
const $searchButton = screen.getByRole('button', { name: 'Search' })
expect($searchButton).toBeInTheDocument()

// search input
const $searchInput = screen.getByPlaceholderText('Search')
expect($searchInput).toBeInTheDocument()
expect($searchInput).toHaveAttribute('type', 'search')
expect($searchInput).toHaveAttribute('aria-label', 'Search')
expect($searchInput).toHaveAttribute('name', 'q')
expect($searchInput).toHaveAttribute('value', '')
})

it('should update search bar value when typing', async () => {
const handleOnChange = vi.fn()

render(<SearchBar onChange={handleOnChange} />)

const $searchInput = screen.getByPlaceholderText('Search')
expect($searchInput).toBeInTheDocument()

const searchValue = 'test'
fireEvent.change($searchInput, { target: { value: searchValue } })
expect($searchInput).toHaveValue(searchValue)

waitTime(INPUT_DEBOUNCE)
expect(handleOnChange).toBeCalledWith(searchValue)

// dropdown
const $dropdown = screen.getByText(/Loading/i)
expect($dropdown).toBeInTheDocument()
})
})
33 changes: 13 additions & 20 deletions src/components/Search/SearchBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,7 @@ export const SearchBar: React.FC<SearchBarProps> = ({
const [debouncedSearch] = useDebounce(search, INPUT_DEBOUNCE)
const intl = useIntl()

const textAriaLabel = intl.formatMessage({
defaultMessage: 'Search',
id: 'xmcVZ0',
})
const textPlaceholder = intl.formatMessage({
const searchText = intl.formatMessage({
defaultMessage: 'Search',
id: 'xmcVZ0',
})
Expand Down Expand Up @@ -121,29 +117,26 @@ export const SearchBar: React.FC<SearchBarProps> = ({
}

useNativeEventListener('keydown', (event: KeyboardEvent) => {
if (event.code.toLowerCase() === KEYVALUE.arrowUp) {
if (!showDropdown) return
if (!showDropdown) return

const code = event.code.toLowerCase()
if (code === KEYVALUE.arrowUp) {
event.preventDefault()
const activeIndex = items.indexOf(activeItem)
if (activeIndex === 0) return

setActiveItem(items[activeIndex - 1])
}

if (event.code.toLowerCase() === KEYVALUE.arrowDown) {
if (!showDropdown) return

if (code === KEYVALUE.arrowDown) {
event.preventDefault()
const activeIndex = items.indexOf(activeItem)
if (activeIndex === items.length - 1) return

setActiveItem(items[activeIndex + 1])
}

if (event.code.toLowerCase() === KEYVALUE.escape) {
if (!showDropdown) return

if (code === KEYVALUE.escape) {
setShowDropdown(false)
}
})
Expand Down Expand Up @@ -199,19 +192,19 @@ export const SearchBar: React.FC<SearchBarProps> = ({
<form
className={styles.form}
onSubmit={handleSubmit}
aria-label={textPlaceholder}
aria-label={searchText}
role="search"
autoComplete="off"
action=""
>
<input
// FIMXME: FOUC on re-render
// FIXME: FOUC on re-render
style={{ borderColor: 'var(--color-line-grey-light)' }}
type="search"
name="q"
ref={searchTextInput}
aria-label={textAriaLabel}
placeholder={textPlaceholder}
aria-label={searchText}
placeholder={searchText}
autoCorrect="off"
onChange={(e) => {
handleChange(e)
Expand Down Expand Up @@ -272,13 +265,13 @@ export const SearchBar: React.FC<SearchBarProps> = ({
ref={ref}
>
<input
// FIMXME: FOUC on re-render
// FIXME: FOUC on re-render
style={{ borderColor: 'var(--color-line-grey-light)' }}
type="search"
name="q"
ref={searchTextInput}
aria-label={textAriaLabel}
placeholder={textPlaceholder}
aria-label={searchText}
placeholder={searchText}
value={values.q}
onChange={(e) => {
handleChange(e)
Expand Down
26 changes: 26 additions & 0 deletions vitest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,32 @@ Object.defineProperty(window, 'matchMedia', {
})),
})

document.createRange = () => {
const range = new Range()

range.getBoundingClientRect = vi.fn(() => ({
x: 851.671875,
y: 200.046875,
width: 8.34375,
height: 17,
top: 967.046875,
right: 860.015625,
bottom: 984.046875,
left: 851.671875,
toJSON: vi.fn(),
}))

// @ts-ignore
range.getClientRects = vi.fn(() => ({
item: () => null,
length: 0,
}))

return range
}

document.elementFromPoint = vi.fn(() => null)

vi.mock('next/router', () => require('next-router-mock'))

vi.mock('next/dynamic', async () => {
Expand Down