mirror of
https://github.com/status-im/ETHReport.git
synced 2025-02-27 05:00:32 +00:00
Merge pull request #16 from Nona-Creative/feat/multiple-search-answers
Feat/multiple search answers
This commit is contained in:
commit
b784297a85
@ -17,12 +17,13 @@ class BrowseArchives extends React.Component {
|
||||
this.state = {
|
||||
term: '',
|
||||
debounceTerm: '',
|
||||
searchResults: [null],
|
||||
searchResults: [],
|
||||
isSingleInterviewModalOpen: false,
|
||||
isInterviewsListModalOpen: false,
|
||||
activeSingleInterviewId: 1,
|
||||
isSearchActive: false,
|
||||
interviewData: this.transformInterviews(InterviewsData),
|
||||
matchedCount: 0,
|
||||
};
|
||||
|
||||
this.onSearchInputChange = this.onSearchInputChange.bind(this);
|
||||
@ -39,7 +40,7 @@ class BrowseArchives extends React.Component {
|
||||
this.setState({
|
||||
term: event.target.value,
|
||||
isSearchActive: true,
|
||||
searchResults: [null],
|
||||
searchResults: [],
|
||||
});
|
||||
|
||||
if (event.target.value.length === 0) {
|
||||
@ -56,17 +57,28 @@ class BrowseArchives extends React.Component {
|
||||
const searchResults = interviewData.reduce((filtered, interview) => {
|
||||
const findTerm = this.termIsInInterview(term, interview);
|
||||
const matchedIndex = findTerm.foundIndex;
|
||||
const { matchingQuestionAnswerPositions } = findTerm;
|
||||
const { matchCount } = findTerm;
|
||||
|
||||
if (findTerm.found) {
|
||||
filtered.push({ ...interview, matchedIndex });
|
||||
filtered.push({
|
||||
...interview,
|
||||
matchedIndex,
|
||||
matchingQuestionAnswerPositions,
|
||||
matchCount,
|
||||
});
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}, []);
|
||||
|
||||
const matchedCount = searchResults
|
||||
.reduce((accumulator, match) => accumulator + match.matchCount, 0);
|
||||
|
||||
this.setState({
|
||||
searchResults,
|
||||
debounceTerm: term,
|
||||
matchedCount,
|
||||
});
|
||||
}
|
||||
|
||||
@ -95,12 +107,13 @@ class BrowseArchives extends React.Component {
|
||||
this.setState({
|
||||
isSearchActive: false,
|
||||
term: '',
|
||||
matchedCount: 0,
|
||||
searchResults: [],
|
||||
});
|
||||
}
|
||||
|
||||
transformInterviews = (interviews) => {
|
||||
// eslint-disable-next-line
|
||||
const length = Object.keys(interviews).length;
|
||||
const { length } = Object.keys(interviews);
|
||||
const betterInterviews = [];
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
@ -157,13 +170,14 @@ class BrowseArchives extends React.Component {
|
||||
termIsInInterview = (term, interview) => {
|
||||
const lcTerm = term.toLowerCase();
|
||||
const matchesName = interview.name.toLowerCase().includes(lcTerm);
|
||||
const { interviewData } = this.state;
|
||||
let foundIndex = 0;
|
||||
let positionInAnswer = -1;
|
||||
|
||||
if (matchesName) {
|
||||
return {
|
||||
found: true,
|
||||
foundIndex: 0,
|
||||
matchCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@ -173,9 +187,12 @@ class BrowseArchives extends React.Component {
|
||||
return {
|
||||
found: true,
|
||||
foundIndex: 0,
|
||||
matchCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const matchingQuestionAnswerPositions = [];
|
||||
|
||||
const matchingQuestions = interview.interview
|
||||
.filter((question, questionIndex) => {
|
||||
if (question.answer === null) {
|
||||
@ -185,20 +202,34 @@ class BrowseArchives extends React.Component {
|
||||
const index = question.answer.toLowerCase().indexOf(lcTerm);
|
||||
|
||||
if (index !== -1 && interview.activeIndex !== -1) {
|
||||
const cleanTerm = term.replace(/[^a-zA-Z 0-9]+/g, '');
|
||||
const regex = new RegExp(cleanTerm, 'ig');
|
||||
const count = question.answer.match(regex).length;
|
||||
|
||||
foundIndex = questionIndex;
|
||||
positionInAnswer = index;
|
||||
matchingQuestionAnswerPositions.push({
|
||||
id: question.question,
|
||||
strpos: index,
|
||||
answer: question.answer,
|
||||
index: questionIndex,
|
||||
count,
|
||||
});
|
||||
}
|
||||
|
||||
return index !== -1;
|
||||
});
|
||||
|
||||
this.setState({
|
||||
interviewData,
|
||||
});
|
||||
const matchCount = matchingQuestionAnswerPositions
|
||||
.reduce((accumulator, match) => accumulator + match.count, 0);
|
||||
|
||||
if (matchingQuestions.length > 0) {
|
||||
return {
|
||||
found: true,
|
||||
foundIndex,
|
||||
positionInAnswer,
|
||||
matchingQuestionAnswerPositions,
|
||||
matchCount,
|
||||
};
|
||||
}
|
||||
|
||||
@ -215,6 +246,7 @@ class BrowseArchives extends React.Component {
|
||||
searchResults,
|
||||
interviewData,
|
||||
debounceTerm,
|
||||
matchedCount,
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
@ -224,6 +256,8 @@ class BrowseArchives extends React.Component {
|
||||
clearSearchInput={this.clearSearchInput}
|
||||
isSearchActive={isSearchActive}
|
||||
term={term}
|
||||
numResults={searchResults.length}
|
||||
numMatchedTerms={matchedCount}
|
||||
/>
|
||||
<div className="browse-content-wrap container">
|
||||
<div className="browse-content-left">
|
||||
|
@ -30,6 +30,9 @@ const SearchBar = props => (
|
||||
)
|
||||
}
|
||||
</form>
|
||||
{props.numResults > 0 && props.numResults[0] !== null ?
|
||||
<p className="search-count">{ props.numMatchedTerms } search term matches | { props.numResults } interview matches</p> :
|
||||
''}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -39,6 +42,8 @@ SearchBar.propTypes = {
|
||||
isSearchActive: PropTypes.bool.isRequired,
|
||||
onSearchInputChange: PropTypes.func.isRequired,
|
||||
clearSearchInput: PropTypes.func.isRequired,
|
||||
numResults: PropTypes.number.isRequired,
|
||||
numMatchedTerms: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
export default SearchBar;
|
||||
|
@ -71,3 +71,8 @@
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.search-count {
|
||||
margin: calculateRem(36) 0;
|
||||
font-size: calculateRem(13);
|
||||
}
|
||||
|
@ -16,39 +16,75 @@ const SearchResults = (props) => {
|
||||
// sort array alphabetically
|
||||
const sortedInterviews = props.data.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
const trimText = (text, length) => {
|
||||
const getStartOffset = (text) => {
|
||||
if (text.indexOf('>') === 0) {
|
||||
return -2;
|
||||
} else if (text.indexOf('/p>') === 0) {
|
||||
return 3;
|
||||
} else if (text.indexOf('p>') === 0) {
|
||||
return -1;
|
||||
} else if (text.indexOf('<p>') !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
const getEndOffset = (text) => {
|
||||
if (text.substr(text.length - 1, 1) === '<') {
|
||||
return -1;
|
||||
} else if (text.substr(text.length - 2, 2) === '<p') {
|
||||
return -2;
|
||||
} else if (text.substr(text.length - 3, 3) === '<p>') {
|
||||
return -3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
const trimText = (text, strpos, length) => {
|
||||
let offset = 0;
|
||||
let firstEllipses = '';
|
||||
let lastEllipses = '';
|
||||
let startOffset = 0;
|
||||
let endOffset = 0;
|
||||
|
||||
if (text === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return text.length <= length ? text : `${text.substr(0, length)}...`;
|
||||
if (strpos > length && strpos !== -1 && length > 50) {
|
||||
offset = strpos - length;
|
||||
firstEllipses = '<p>...</p>';
|
||||
}
|
||||
|
||||
const offsetText = text.substr(offset, length + offset);
|
||||
startOffset = getStartOffset(offsetText);
|
||||
endOffset = getEndOffset(offsetText);
|
||||
|
||||
const newOffsetText = startOffset ?
|
||||
text.substr(offset + startOffset, length + offset + endOffset) :
|
||||
`<p>${text.substr(offset + 0, length + offset + endOffset)}`;
|
||||
|
||||
if (newOffsetText.substr(newOffsetText.length - 1, 1) !== '.' && newOffsetText.substr(newOffsetText.length - 1, 1) !== '>') {
|
||||
lastEllipses = '...';
|
||||
}
|
||||
|
||||
return text.length <= length ? text : `${firstEllipses}${newOffsetText}${lastEllipses}`;
|
||||
};
|
||||
|
||||
const highlightTerm = (text) => {
|
||||
const cleanTerm = props.term.replace(/[^a-zA-Z 0-9]+/g, '').toLowerCase();
|
||||
const cleanTerm = props.term.replace(/[^a-zA-Z 0-9]+/g, '');
|
||||
const regex = new RegExp(cleanTerm, 'ig');
|
||||
return text.replace(regex, `<span>${cleanTerm}</span>`);
|
||||
return text.replace(regex, match => `<span>${match}</span>`);
|
||||
};
|
||||
|
||||
const processText = (text, length = 500) => highlightTerm(trimText(text, length));
|
||||
|
||||
const findFirstQuestion = (interview) => {
|
||||
let { answer } = interview.interview[interview.matchedIndex];
|
||||
let id = interview.interview[interview.matchedIndex].question;
|
||||
|
||||
if (answer === null) {
|
||||
const firstNonNullAnswer = interview.interview.find(question => question.answer !== null);
|
||||
id = firstNonNullAnswer.question;
|
||||
// eslint-disable-next-line
|
||||
answer = firstNonNullAnswer.answer;
|
||||
}
|
||||
const processText = (text, strpos, length = 500) => highlightTerm(trimText(text, strpos, length));
|
||||
|
||||
const findQuestion = (answer) => {
|
||||
const { id } = answer;
|
||||
const { text } = props.questions.find(question => question.id === id);
|
||||
|
||||
return {
|
||||
question: text,
|
||||
answer: processText(answer),
|
||||
};
|
||||
return text;
|
||||
};
|
||||
|
||||
const interviewNameContainsTerm = (name, searchTerm) =>
|
||||
@ -69,12 +105,17 @@ const SearchResults = (props) => {
|
||||
<img src={`${publicRuntimeConfig.subDirPath}/static/img/right-chevron-icon.svg`} alt="right chevron icon" />
|
||||
</div>
|
||||
</div>
|
||||
<h5>{interview.matchedIndex + 1})
|
||||
{ findFirstQuestion(interview).question }
|
||||
</h5>
|
||||
<div>
|
||||
{ Parser(findFirstQuestion(interview).answer) }
|
||||
</div>
|
||||
{interview.matchingQuestionAnswerPositions ?
|
||||
interview.matchingQuestionAnswerPositions.map(match => (
|
||||
<div key={match.index + 1}>
|
||||
<h5>{match.index + 1})
|
||||
{ findQuestion(match) }
|
||||
</h5>
|
||||
<div>
|
||||
{ Parser(processText(match.answer, match.strpos)) }
|
||||
</div>
|
||||
</div>
|
||||
)) : ''}
|
||||
</button>
|
||||
</li>
|
||||
))
|
||||
|
@ -1,6 +1,7 @@
|
||||
@import './assets/styles/global.scss';
|
||||
|
||||
.search-results {
|
||||
margin-top: calculateRem(32);
|
||||
|
||||
li {
|
||||
margin-bottom: calculateRem(32);
|
||||
|
Loading…
x
Reference in New Issue
Block a user