• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DLR-SC / ESID / 16418551684

21 Jul 2025 01:37PM UTC coverage: 6.386% (-47.7%) from 54.09%
16418551684

Pull #414

github

fifth-island
fix: Add missing license file for pdf.js worker
Pull Request #414: feat: Implement basic article search feature

411 of 532 branches covered (77.26%)

Branch coverage included in aggregate %.

213 of 61424 new or added lines in 13 files covered. (0.35%)

136 existing lines in 7 files now uncovered.

4041 of 69186 relevant lines covered (5.84%)

0.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.94
/src/components/OnboardingComponents/SemanticSearch/SemanticSearchContainer.tsx
1
// SPDX-FileCopyrightText: 2024 German Aerospace Center (DLR)
2
// SPDX-License-Identifier: Apache-2.0
3

4
import React, {useState} from 'react';
1✔
5
import {Box, Typography, Divider, TextField, IconButton, InputAdornment, CircularProgress} from '@mui/material';
1✔
6
import {useSemanticSearch} from 'context/SemanticSearchContext';
1✔
7
import QuerySuggestions from './QuerySuggestions';
1✔
8
import {useTranslation} from 'react-i18next';
1✔
9
import SearchIcon from '@mui/icons-material/Search';
1✔
10
import {useAppDispatch, useAppSelector} from 'store/hooks';
1✔
11
import {setSemanticSearchQuery} from 'store/SemanticSearchSlice';
1✔
12
import SearchResultsList from './SearchResultsList';
1✔
13
import ArticleDialog from './ArticleDialog';
1✔
14
import {SearchResult} from 'types/semanticSearch';
15

16
/**
17
 * Main container for the Semantic Search feature.
18
 * It will manage the search state and orchestrate the child components.
19
 */
20
export default function SemanticSearchContainer(): JSX.Element {
5✔
21
  const {searchResults, isLoading, performSearch, clearSearch} = useSemanticSearch();
5✔
22
  const dispatch = useAppDispatch();
5✔
23
  const query = useAppSelector((state) => state.semanticSearch.searchQuery);
5✔
24
  const searchStatus = useAppSelector((state) => state.semanticSearch.searchStatus);
5✔
25
  const {t} = useTranslation('global');
5✔
26
  const [selectedArticle, setSelectedArticle] = useState<SearchResult | null>(null);
5✔
27

28
  const suggestions = [
5✔
29
    t('semanticSearch.suggestions.flood'),
5✔
30
    t('semanticSearch.suggestions.calibration'),
5✔
31
    t('semanticSearch.suggestions.heatwave'),
5✔
32
  ];
5✔
33

34
  const handleSearch = () => {
5✔
NEW
35
    performSearch(query);
×
NEW
36
  };
×
37

38
  const handleClear = () => {
5✔
NEW
39
    dispatch(setSemanticSearchQuery(''));
×
NEW
40
    clearSearch();
×
NEW
41
  };
×
42

43
  const handleSuggestionClick = (suggestion: string) => {
5✔
NEW
44
    dispatch(setSemanticSearchQuery(suggestion));
×
NEW
45
    performSearch(suggestion);
×
NEW
46
  };
×
47

48
  const handleResultClick = (article: SearchResult) => {
5✔
NEW
49
    setSelectedArticle(article);
×
NEW
50
  };
×
51

52
  const handleCloseDialog = () => {
5✔
NEW
53
    setSelectedArticle(null);
×
NEW
54
  };
×
55

56
  return (
5✔
57
    <Box mt={4}>
5✔
58
      <Divider sx={{mb: 3}} />
5✔
59
      <Typography variant='h2' gutterBottom>
5✔
60
        {t('semanticSearch.title')}
5✔
61
      </Typography>
5✔
62
      <Typography variant='body1' paragraph sx={{color: 'GrayText'}}>
5✔
63
        {t('semanticSearch.description')}
5✔
64
      </Typography>
5✔
65

66
      <TextField
5✔
67
        fullWidth
5✔
68
        size='small'
5✔
69
        variant='outlined'
5✔
70
        placeholder={t('semanticSearch.placeholder')}
5✔
71
        value={query}
5✔
72
        onChange={(e) => dispatch(setSemanticSearchQuery(e.target.value))}
5✔
73
        onKeyDown={(e) => {
5✔
NEW
74
          if (e.key === 'Enter') {
×
NEW
75
            handleSearch();
×
NEW
76
          } else if (e.key === 'Escape') {
×
NEW
77
            handleClear();
×
NEW
78
          }
×
NEW
79
        }}
×
80
        InputProps={{
5✔
81
          sx: {
5✔
82
            paddingRight: '4px',
5✔
83
          },
5✔
84
          endAdornment: (
5✔
85
            <InputAdornment position='end'>
5✔
86
              <IconButton
5✔
87
                onClick={handleSearch}
5✔
88
                disabled={isLoading}
5✔
89
                sx={{
5✔
90
                  width: '32px',
5✔
91
                  height: '32px',
5✔
92
                  borderRadius: '4px', // Standard border radius
5✔
93
                  backgroundColor: 'primary.main',
5✔
94
                  color: 'primary.contrastText',
5✔
95
                  '&:hover': {
5✔
96
                    backgroundColor: 'primary.dark',
5✔
97
                  },
5✔
98
                  '&.Mui-disabled': {
5✔
99
                    backgroundColor: 'action.disabledBackground',
5✔
100
                  },
5✔
101
                }}
5✔
102
              >
103
                {isLoading ? <CircularProgress size={24} color='inherit' /> : <SearchIcon sx={{fontSize: '16px'}} />}
5!
104
              </IconButton>
5✔
105
            </InputAdornment>
5✔
106
          ),
107
        }}
5✔
108
      />
5✔
109

110
      <QuerySuggestions suggestions={suggestions} onSuggestionClick={handleSuggestionClick} />
5✔
111

112
      <Box
5✔
113
        mt={3}
5✔
114
        sx={{
5✔
115
          maxHeight: '450px',
5✔
116
          overflowY: 'auto',
5✔
117
          p: '2px',
5✔
118
        }}
5✔
119
      >
120
        {searchResults.length > 0 && (
5!
NEW
121
          <Typography variant='body2' sx={{color: 'GrayText', mb: 1.5, px: 1}}>
×
NEW
122
            {t(
×
NEW
123
              searchResults.length === 1
×
NEW
124
                ? 'semanticSearch.results.resultsFound_one'
×
NEW
125
                : 'semanticSearch.results.resultsFound_other',
×
NEW
126
              {count: searchResults.length}
×
NEW
127
            )}{' '}
×
128
            &quot;
NEW
129
            <Box component='span' sx={{fontStyle: 'italic', color: 'primary.main'}}>
×
NEW
130
              {query}
×
NEW
131
            </Box>
×
132
            &quot;
NEW
133
          </Typography>
×
134
        )}
135
        {isLoading ? (
5!
NEW
136
          <Box display='flex' justifyContent='center' alignItems='center' sx={{py: 4}}>
×
NEW
137
            <CircularProgress />
×
NEW
138
          </Box>
×
139
        ) : searchResults.length > 0 ? (
5!
NEW
140
          <SearchResultsList results={searchResults} onResultClick={handleResultClick} />
×
141
        ) : searchStatus === 'succeeded' ? (
5!
NEW
142
          <Box display='flex' justifyContent='center' alignItems='center' sx={{py: 4}}>
×
NEW
143
            <Typography sx={{color: 'GrayText'}}>{t('semanticSearch.results.noResults')}</Typography>
×
NEW
144
          </Box>
×
145
        ) : null}
5✔
146
      </Box>
5✔
147

148
      <ArticleDialog open={selectedArticle !== null} onClose={handleCloseDialog} article={selectedArticle} />
5✔
149
    </Box>
5✔
150
  );
151
}
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc