(({className, ...props}, forwardRef) => {
- return
-})
+ return (
+ )}
+ className={clsx(className, classes.TimelineBreak)}
+ ref={forwardRef as React.ForwardedRef}
+ />
+ )
+ },
+)
TimelineBreak.displayName = 'TimelineBreak'
diff --git a/packages/react/src/Timeline/__tests__/Timeline.test.tsx b/packages/react/src/Timeline/__tests__/Timeline.test.tsx
index c7f37362159..66a1e9b7061 100644
--- a/packages/react/src/Timeline/__tests__/Timeline.test.tsx
+++ b/packages/react/src/Timeline/__tests__/Timeline.test.tsx
@@ -1,12 +1,28 @@
import {render} from '@testing-library/react'
+import type {ReactElement} from 'react'
import {describe, expect, it} from 'vitest'
import Timeline from '..'
+import {FeatureFlags} from '../../FeatureFlags'
import {implementsClassName} from '../../utils/testing'
import classes from '../Timeline.module.css'
+function renderWithListSemantics(ui: ReactElement) {
+ return render({ui})
+}
+
describe('Timeline', () => {
implementsClassName(Timeline, classes.Timeline)
+ it('renders as a div by default (flag off)', () => {
+ const {container} = render()
+ expect(container.firstChild?.nodeName).toBe('DIV')
+ })
+
+ it('does not set role="list" by default (flag off)', () => {
+ const {container} = render()
+ expect(container.firstChild).not.toHaveAttribute('role')
+ })
+
it('renders with clipSidebar prop (boolean)', () => {
const {container} = render()
expect(container.firstChild).toHaveAttribute('data-clip-sidebar', 'both')
@@ -38,8 +54,41 @@ describe('Timeline', () => {
})
})
+describe('Timeline with primer_react_timeline_list_semantics flag', () => {
+ it('renders as an ordered list', () => {
+ const {container} = renderWithListSemantics()
+ expect(container.firstChild?.nodeName).toBe('OL')
+ })
+
+ it('has role="list" to restore semantics in Safari/VoiceOver', () => {
+ const {container} = renderWithListSemantics()
+ expect(container.firstChild).toHaveAttribute('role', 'list')
+ })
+
+ it('renders items as list items', () => {
+ const {container} = renderWithListSemantics(
+
+
+ ,
+ )
+ expect(container.querySelector('ol > li')).not.toBeNull()
+ })
+
+ it('renders break as a presentational list item', () => {
+ const {container} = renderWithListSemantics()
+ expect(container.firstChild?.nodeName).toBe('LI')
+ expect(container.firstChild).toHaveAttribute('role', 'presentation')
+ })
+})
+
describe('Timeline.Item', () => {
implementsClassName(Timeline.Item, classes.TimelineItem)
+
+ it('renders as a div by default (flag off)', () => {
+ const {container} = render()
+ expect(container.firstChild?.nodeName).toBe('DIV')
+ })
+
it('renders with condensed prop', () => {
const {container} = render()
expect(container).toMatchSnapshot()
@@ -71,6 +120,12 @@ describe('Timeline.Body', () => {
describe('Timeline.Break', () => {
implementsClassName(Timeline.Break, classes.TimelineBreak)
+
+ it('renders as a div by default (flag off)', () => {
+ const {container} = render()
+ expect(container.firstChild?.nodeName).toBe('DIV')
+ expect(container.firstChild).not.toHaveAttribute('role')
+ })
})
describe('Timeline.Actions', () => {