feat: create HighlightsTab component with color picker
Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
58
__tests__/components/highlights-tab.test.tsx
Normal file
58
__tests__/components/highlights-tab.test.tsx
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { render, screen, fireEvent } from '@testing-library/react'
|
||||||
|
import { HighlightsTab } from '@/components/bible/highlights-tab'
|
||||||
|
import { BibleVerse } from '@/types'
|
||||||
|
|
||||||
|
describe('HighlightsTab', () => {
|
||||||
|
const mockVerse: BibleVerse = {
|
||||||
|
id: 'v-1',
|
||||||
|
verseNum: 1,
|
||||||
|
text: 'In the beginning God created the heavens and the earth'
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should render highlight button when verse not highlighted', () => {
|
||||||
|
render(
|
||||||
|
<HighlightsTab
|
||||||
|
verse={mockVerse}
|
||||||
|
isHighlighted={false}
|
||||||
|
currentColor={null}
|
||||||
|
onToggleHighlight={() => {}}
|
||||||
|
onColorChange={() => {}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(screen.getByText(/Highlight/i)).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render color picker when verse is highlighted', () => {
|
||||||
|
render(
|
||||||
|
<HighlightsTab
|
||||||
|
verse={mockVerse}
|
||||||
|
isHighlighted={true}
|
||||||
|
currentColor="yellow"
|
||||||
|
onToggleHighlight={() => {}}
|
||||||
|
onColorChange={() => {}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(screen.getByText(/Remove highlight/i)).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should call onColorChange when color is selected', () => {
|
||||||
|
const onColorChange = jest.fn()
|
||||||
|
|
||||||
|
render(
|
||||||
|
<HighlightsTab
|
||||||
|
verse={mockVerse}
|
||||||
|
isHighlighted={true}
|
||||||
|
currentColor="yellow"
|
||||||
|
onToggleHighlight={() => {}}
|
||||||
|
onColorChange={onColorChange}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
const blueButton = screen.getByTestId('color-blue')
|
||||||
|
fireEvent.click(blueButton)
|
||||||
|
|
||||||
|
expect(onColorChange).toHaveBeenCalledWith('blue')
|
||||||
|
})
|
||||||
|
})
|
||||||
90
components/bible/highlights-tab.tsx
Normal file
90
components/bible/highlights-tab.tsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
'use client'
|
||||||
|
import { Box, Button, Grid, Typography, Divider } from '@mui/material'
|
||||||
|
import { BibleVerse, HighlightColor } from '@/types'
|
||||||
|
|
||||||
|
const HIGHLIGHT_COLORS: HighlightColor[] = ['yellow', 'orange', 'pink', 'blue']
|
||||||
|
|
||||||
|
const COLOR_MAP: Record<HighlightColor, { bg: string; hex: string }> = {
|
||||||
|
yellow: { bg: 'rgba(255, 193, 7, 0.3)', hex: '#FFC107' },
|
||||||
|
orange: { bg: 'rgba(255, 152, 0, 0.3)', hex: '#FF9800' },
|
||||||
|
pink: { bg: 'rgba(233, 30, 99, 0.3)', hex: '#E91E63' },
|
||||||
|
blue: { bg: 'rgba(33, 150, 243, 0.3)', hex: '#2196F3' }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HighlightsTabProps {
|
||||||
|
verse: BibleVerse | null
|
||||||
|
isHighlighted: boolean
|
||||||
|
currentColor: HighlightColor | null
|
||||||
|
onToggleHighlight: () => void
|
||||||
|
onColorChange: (color: HighlightColor) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function HighlightsTab({
|
||||||
|
verse,
|
||||||
|
isHighlighted,
|
||||||
|
currentColor,
|
||||||
|
onToggleHighlight,
|
||||||
|
onColorChange
|
||||||
|
}: HighlightsTabProps) {
|
||||||
|
if (!verse) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ p: 2 }}>
|
||||||
|
{!isHighlighted ? (
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={onToggleHighlight}
|
||||||
|
>
|
||||||
|
Highlight this verse
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
color="error"
|
||||||
|
onClick={onToggleHighlight}
|
||||||
|
sx={{ mb: 2 }}
|
||||||
|
>
|
||||||
|
Remove highlight
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
||||||
|
Highlight Color
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Grid container spacing={1} sx={{ mb: 2 }}>
|
||||||
|
{HIGHLIGHT_COLORS.map((color) => (
|
||||||
|
<Grid item xs={3} key={color}>
|
||||||
|
<Button
|
||||||
|
data-testid={`color-${color}`}
|
||||||
|
fullWidth
|
||||||
|
variant={currentColor === color ? 'contained' : 'outlined'}
|
||||||
|
onClick={() => onColorChange(color)}
|
||||||
|
sx={{
|
||||||
|
bgcolor: COLOR_MAP[color].bg,
|
||||||
|
borderColor: COLOR_MAP[color].hex,
|
||||||
|
border: currentColor === color ? `2px solid ${COLOR_MAP[color].hex}` : undefined,
|
||||||
|
minHeight: 50,
|
||||||
|
textTransform: 'capitalize',
|
||||||
|
color: currentColor === color ? '#000' : 'inherit'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{color}
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Divider sx={{ my: 2 }} />
|
||||||
|
|
||||||
|
<Typography variant="body2" color="textSecondary">
|
||||||
|
You can highlight the same verse multiple times with different colors.
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { useState, useEffect } from 'react'
|
|||||||
import { Box, Paper, Typography, Tabs, Tab, IconButton, useMediaQuery, useTheme, TextField, Button } from '@mui/material'
|
import { Box, Paper, Typography, Tabs, Tab, IconButton, useMediaQuery, useTheme, TextField, Button } from '@mui/material'
|
||||||
import { Close, Bookmark, BookmarkBorder } from '@mui/icons-material'
|
import { Close, Bookmark, BookmarkBorder } from '@mui/icons-material'
|
||||||
import { BibleVerse } from '@/types'
|
import { BibleVerse } from '@/types'
|
||||||
|
import { HighlightsTab } from './highlights-tab'
|
||||||
|
|
||||||
interface VersDetailsPanelProps {
|
interface VersDetailsPanelProps {
|
||||||
verse: BibleVerse | null
|
verse: BibleVerse | null
|
||||||
@@ -118,9 +119,13 @@ export function VersDetailsPanel({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{tabValue === 1 && (
|
{tabValue === 1 && (
|
||||||
<Typography variant="body2" color="text.secondary">
|
<HighlightsTab
|
||||||
Highlight colors coming soon
|
verse={verse}
|
||||||
</Typography>
|
isHighlighted={false} // TODO: get from state
|
||||||
|
currentColor={null} // TODO: get from state
|
||||||
|
onToggleHighlight={() => {}} // TODO: implement
|
||||||
|
onColorChange={() => {}} // TODO: implement
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{tabValue === 2 && (
|
{tabValue === 2 && (
|
||||||
|
|||||||
Reference in New Issue
Block a user