From 264a6acf534279fce52366d6457f4df91279b149 Mon Sep 17 00:00:00 2001 From: Aswin Date: Tue, 4 Apr 2023 16:11:14 +0530 Subject: [PATCH] Feature Request: Search box above section list #277 Add new SectionFilter component to filter slugs based on section names Update SectionsColumn to display filtered sections Add units tests for SectionFilter component --- components/SectionFilter.js | 37 +++++++++++++++++ components/SectionsColumn.js | 48 ++++++++++++++++------ components/__tests__/SectionFilter.test.js | 42 +++++++++++++++++++ 3 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 components/SectionFilter.js create mode 100644 components/__tests__/SectionFilter.test.js diff --git a/components/SectionFilter.js b/components/SectionFilter.js new file mode 100644 index 0000000..f3fa91d --- /dev/null +++ b/components/SectionFilter.js @@ -0,0 +1,37 @@ +import { useEffect, useState } from 'react' + +const SectionFilter = ({ sectionSlugs, getTemplate, filterSections }) => { + const [section, setSection] = useState('') + + const getAutoCompleteResults = (section) => { + const suggestedSlugs = sectionSlugs.filter((slug) => { + return getTemplate(slug).name.toLowerCase().includes(section.toLowerCase()) + }) + + return suggestedSlugs.length ? suggestedSlugs : [undefined] + } + + useEffect(() => { + if (!section) { + filterSections([]) + return + } + + const suggestedSlugs = getAutoCompleteResults(section) + + filterSections(suggestedSlugs) + }, [section]) + + return ( + setSection(e.target.value)} + /> + ) +} + +export default SectionFilter diff --git a/components/SectionsColumn.js b/components/SectionsColumn.js index 7e8a56f..4b0d38e 100644 --- a/components/SectionsColumn.js +++ b/components/SectionsColumn.js @@ -15,6 +15,7 @@ import Image from 'next/image' import useLocalStorage from '../hooks/useLocalStorage' import { SortableItem } from './SortableItem' import CustomSection from './CustomSection' +import SectionFilter from './SectionFilter' const kebabCaseToTitleCase = (str) => { return str @@ -50,6 +51,7 @@ export const SectionsColumn = ({ const [addAction, setAddAction] = useState(false) const [currentSlugList, setCurrentSlugList] = useState([]) const [slugsFromPreviousSession, setSlugsFromPreviousSession] = useState([]) + const [filteredSlugs, setFilteredSlugs] = useState([]) const { saveBackup, deleteBackup } = useLocalStorage() useEffect(() => { @@ -163,6 +165,10 @@ export const SectionsColumn = ({ let alphabetizedSectionSlugs = sectionSlugs.sort() + const filterSections = (suggestedSlugs) => { + setFilteredSlugs(suggestedSlugs) + } + return (

@@ -235,29 +241,47 @@ export const SectionsColumn = ({ setAddAction={setAddAction} setTemplates={setTemplates} /> +
    { (pageRefreshed && slugsFromPreviousSession.indexOf('title-and-description') == -1 ? sectionSlugs.push('title-and-description') : ' ', - (alphabetizedSectionSlugs = sectionSlugs.sort()), + (alphabetizedSectionSlugs = !filteredSlugs.length + ? sectionSlugs.sort() + : filteredSlugs.sort()), pageRefreshed || addAction ? (alphabetizedSectionSlugs = [...new Set(alphabetizedSectionSlugs)]) : ' ', alphabetizedSectionSlugs.map((s) => { - const template = getTemplate(s) - if (template) { + if (s === undefined) { return ( -
  • - -
  • +

    + The section you're looking for is unavailable +

    ) + } else { + const template = getTemplate(s) + if (template) { + return ( +
  • + +
  • + ) + } } })) } diff --git a/components/__tests__/SectionFilter.test.js b/components/__tests__/SectionFilter.test.js new file mode 100644 index 0000000..3f2b2e4 --- /dev/null +++ b/components/__tests__/SectionFilter.test.js @@ -0,0 +1,42 @@ +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' + +import SectionFilter from '../SectionFilter' + +import { en_EN } from '../../data/section-templates-en_EN' + +jest.mock('next-i18next', () => ({ + useTranslation: () => ({ t: jest.fn() }), +})) + +describe('', () => { + const props = { + sectionSlugs: ['api', 'appendix'], + getTemplate: (slug) => en_EN.find((t) => t.slug === slug), + filterSections: jest.fn(), + } + + it('should render', () => { + const { container } = render() + expect(container).toBeInTheDocument() + }) + + it('should call the callBack function with suggested slugs', () => { + render() + + const input = screen.getByTestId('slugs-filter') + expect(input).toBeInTheDocument() + expect(input).toHaveAttribute('type', 'text') + expect(input).toHaveAttribute('placeholder', 'Search for a section') + + userEvent.type(input, 'app') + expect(screen.getByTestId('slugs-filter')).toHaveValue('app') + + expect(props.filterSections).toHaveBeenCalledTimes(5) + expect(props.filterSections).toHaveBeenNthCalledWith(1, []) + expect(props.filterSections).toHaveBeenNthCalledWith(2, []) + expect(props.filterSections).toHaveBeenNthCalledWith(3, ['api', 'appendix']) + expect(props.filterSections).toHaveBeenNthCalledWith(4, ['api', 'appendix']) + expect(props.filterSections).toHaveBeenNthCalledWith(5, ['appendix']) + }) +})