Skip to content

Commit faf9505

Browse files
authored
Update to Next 15 (#84)
* Update to Next 15 * Fix CI/CD compatibility with React 19 and Next.js 15 - Add --legacy-peer-deps flag to GitHub workflow npm ci commands to resolve React 19 peer dependency conflicts - Update react-image-gallery to 1.4.0 for React 19 compatibility - Fix AutocompleteSearch Configure component to remove TypeScript any usage - Remove deprecated yarn.lock file in favor of package-lock.json This resolves the ERESOLVE peer dependency errors preventing successful CI/CD builds. * Remove yarn.lock as project now uses npm exclusively
1 parent d0ea986 commit faf9505

6 files changed

Lines changed: 3066 additions & 12947 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
cache: 'npm'
3737

3838
- name: Install dependencies
39-
run: npm ci
39+
run: npm ci --legacy-peer-deps
4040

4141
- name: Run linting
4242
run: npm run lint
@@ -69,7 +69,7 @@ jobs:
6969
cache: 'npm'
7070

7171
- name: Install dependencies
72-
run: npm ci
72+
run: npm ci --legacy-peer-deps
7373

7474
- name: Run linting
7575
run: npm run lint

components/elements/AutocompleteSearch/AutocompleteSearch.tsx

Lines changed: 57 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
1-
import { FC, useState, FocusEvent } from 'react';
1+
import { FC, useState, useRef, useEffect } from 'react';
22
import Link from 'next/link';
33
import algoliasearch from 'algoliasearch/lite';
44
import {
55
Configure,
66
Highlight,
77
Hits,
88
InstantSearch,
9-
SearchBox,
10-
} from 'react-instantsearch-hooks-web';
11-
import type { Hit } from 'instantsearch.js';
9+
useSearchBox,
10+
} from 'react-instantsearch';
11+
import type { Hit, BaseHit } from 'instantsearch.js';
1212
import { Card } from '@components/layout';
1313
import classNames from 'classnames';
14-
import { AlgoliaSearchHelper } from 'algoliasearch-helper';
15-
import { useDocumentEvent } from 'hooks';
1614

1715
const searchClient = algoliasearch(
1816
'V0X7Z4KE9D',
1917
'544bec33383dc791bcbca3e1ceaec11b',
2018
);
2119

22-
interface ToolHit extends Hit {
20+
interface ToolHit extends Hit<BaseHit> {
2321
fields: {
2422
slug: string;
2523
name: string;
@@ -39,57 +37,65 @@ const Hit = (result: SearchResult) => {
3937
);
4038
};
4139

42-
const AutocompleteSearch: FC = () => {
43-
const [showResults, setShow] = useState(false);
40+
const CustomSearchBox: FC<{
41+
onFocus: () => void;
42+
onChange: (query: string) => void;
43+
}> = ({ onFocus, onChange }) => {
44+
const { query, refine } = useSearchBox();
4445

45-
const handleShowResults = (e: FocusEvent<HTMLInputElement>) => {
46-
if (!!e.target.value) {
47-
setShow(true);
48-
}
49-
};
46+
return (
47+
<div className="ais-SearchBox">
48+
<input
49+
type="search"
50+
placeholder="Find analysis tools, formatters, linters.."
51+
value={query}
52+
onFocus={onFocus}
53+
onChange={(e) => {
54+
refine(e.target.value);
55+
onChange(e.target.value);
56+
}}
57+
className="ais-SearchBox-input"
58+
/>
59+
</div>
60+
);
61+
};
5062

51-
const handleSearch = (e: AlgoliaSearchHelper) => {
52-
const shouldShow =
53-
e.state.query && e.state.query.length > 0 ? true : false;
54-
setShow(shouldShow);
55-
if (shouldShow) {
56-
e.search();
57-
}
58-
};
63+
const AutocompleteSearch: FC = () => {
64+
const [showResults, setShowResults] = useState(false);
65+
const searchRef = useRef<HTMLDivElement>(null);
5966

60-
const handleHideDropdown = (event: KeyboardEvent) => {
61-
if (event.key === 'Escape') {
62-
setShow(false);
63-
}
64-
};
67+
useEffect(() => {
68+
const handleClickOutside = (event: MouseEvent) => {
69+
if (
70+
searchRef.current &&
71+
!searchRef.current.contains(event.target as Node)
72+
) {
73+
setShowResults(false);
74+
}
75+
};
6576

66-
const handleClickOutside = (event: MouseEvent) => {
67-
const composedPath = event.composedPath?.() || [];
68-
const hasSearchContainer = composedPath.some(
69-
(el) => (el as Element).className === 'autocomplete-search',
70-
);
71-
if (!hasSearchContainer) {
72-
setShow(false);
73-
}
74-
};
77+
const handleEscape = (event: KeyboardEvent) => {
78+
if (event.key === 'Escape') {
79+
setShowResults(false);
80+
}
81+
};
7582

76-
useDocumentEvent([
77-
{ type: 'click', callback: handleClickOutside },
78-
{ type: 'keydown', callback: handleHideDropdown },
79-
]);
83+
document.addEventListener('mousedown', handleClickOutside);
84+
document.addEventListener('keydown', handleEscape);
85+
86+
return () => {
87+
document.removeEventListener('mousedown', handleClickOutside);
88+
document.removeEventListener('keydown', handleEscape);
89+
};
90+
}, []);
8091

8192
return (
82-
<InstantSearch
83-
searchClient={searchClient}
84-
indexName="tools"
85-
searchFunction={handleSearch}>
86-
<div className="autocomplete-search">
87-
<Configure
88-
{...({ hitsPerPage: 10, typoTolerance: true } as any)}
89-
/>
90-
<SearchBox
91-
placeholder="Find analysis tools, formatters, linters.."
92-
onFocus={handleShowResults}
93+
<InstantSearch searchClient={searchClient} indexName="tools">
94+
<div className="autocomplete-search" ref={searchRef}>
95+
<Configure hitsPerPage={10} typoTolerance={true} />
96+
<CustomSearchBox
97+
onFocus={() => setShowResults(true)}
98+
onChange={(query) => setShowResults(query.length > 0)}
9399
/>
94100
<div className="relative">
95101
<Card

next-env.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/// <reference types="next" />
22
/// <reference types="next/image-types/global" />
3+
/// <reference path="./.next/types/routes.d.ts" />
34

45
// NOTE: This file should not be edited
5-
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
6+
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.

0 commit comments

Comments
 (0)