digitalfabrik/integreat-app

View on GitHub
web/src/components/SearchListItem.tsx

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
import React, { ReactElement } from 'react'
import Highlighter from 'react-highlight-words'
import { Link } from 'react-router-dom'
import styled, { useTheme } from 'styled-components'

import { getExcerpt, normalizeString } from 'shared'

import { EXCERPT_MAX_CHARS } from '../constants'

const Row = styled.li`
  width: 100%;
`

const CategoryThumbnail = styled.img`
  width: 30px;
  height: 30px;
  padding: 0 5px;
  flex-shrink: 0;
  object-fit: contain;
`

const CategoryTitleContainer = styled.div`
  display: flex;
  align-items: center;
  flex-direction: row;
`

const CategoryItemContainer = styled.div`
  display: flex;
  flex-direction: column;
  padding: 15px 5px;
  color: inherit;
  text-decoration: inherit;
  height: 100%;
  min-width: 1px; /* needed to enable line breaks for too long words, exact value doesn't matter */
  flex-grow: 1;
  word-wrap: break-word;
`

const StyledHighlighter = styled(Highlighter)`
  display: inline-block;
`

const StyledLink = styled(Link)`
  display: inline-flex;
  margin: 0 auto;
  width: inherit;
  border-bottom: 1px solid ${props => props.theme.colors.themeColor};

  &:hover {
    color: inherit;
    text-decoration: inherit;
    transition: background-color 0.5s ease;
    background-color: ${props => props.theme.colors.backgroundAccentColor};
  }
`

type SearchListItemProps = {
  title: string
  contentWithoutHtml: string
  query: string
  path: string
  thumbnail: string | null
}

const SearchListItem = ({ title, contentWithoutHtml, query, path, thumbnail }: SearchListItemProps): ReactElement => {
  const theme = useTheme()

  const excerpt = getExcerpt(contentWithoutHtml, { query, maxChars: EXCERPT_MAX_CHARS })

  const Title = (
    <Highlighter
      dir='auto'
      searchWords={query ? [query] : []}
      aria-label={title}
      autoEscape
      sanitize={normalizeString}
      highlightStyle={{ backgroundColor: theme.colors.backgroundColor, fontWeight: 'bold' }}
      textToHighlight={title}
    />
  )

  const Content = query && excerpt.length > 0 && (
    <StyledHighlighter
      aria-label={excerpt}
      searchWords={[query]}
      autoEscape
      sanitize={normalizeString}
      textToHighlight={excerpt}
      highlightStyle={{ backgroundColor: theme.colors.backgroundColor, fontWeight: 'bold' }}
    />
  )

  return (
    <Row>
      <StyledLink to={path}>
        <CategoryItemContainer dir='auto'>
          <CategoryTitleContainer>
            {!!thumbnail && <CategoryThumbnail alt='' src={thumbnail} />}
            {Title}
          </CategoryTitleContainer>
          <div style={{ margin: '0 5px', fontSize: '12px' }} dir='auto'>
            {Content}
          </div>
        </CategoryItemContainer>
      </StyledLink>
    </Row>
  )
}

export default SearchListItem