From e9135f06e75ca4bc9c1d4ec866671fa96d6f00bc Mon Sep 17 00:00:00 2001 From: Tom Southall Date: Wed, 16 Feb 2022 19:26:15 +0000 Subject: [PATCH] Refactor queryMatchesTypeahead and add tests --- src/lib/components/container.jsx | 10 ++------ .../components/effects/containerEffects.js | 12 +++------ .../effects/containerEffects.test.js | 5 +++- src/lib/utils/startsWithCaseInsensitive.js | 8 ++++++ .../utils/startsWithCaseInsensitive.test.js | 25 +++++++++++++++++++ 5 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 src/lib/utils/startsWithCaseInsensitive.js create mode 100644 src/lib/utils/startsWithCaseInsensitive.test.js diff --git a/src/lib/components/container.jsx b/src/lib/components/container.jsx index 0045813..cef5b9e 100644 --- a/src/lib/components/container.jsx +++ b/src/lib/components/container.jsx @@ -9,6 +9,7 @@ import { useAutoFocus, useQueryChange } from './effects/containerEffects' import firstOfType from 'first-of-type' import undef from '../utils/undef' import isUndefined from '../utils/isUndefined' +import startsWithCaseInsensitive from '../utils/startsWithCaseInsensitive' import defaultStyles from './styles/input.styles.js' // import fetch from 'unfetch' // TODO: may need this if not using Next.js @@ -224,7 +225,7 @@ export default function Container(props) { highlightedState && hasFocus && queryInput.current.value.length > 0 && - queryMatchesTypeahead(queryInput.current.value, highlightedState.text) + startsWithCaseInsensitive(highlightedState.text, queryInput.current.value) ? highlightedState.text : '' const queryValue = formatQuery(queryInput.current.value, typeAheadValue) @@ -244,13 +245,6 @@ export default function Container(props) { } }, [selectedState, setQueryState, onSelect]) - const queryMatchesTypeahead = (query, typeahead, caseSensitive = false) => { - return caseSensitive - ? typeahead.substring(0, query.length) === query - : typeahead.substring(0, query.length).toLowerCase() === - query.toLowerCase() - } - const formatQuery = (query, typeahead) => { const formattedQuery = typeahead.substring(0, query.length) return formattedQuery.length > 0 && diff --git a/src/lib/components/effects/containerEffects.js b/src/lib/components/effects/containerEffects.js index f4a7a7e..a36a9d8 100644 --- a/src/lib/components/effects/containerEffects.js +++ b/src/lib/components/effects/containerEffects.js @@ -1,5 +1,6 @@ import { useEffect } from 'react' import setify from 'setify' // Sets input value without changing cursor position +//import startsWithCaseInsensitive from '../../utils/startsWithCaseInsensitive' export const useAutoFocus = (queryInput, autoFocus) => { // TODO: might be able to use autofus property of input for this - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-autofocus useEffect(() => { @@ -13,7 +14,8 @@ export const useQueryChange = (queryInput, typeaheadInput, queryState, onChange) const value = (() => { const currentValue = typeaheadInput.current.value if (!queryState) return '' - if (!queryMatchesTypeahead(queryState, currentValue, true)) return '' + //if (!queryMatchesTypeahead(queryState, currentValue, true)) return '' + if (!currentValue.startsWith(queryState)) return '' return currentValue })() @@ -22,12 +24,4 @@ export const useQueryChange = (queryInput, typeaheadInput, queryState, onChange) setify(queryInput.current, queryState) if (typeof onChange === 'function') onChange(queryState) }, [queryState, onChange]) -} - -// TODO export for testing -const queryMatchesTypeahead = (query, typeahead, caseSensitive = false) => { - return caseSensitive - ? typeahead.substring(0, query.length) === query - : typeahead.substring(0, query.length).toLowerCase() === - query.toLowerCase() } \ No newline at end of file diff --git a/src/lib/components/effects/containerEffects.test.js b/src/lib/components/effects/containerEffects.test.js index 3ddd280..9f8b627 100644 --- a/src/lib/components/effects/containerEffects.test.js +++ b/src/lib/components/effects/containerEffects.test.js @@ -1,6 +1,9 @@ import { vi, describe, test, expect, beforeEach } from 'vitest' import { renderHook } from '@testing-library/react-hooks' -import { useAutoFocus, useQueryChange } from './containerEffects' +import { + useAutoFocus, + useQueryChange +} from './containerEffects' let inputRef = (value = '') => ( //TODO: Put in a beforeEach when blogging { diff --git a/src/lib/utils/startsWithCaseInsensitive.js b/src/lib/utils/startsWithCaseInsensitive.js new file mode 100644 index 0000000..2ab683b --- /dev/null +++ b/src/lib/utils/startsWithCaseInsensitive.js @@ -0,0 +1,8 @@ +const startsWithCaseInsensitive = (str, startsWith) => { + if(typeof str !== 'string') return false + if(typeof startsWith !== 'string') return false + + return str.toLowerCase().startsWith(startsWith.toLowerCase()) +} + +export default startsWithCaseInsensitive \ No newline at end of file diff --git a/src/lib/utils/startsWithCaseInsensitive.test.js b/src/lib/utils/startsWithCaseInsensitive.test.js new file mode 100644 index 0000000..347a4d2 --- /dev/null +++ b/src/lib/utils/startsWithCaseInsensitive.test.js @@ -0,0 +1,25 @@ +import { describe, test, expect } from 'vitest' +import startsWithCaseInsensitive from './startsWithCaseInsensitive' + +describe('startsWithCaseInsensitive', () => { + test('Returns true if they match exactly', () => { + expect(startsWithCaseInsensitive('Foobar', 'foo')).toBe(true) + }) + + test('Returns true if they match inexactly', () => { + expect(startsWithCaseInsensitive('Foobar', 'foo')).toBe(true) + }) + + test('Returns false if they do not match', () => { + expect(startsWithCaseInsensitive('Foobar', 'bar')).toBe(false) + }) + + test('Returns false if either argument is not a string', () => { + expect(startsWithCaseInsensitive(void(0), 'foo')).toBe(false) + expect(startsWithCaseInsensitive('Foobar', void(0))).toBe(false) + }) + + test('An empty string always matches', () => { + expect(startsWithCaseInsensitive('Foobar', '')).toBe(true) + }) +}) \ No newline at end of file