diff --git a/package.json b/package.json
index 9ae4c15c..139d5aa9 100644
--- a/package.json
+++ b/package.json
@@ -73,6 +73,7 @@
"rollup-plugin-terser": "^7.0.1",
"sass": "^1.28.0",
"sass-loader": "^10.0.5",
+ "sinon": "^18.0.0",
"stream-browserify": "^3.0.0",
"style-loader": "^2.0.0",
"svg-url-loader": "^6.0.0",
diff --git a/src/components/Transcript/Transcript.js b/src/components/Transcript/Transcript.js
index bd2f94f5..15dc069f 100644
--- a/src/components/Transcript/Transcript.js
+++ b/src/components/Transcript/Transcript.js
@@ -32,7 +32,7 @@ const buildSpeakerText = (item, isDocx = false) => {
}
};
-const TranscriptLine = ({
+const TranscriptLine = React.memo(({
item,
goToItem,
isActive,
@@ -185,13 +185,15 @@ const TranscriptLine = ({
isFocused && 'focused'
)}
data-testid="transcript_untimed_text">
-
+
+
;
} else {
return null;
}
-};
+});
const Spinner = () => (
@@ -201,8 +203,7 @@ const Spinner = () => (
);
-const TranscriptList = ({
- isSearchable,
+const TranscriptList = React.memo(({
seekPlayer,
currentTime,
searchResults,
@@ -239,51 +240,49 @@ const TranscriptList = ({
break;
}
- if (isSearchable) {
- if (!searchResults.results || searchResults.results.length === 0) {
- return (
-
- );
- } else {
- return (
-
- {
- searchResults.ids.map((itemId) => (
-
- ))
- }
-
- );
- }
- } else {
+ if (transcriptInfo.tError) {
return (
{transcriptInfo.tError}
);
+ } else if (!searchResults.results || searchResults.results.length === 0) {
+ return (
+
+ );
+ } else {
+ return (
+
+ {
+ searchResults.ids.map((itemId) => (
+
+ ))
+ }
+
+ );
}
-};
+});
/**
*
@@ -332,18 +331,28 @@ const Transcript = ({ playerID, manifestUrl, showNotes = false, search = {}, tra
_setCanvasIndex(c); // force re-render
};
- const searchResults = useFilteredTranscripts({
- ...searchOpts,
- query: searchQuery,
- transcripts: transcript,
- canvasIndex: canvasIndexRef.current,
- selectedTranscript: selectedTranscript,
+ const searchResults =
+ useFilteredTranscripts({
+ ...searchOpts,
+ query: searchQuery,
+ transcripts: transcript,
+ canvasIndex: canvasIndexRef.current,
+ selectedTranscript: selectedTranscript,
+ });
+
+ const {
+ focusedMatchId,
+ setFocusedMatchId,
+ focusedMatchIndex,
+ setFocusedMatchIndex
+ } = useFocusedMatch({ searchResults });
+
+ const tanscriptHitCounts = useSearchCounts({
+ searchResults,
+ canvasTranscripts,
+ searchQuery
});
- const { focusedMatchId, setFocusedMatchId, focusedMatchIndex, setFocusedMatchIndex } = useFocusedMatch({ searchResults });
-
- const tanscriptHitCounts = useSearchCounts({ searchResults, canvasTranscripts, searchQuery });
-
const [isEmpty, setIsEmpty] = React.useState(true);
const [_autoScrollEnabled, _setAutoScrollEnabled] = React.useState(true);
const autoScrollEnabledRef = React.useRef(_autoScrollEnabled);
@@ -579,7 +588,6 @@ const Transcript = ({ playerID, manifestUrl, showNotes = false, search = {}, tra
ref={transcriptContainerRef}
>
+ debounce((event) => {
+ setSearchQuery(event.target.value);
+ }, 100),
+ []
+ );
+
const searchQueryEmpty = searchQuery === null || searchQuery.replace(/\s/g, '') === '';
let resultNavigation = null;
@@ -89,7 +98,7 @@ export const TranscriptSearch = ({
if (event.target.value.trim() == '') {
setSearchQuery(null);
} else {
- setSearchQuery(event.target.value);
+ handleOnChange(event);
}
}}
/>
diff --git a/src/components/Transcript/TranscriptMenu/TranscriptSearch.test.js b/src/components/Transcript/TranscriptMenu/TranscriptSearch.test.js
index 7414e0ec..52d2dc75 100644
--- a/src/components/Transcript/TranscriptMenu/TranscriptSearch.test.js
+++ b/src/components/Transcript/TranscriptMenu/TranscriptSearch.test.js
@@ -1,263 +1,278 @@
import React from 'react';
-import { render, screen, fireEvent, act, waitFor } from '@testing-library/react';
+import { render, screen, fireEvent } from '@testing-library/react';
import TranscriptSearch from './TranscriptSearch';
-
+import * as sinon from 'sinon';
describe('TranscriptSearch component', () => {
- const getBaseProps = () => ({
- searchResults: {
- results: {},
- id: [],
- matchingIds: []
+ const getBaseProps = () => ({
+ searchResults: {
+ results: {},
+ id: [],
+ matchingIds: []
+ },
+ searchQuery: null,
+ focusedMatchIndex: null,
+ setFocusedMatchIndex: jest.fn(),
+ setSearchQuery: jest.fn()
+ });
+
+ describe('with searchResults', () => {
+ const getPropsWithResults = () => ({
+ ...getBaseProps(),
+ searchResults: {
+ results: {
+ 0: { id: 0, text: 'The party has begun.' },
+ 1: { id: 1, text: 'I believe that on the first night I went to Gatsby\'s house' },
+ 2: { id: 2, text: 'I was one of the few guests who had actually been invited.' },
+ 3: { id: 3, text: 'People were not invited-they went there. They got into automobiles which bore them out to Long Island,' },
+ 4: { id: 4, text: 'and somehow they ended up at Gatsby\'s door.' },
+ 5: { id: 5, text: 'Once there they were introduced by somebody who knew Gatsby,' },
+ 6: { id: 6, text: 'and after that they conducted themselves according to the rules of behaviour associated with an amusement park.' },
+ 7: { id: 7, text: 'Sometimes they came and went without having met Gatsby at all,' },
+ 8: { id: 8, text: 'came for the party with a simplicity of heart that was its own ticket of admission.' }
},
- searchQuery: null,
- focusedMatchIndex: null,
- setFocusedMatchIndex: jest.fn(),
- setSearchQuery: jest.fn()
+ ids: [0, 1, 2, 3, 4, 5, 6, 7, 8],
+ matchingIds: []
+ }
});
-
- describe('with searchResults', () => {
- const getPropsWithResults = () => ({
- ...getBaseProps(),
- searchResults: {
- results: {
- 0: { id: 0, text: 'The party has begun.' },
- 1: { id: 1, text: 'I believe that on the first night I went to Gatsby\'s house' },
- 2: { id: 2, text: 'I was one of the few guests who had actually been invited.' },
- 3: { id: 3, text: 'People were not invited-they went there. They got into automobiles which bore them out to Long Island,' },
- 4: { id: 4, text: 'and somehow they ended up at Gatsby\'s door.' },
- 5: { id: 5, text: 'Once there they were introduced by somebody who knew Gatsby,' },
- 6: { id: 6, text: 'and after that they conducted themselves according to the rules of behaviour associated with an amusement park.' },
- 7: { id: 7, text: 'Sometimes they came and went without having met Gatsby at all,' },
- 8: { id: 8, text: 'came for the party with a simplicity of heart that was its own ticket of admission.' }
- },
- ids: [0, 1, 2, 3, 4, 5, 6, 7, 8],
- matchingIds: []
+
+ describe('when there are matches for the search query', () => {
+ const getPropsWithMatchingResults = () => {
+ const base = getPropsWithResults();
+ return {
+ ...base,
+ searchQuery: 'Gatsby',
+ searchResults: {
+ ...base.searchResults,
+ matchingIds: [1, 4, 5, 7],
+ results: {
+ ...base.searchResults.results,
+ 1: {
+ id: 1,
+ text: 'I believe that on the first night I went to Gatsby\'s house',
+ matches: [
+ 'I believe that on the first night I went to ',
+ 'Gatsby',
+ '\'s house'
+ ]
+ },
+ 4: {
+ id: 4,
+ text: 'and somehow they ended up at Gatsby\'s door.',
+ matches: [
+ 'and somehow they ended up at ',
+ 'Gatsby',
+ '\'s door.'
+ ]
+ },
+ 5: {
+ id: 5,
+ text: 'Once there they were introduced by somebody who knew Gatsby,',
+ matches: [
+ 'Once there they were introduced by somebody who knew ',
+ 'Gatsby',
+ ','
+ ]
+ },
+ 7: {
+ id: 7,
+ text: 'Sometimes they came and went without having met Gatsby at all,',
+ matches: [
+ 'Sometimes they came and went without having met ',
+ 'Gatsby',
+ ' at all,'
+ ]
+ }
}
+ }
+ };
+ };
+
+ test('result count reads "(focusedMatchIndex + 1) of (searchResults.matchingIds.length)"', async () => {
+ const props = getPropsWithMatchingResults();
+ const { rerender } = render();
+ const countEl = screen.getByTestId('transcript-search-count');
+ expect(countEl).toHaveTextContent('1 of 4');
+ rerender();
+ expect(countEl).toHaveTextContent('4 of 4');
+ });
+ test('when focusedMatchIndex is 0, the prev button is disabled', async () => {
+ const props = getPropsWithMatchingResults();
+ render();
+ const prevButtonEl = screen.getByTestId('transcript-search-prev');
+ expect(prevButtonEl).toBeDisabled();
+ });
+ test('when focusedMatchIndex is not 0, the prev button is disabled', async () => {
+ const props = getPropsWithMatchingResults();
+ render();
+ const prevButtonEl = screen.getByTestId('transcript-search-prev');
+ expect(prevButtonEl).not.toBeDisabled();
+ });
+ test('when focusedMatchIndex is the last index, the next button is disabled', () => {
+ const props = getPropsWithMatchingResults();
+ render();
+ const nextButtonEl = screen.getByTestId('transcript-search-next');
+ expect(nextButtonEl).toBeDisabled();
+ });
+ test('when focusedMatchIndex is not the last index, the next button is not disabled', () => {
+ const props = getPropsWithMatchingResults();
+ render();
+ const nextButtonEl = screen.getByTestId('transcript-search-next');
+ expect(nextButtonEl).not.toBeDisabled();
+ });
+ describe('interacting with the prev and next buttons', () => {
+ test('clicking the prev button decrements focusedMatchIndex', () => {
+ const props = getPropsWithMatchingResults();
+ render();
+ const prevButtonEl = screen.getByTestId('transcript-search-prev');
+ expect(props.setFocusedMatchIndex).not.toHaveBeenCalled();
+ fireEvent.click(prevButtonEl);
+ expect(props.setFocusedMatchIndex).toHaveBeenCalledWith(1);
});
+ test('clicking the next button increments focusedMatchIndex', () => {
+ const props = getPropsWithMatchingResults();
+ render();
+ const nextButtonEl = screen.getByTestId('transcript-search-next');
+ expect(props.setFocusedMatchIndex).not.toHaveBeenCalled();
+ fireEvent.click(nextButtonEl);
+ expect(props.setFocusedMatchIndex).toHaveBeenCalledWith(3);
+ });
+ test('clicking a disabled button does nothing', () => {
+ const props = getPropsWithMatchingResults();
+ const { rerender } = render();
+ const prevButtonEl = screen.getByTestId('transcript-search-prev');
+ fireEvent.click(prevButtonEl);
+ rerender();
+ const nextButtonEl = screen.getByTestId('transcript-search-next');
+ fireEvent.click(nextButtonEl);
+ expect(props.setFocusedMatchIndex).not.toHaveBeenCalled();
+ });
+ });
+ });
- describe('when there are matches for the search query', () => {
- const getPropsWithMatchingResults = () => {
- const base = getPropsWithResults();
- return {
- ...base,
- searchQuery: 'Gatsby',
- searchResults: {
- ...base.searchResults,
- matchingIds: [1, 4, 5, 7],
- results: {
- ...base.searchResults.results,
- 1: {
- id: 1,
- text: 'I believe that on the first night I went to Gatsby\'s house',
- matches: [
- 'I believe that on the first night I went to ',
- 'Gatsby',
- '\'s house'
- ]
- },
- 4: {
- id: 4,
- text: 'and somehow they ended up at Gatsby\'s door.',
- matches: [
- 'and somehow they ended up at ',
- 'Gatsby',
- '\'s door.'
- ]
- },
- 5: {
- id: 5,
- text: 'Once there they were introduced by somebody who knew Gatsby,',
- matches: [
- 'Once there they were introduced by somebody who knew ',
- 'Gatsby',
- ','
- ]
- },
- 7: {
- id: 7,
- text: 'Sometimes they came and went without having met Gatsby at all,',
- matches: [
- 'Sometimes they came and went without having met ',
- 'Gatsby',
- ' at all,'
- ]
- }
- }
- }
- };
- };
+ describe('when there are no matches for the search query', () => {
+ test('a no results found message is displayed', async () => {
+ const props = getPropsWithResults();
+ render();
+ const countEl = screen.getByTestId('transcript-search-count');
+ expect(countEl).toHaveTextContent('no results found in this transcript');
+ });
- test('result count reads "(focusedMatchIndex + 1) of (searchResults.matchingIds.length)"', async () => {
- const props = getPropsWithMatchingResults();
- const { rerender } = render();
- const countEl = screen.getByTestId('transcript-search-count');
- expect(countEl).toHaveTextContent('1 of 4');
- rerender();
- expect(countEl).toHaveTextContent('4 of 4');
- });
- test('when focusedMatchIndex is 0, the prev button is disabled', async () => {
- const props = getPropsWithMatchingResults();
- render();
- const prevButtonEl = screen.getByTestId('transcript-search-prev');
- expect(prevButtonEl).toBeDisabled();
- });
- test('when focusedMatchIndex is not 0, the prev button is disabled', async () => {
- const props = getPropsWithMatchingResults();
- render();
- const prevButtonEl = screen.getByTestId('transcript-search-prev');
- expect(prevButtonEl).not.toBeDisabled();
- });
- test('when focusedMatchIndex is the last index, the next button is disabled', () => {
- const props = getPropsWithMatchingResults();
- render();
- const nextButtonEl = screen.getByTestId('transcript-search-next');
- expect(nextButtonEl).toBeDisabled();
- });
- test('when focusedMatchIndex is not the last index, the next button is not disabled', () => {
- const props = getPropsWithMatchingResults();
- render();
- const nextButtonEl = screen.getByTestId('transcript-search-next');
- expect(nextButtonEl).not.toBeDisabled();
- });
- describe('interacting with the prev and next buttons', () => {
- test('clicking the prev button decrements focusedMatchIndex', () => {
- const props = getPropsWithMatchingResults();
- render();
- const prevButtonEl = screen.getByTestId('transcript-search-prev');
- expect(props.setFocusedMatchIndex).not.toHaveBeenCalled();
- fireEvent.click(prevButtonEl);
- expect(props.setFocusedMatchIndex).toHaveBeenCalledWith(1);
- });
- test('clicking the next button increments focusedMatchIndex', () => {
- const props = getPropsWithMatchingResults();
- render();
- const nextButtonEl = screen.getByTestId('transcript-search-next');
- expect(props.setFocusedMatchIndex).not.toHaveBeenCalled();
- fireEvent.click(nextButtonEl);
- expect(props.setFocusedMatchIndex).toHaveBeenCalledWith(3);
- });
- test('clicking a disabled button does nothing', () =>{
- const props = getPropsWithMatchingResults();
- const { rerender } = render();
- const prevButtonEl = screen.getByTestId('transcript-search-prev');
- fireEvent.click(prevButtonEl);
- rerender();
- const nextButtonEl = screen.getByTestId('transcript-search-next');
- fireEvent.click(nextButtonEl);
- expect(props.setFocusedMatchIndex).not.toHaveBeenCalled();
- });
- });
- });
+ test('prev and next buttons are not shown', async () => {
+ const props = getPropsWithResults();
+ render();
+ const prevButtonEl = screen.queryByTestId('transcript-search-prev');
+ const nextButtonEl = screen.queryByTestId('transcript-search-next');
+ expect(prevButtonEl).toBeNull();
+ expect(nextButtonEl).toBeNull();
+ });
+ });
+ });
- describe('when there are no matches for the search query', () => {
- test('a no results found message is displayed', async () => {
- const props = getPropsWithResults();
- render();
- const countEl = screen.getByTestId('transcript-search-count');
- expect(countEl).toHaveTextContent('no results found in this transcript');
- });
+ describe('modifying the search query', () => {
+ let clock;
+ beforeEach(() => {
+ clock = sinon.useFakeTimers();
+ });
- test('prev and next buttons are not shown', async () => {
- const props = getPropsWithResults();
- render();
- const prevButtonEl = screen.queryByTestId('transcript-search-prev');
- const nextButtonEl = screen.queryByTestId('transcript-search-next');
- expect(prevButtonEl).toBeNull();
- expect(nextButtonEl).toBeNull();
- });
- });
+ afterEach(() => {
+ clock.restore();
});
+ test('setSearchQuery is called when value of search input changes', () => {
+ const props = getBaseProps();
+ render();
+ expect(props.setSearchQuery).not.toHaveBeenCalled();
+ const searchInputEl = screen.getByTestId('transcript-search-input');
+ expect(searchInputEl.value).toBe('');
+ fireEvent.change(searchInputEl, { target: { value: 'test' } });
- describe('modifying the search query', () => {
- test('setSearchQuery is called when value of search input changes', () => {
- const props = getBaseProps();
- render();
- expect(props.setSearchQuery).not.toHaveBeenCalled();
- const searchInputEl = screen.getByTestId('transcript-search-input');
- expect(searchInputEl.value).toBe('');
- fireEvent.change(searchInputEl, { target: { value: 'test' } });
- expect(searchInputEl.value).toBe('test');
- expect(props.setSearchQuery).toHaveBeenCalled();
- });
- test('search query is set to null when input contains nothing but whitespace', () => {
- const props = getBaseProps();
- render();
- const searchInputEl = screen.getByTestId('transcript-search-input');
- expect(searchInputEl.value).toBe('test');
- fireEvent.change(searchInputEl, { target: { value: ' ' } });
- expect(searchInputEl.value).toBe(' ');
- expect(props.setSearchQuery).toHaveBeenCalledWith(null);
- });
- test('result count display is shown when search query is not empty', async () => {
- const props = getBaseProps();
- render();
- const countEl = screen.queryByTestId('transcript-search-count');
- expect(countEl).not.toBeNull();
- });
- describe('clear button', () => {
- test('clear button becomes visible when there is a searchQuery', async () => {
- const props = getBaseProps();
- const { rerender } = render();
-
- const searchInputEl = screen.getByTestId('transcript-search-input');
- let clearButtonEl = screen.queryByTestId('transcript-search-clear');
- expect(clearButtonEl).toBeNull();
-
- fireEvent.change(searchInputEl, { target: { value: 'test' } });
-
- expect(props.setSearchQuery).toHaveBeenCalledWith('test');
- rerender();
-
- clearButtonEl = screen.queryByTestId('transcript-search-clear');
- expect(clearButtonEl).not.toBeNull();
- expect(searchInputEl.value).toBe('test');
- });
- test('clicking clear button causes value of input to be cleared and clear button to disappear', () => {
- const props = getBaseProps();
- const { rerender } = render();
- const searchInputEl = screen.getByTestId('transcript-search-input');
- expect(searchInputEl.value).toBe('test');
- let clearButtonEl = screen.queryByTestId('transcript-search-clear');
- expect(clearButtonEl).not.toBeNull();
-
- fireEvent.click(clearButtonEl);
-
- expect(props.setSearchQuery).toHaveBeenCalledWith(null);
- rerender();
+ // Wait 100ms
+ clock.tick(100);
- expect(searchInputEl.value).toBe('');
- clearButtonEl = screen.queryByTestId('transcript-search-clear');
- expect(clearButtonEl).toBeNull();
- });
- });
+ expect(searchInputEl.value).toBe('test');
+ expect(props.setSearchQuery).toHaveBeenCalled();
});
- describe('with empty search query and no searchResults', () => {
- beforeEach(() => {
- const props = getBaseProps();
- render();
- });
- test('search input is empty', () => {
- const searchInputEl = screen.getByTestId('transcript-search-input');
- expect(searchInputEl.value).toBe('');
- });
- test('the result count display, clear, prev, and next buttons are not shown', () => {
- const props = getBaseProps();
- render();
- const countEl = screen.queryByTestId('transcript-search-count');
- const clearButtonEl = screen.queryByTestId('transcript-search-clear');
- const nextButtonEl = screen.queryByTestId('transcript-search-next');
- const prevButtonEl = screen.queryByTestId('transcript-search-prev');
- expect(countEl).toBeNull();
- expect(clearButtonEl).toBeNull();
- expect(prevButtonEl).toBeNull();
- expect(nextButtonEl).toBeNull();
- });
+ test('search query is set to null when input contains nothing but whitespace', () => {
+ const props = getBaseProps();
+ render();
+ const searchInputEl = screen.getByTestId('transcript-search-input');
+ expect(searchInputEl.value).toBe('test');
+ fireEvent.change(searchInputEl, { target: { value: ' ' } });
+ expect(searchInputEl.value).toBe(' ');
+ expect(props.setSearchQuery).toHaveBeenCalledWith(null);
+ });
+ test('result count display is shown when search query is not empty', async () => {
+ const props = getBaseProps();
+ render();
+ const countEl = screen.queryByTestId('transcript-search-count');
+ expect(countEl).not.toBeNull();
});
- test('when searchQuery is passed during components initial render, the search input is pre-populated', () => {
+ describe('clear button', () => {
+ test('clear button becomes visible when there is a searchQuery', async () => {
+ const props = getBaseProps();
+ const { rerender } = render();
+
+ const searchInputEl = screen.getByTestId('transcript-search-input');
+ let clearButtonEl = screen.queryByTestId('transcript-search-clear');
+ expect(clearButtonEl).toBeNull();
+
+ fireEvent.change(searchInputEl, { target: { value: 'test' } });
+
+ // Wait 100ms
+ clock.tick(100);
+
+ expect(props.setSearchQuery).toHaveBeenCalledWith('test');
+ rerender();
+
+ clearButtonEl = screen.queryByTestId('transcript-search-clear');
+ expect(clearButtonEl).not.toBeNull();
+ expect(searchInputEl.value).toBe('test');
+ });
+ test('clicking clear button causes value of input to be cleared and clear button to disappear', () => {
const props = getBaseProps();
const { rerender } = render();
const searchInputEl = screen.getByTestId('transcript-search-input');
expect(searchInputEl.value).toBe('test');
+ let clearButtonEl = screen.queryByTestId('transcript-search-clear');
+ expect(clearButtonEl).not.toBeNull();
+
+ fireEvent.click(clearButtonEl);
+
+ expect(props.setSearchQuery).toHaveBeenCalledWith(null);
+ rerender();
+
+ expect(searchInputEl.value).toBe('');
+ clearButtonEl = screen.queryByTestId('transcript-search-clear');
+ expect(clearButtonEl).toBeNull();
+ });
+ });
+ });
+ describe('with empty search query and no searchResults', () => {
+ beforeEach(() => {
+ const props = getBaseProps();
+ render();
+ });
+ test('search input is empty', () => {
+ const searchInputEl = screen.getByTestId('transcript-search-input');
+ expect(searchInputEl.value).toBe('');
+ });
+ test('the result count display, clear, prev, and next buttons are not shown', () => {
+ const props = getBaseProps();
+ render();
+ const countEl = screen.queryByTestId('transcript-search-count');
+ const clearButtonEl = screen.queryByTestId('transcript-search-clear');
+ const nextButtonEl = screen.queryByTestId('transcript-search-next');
+ const prevButtonEl = screen.queryByTestId('transcript-search-prev');
+ expect(countEl).toBeNull();
+ expect(clearButtonEl).toBeNull();
+ expect(prevButtonEl).toBeNull();
+ expect(nextButtonEl).toBeNull();
});
-})
\ No newline at end of file
+ });
+ test('when searchQuery is passed during components initial render, the search input is pre-populated', () => {
+ const props = getBaseProps();
+ const { rerender } = render();
+ const searchInputEl = screen.getByTestId('transcript-search-input');
+ expect(searchInputEl.value).toBe('test');
+ });
+});
diff --git a/src/services/search.js b/src/services/search.js
index a53f5014..45718770 100644
--- a/src/services/search.js
+++ b/src/services/search.js
@@ -37,8 +37,21 @@ export const defaultMatcherFactory = (items) => {
export const contentSearchFactory = (searchService, items, selectedTranscript) => {
return async (query, abortController) => {
try {
+ /**
+ * Prevent caching the response as this slows down the search within function by
+ * giving the ability to race the cache with the network when the cache is slow.
+ * pragma: HTTP/1.0 implementation for older clients
+ * cache-control: HTTP/1.1 implementation
+ */
+ var fetchHeaders = new Headers();
+ fetchHeaders.append('pragma', 'no-cache');
+ fetchHeaders.append('cache-control', 'no-cache');
+
const res = await fetch(`${searchService}?q=${query}`,
- { signal: abortController.signal }
+ {
+ signal: abortController.signal,
+ headers: fetchHeaders
+ }
);
const json = await res.json();
if (json.items?.length > 0) {
@@ -138,7 +151,7 @@ export function useFilteredTranscripts({
// Update searchResult instead of replacing to preserve the hit count
setSearchResults({
...searchResults,
- results: {}, matchingIds: [], ids: [], sortedMatchCounts: []
+ results: {}, matchingIds: [], ids: []
});
return;
} else if (!enabled || !query) {
@@ -148,7 +161,6 @@ export function useFilteredTranscripts({
...searchResults,
results: itemsIndexed,
matchingIds: [],
- sortedMatchCounts: [],
ids: sortedIds
});
// When query is cleared; clear cached search results
@@ -240,14 +252,11 @@ export function useFilteredTranscripts({
}
});
- const sortedMatchCounts = sortedMatchedLines.map((t) => ({ id: t.id, matchCount: t.matchCount }));
-
if (matchesOnly) {
setSearchResults({
...searchResults,
results: matchingItemsIndexed,
ids: sortedMatchIds,
- sortedMatchCounts: sortedMatchCounts,
matchingIds: sortedMatchIds,
});
} else {
@@ -261,7 +270,6 @@ export function useFilteredTranscripts({
...searchResults,
results: joinedIndexed,
ids: sortedItemIds,
- sortedMatchCounts: sortedMatchCounts,
matchingIds: sortedMatchIds,
};
setSearchResults(searchResults);
diff --git a/src/services/search.test.js b/src/services/search.test.js
index 3fc37822..2bdf5f56 100644
--- a/src/services/search.test.js
+++ b/src/services/search.test.js
@@ -23,7 +23,6 @@ const fixture = {
ids: [0, 1, 2, 3, 4, 5, 6, 7, 8],
matchingIds: [],
counts: [],
- sortedMatchCounts: []
};
const transcriptListFixture = [
{
@@ -116,7 +115,6 @@ const untimedFixture = {
ids: [0, 1, 2, 3, 4, 5, 6, 7, 8],
matchingIds: [],
counts: [],
- sortedMatchCounts: []
};
describe('useFilteredTranscripts', () => {
diff --git a/yarn.lock b/yarn.lock
index 0639a7d3..e052bdad 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1542,6 +1542,27 @@
dependencies:
type-detect "4.0.8"
+"@sinonjs/commons@^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3"
+ integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==
+ dependencies:
+ type-detect "4.0.8"
+
+"@sinonjs/commons@^3.0.0", "@sinonjs/commons@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd"
+ integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==
+ dependencies:
+ type-detect "4.0.8"
+
+"@sinonjs/fake-timers@^11.2.2":
+ version "11.2.2"
+ resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz#50063cc3574f4a27bd8453180a04171c85cc9699"
+ integrity sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==
+ dependencies:
+ "@sinonjs/commons" "^3.0.0"
+
"@sinonjs/fake-timers@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40"
@@ -1549,6 +1570,20 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
+"@sinonjs/samsam@^8.0.0":
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-8.0.0.tgz#0d488c91efb3fa1442e26abea81759dfc8b5ac60"
+ integrity sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==
+ dependencies:
+ "@sinonjs/commons" "^2.0.0"
+ lodash.get "^4.4.2"
+ type-detect "^4.0.8"
+
+"@sinonjs/text-encoding@^0.7.2":
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz#5981a8db18b56ba38ef0efb7d995b12aa7b51918"
+ integrity sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==
+
"@testing-library/dom@^7.28.1":
version "7.31.2"
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.31.2.tgz#df361db38f5212b88555068ab8119f5d841a8c4a"
@@ -4040,6 +4075,11 @@ diff-sequences@^29.4.3:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2"
integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==
+diff@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531"
+ integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==
+
dingbat-to-unicode@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz#5091dd673241453e6b5865e26e5a4452cdef5c83"
@@ -7108,6 +7148,11 @@ jszip@^3.7.1:
readable-stream "~2.3.6"
setimmediate "^1.0.5"
+just-extend@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-6.2.0.tgz#b816abfb3d67ee860482e7401564672558163947"
+ integrity sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==
+
keycode@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04"
@@ -7250,6 +7295,11 @@ lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
+lodash.get@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+ integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
+
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@@ -7852,6 +7902,17 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+nise@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/nise/-/nise-6.0.0.tgz#ae56fccb5d912037363c3b3f29ebbfa28bde8b48"
+ integrity sha512-K8ePqo9BFvN31HXwEtTNGzgrPpmvgciDsFz8aztFjt4LqKO/JeFD8tBOeuDiCMXrIl/m1YvfH8auSpxfaD09wg==
+ dependencies:
+ "@sinonjs/commons" "^3.0.0"
+ "@sinonjs/fake-timers" "^11.2.2"
+ "@sinonjs/text-encoding" "^0.7.2"
+ just-extend "^6.2.0"
+ path-to-regexp "^6.2.1"
+
no-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
@@ -8418,6 +8479,11 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
+path-to-regexp@^6.2.1:
+ version "6.2.2"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.2.tgz#324377a83e5049cbecadc5554d6a63a9a4866b36"
+ integrity sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==
+
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
@@ -10122,6 +10188,18 @@ simple-swizzle@^0.2.2:
dependencies:
is-arrayish "^0.3.1"
+sinon@^18.0.0:
+ version "18.0.0"
+ resolved "https://registry.yarnpkg.com/sinon/-/sinon-18.0.0.tgz#69ca293dbc3e82590a8b0d46c97f63ebc1e5fc01"
+ integrity sha512-+dXDXzD1sBO6HlmZDd7mXZCR/y5ECiEiGCBSGuFD/kZ0bDTofPYc6JaeGmPSF+1j1MejGUWkORbYOLDyvqCWpA==
+ dependencies:
+ "@sinonjs/commons" "^3.0.1"
+ "@sinonjs/fake-timers" "^11.2.2"
+ "@sinonjs/samsam" "^8.0.0"
+ diff "^5.2.0"
+ nise "^6.0.0"
+ supports-color "^7"
+
sisteransi@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
@@ -10592,7 +10670,7 @@ supports-color@^6.1.0:
dependencies:
has-flag "^3.0.0"
-supports-color@^7.0.0, supports-color@^7.1.0:
+supports-color@^7, supports-color@^7.0.0, supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==