Merge pull request #28 from phntxx/feature/defaultsearchprovider

Feature/defaultsearchprovider
This commit is contained in:
Bastian Meissner 2021-06-21 20:32:12 +02:00 committed by GitHub
commit b95f9667ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 285 additions and 244 deletions

View file

@ -1,64 +1,67 @@
{
"providers": [
{
"name": "Allmusic",
"url": "https://www.allmusic.com/search/all/",
"prefix": "/a"
},
{
"name": "Discogs",
"url": "https://www.discogs.com/search/?q=",
"prefix": "/di"
},
{
"name": "Duck Duck Go",
"url": "https://duckduckgo.com/?q=",
"prefix": "/d"
},
{
"name": "iMDB",
"url": "https://www.imdb.com/find?q=",
"prefix": "/i"
},
{
"name": "TheMovieDB",
"url": "https://www.themoviedb.org/search?query=",
"prefix": "/m"
},
{
"name": "Reddit",
"url": "https://www.reddit.com/search?q=",
"prefix": "/r"
},
{
"name": "Qwant",
"url": "https://www.qwant.com/?q=",
"prefix": "/q"
},
{
"name": "Soundcloud",
"url": "https://soundcloud.com/search?q=",
"prefix": "/so"
},
{
"name": "Spotify",
"url": "https://open.spotify.com/search/results/",
"prefix": "/s"
},
{
"name": "TheTVDB",
"url": "https://www.thetvdb.com/search?q=",
"prefix": "/tv"
},
{
"name": "Trakt",
"url": "https://trakt.tv/search?query=",
"prefix": "/t"
},
{
"name": "YouTube",
"url": "https://youtube.com/results?search_query=",
"prefix": "/yt"
}
]
"search": {
"defaultProvider": "https://google.com/search?q=",
"providers": [
{
"name": "Allmusic",
"url": "https://www.allmusic.com/search/all/",
"prefix": "/a"
},
{
"name": "Discogs",
"url": "https://www.discogs.com/search/?q=",
"prefix": "/di"
},
{
"name": "Duck Duck Go",
"url": "https://duckduckgo.com/?q=",
"prefix": "/d"
},
{
"name": "iMDB",
"url": "https://www.imdb.com/find?q=",
"prefix": "/i"
},
{
"name": "TheMovieDB",
"url": "https://www.themoviedb.org/search?query=",
"prefix": "/m"
},
{
"name": "Reddit",
"url": "https://www.reddit.com/search?q=",
"prefix": "/r"
},
{
"name": "Qwant",
"url": "https://www.qwant.com/?q=",
"prefix": "/q"
},
{
"name": "Soundcloud",
"url": "https://soundcloud.com/search?q=",
"prefix": "/so"
},
{
"name": "Spotify",
"url": "https://open.spotify.com/search/results/",
"prefix": "/s"
},
{
"name": "TheTVDB",
"url": "https://www.thetvdb.com/search?q=",
"prefix": "/tv"
},
{
"name": "Trakt",
"url": "https://trakt.tv/search?query=",
"prefix": "/t"
},
{
"name": "YouTube",
"url": "https://youtube.com/results?search_query=",
"prefix": "/yt"
}
]
}
}

View file

@ -42,13 +42,14 @@ const App = () => {
<>
<GlobalStyle />
<div>
<SearchBar providers={searchProviderData?.providers} />
{(!themeData.error || !searchProviderData.error) && (
<Settings
themes={themeData?.themes}
providers={searchProviderData?.providers}
/>
)}
<SearchBar search={searchProviderData?.search} />
{!themeData.error ||
(!searchProviderData.error && (
<Settings
themes={themeData?.themes}
search={searchProviderData?.search}
/>
))}
<Greeter data={greeterData.greeter} />
{!appData.error && (
<AppList apps={appData.apps} categories={appData.categories} />

View file

@ -43,12 +43,17 @@ export interface ISearchProviderProps {
prefix: string;
}
export interface ISearchBarProps {
export interface ISearchProps {
defaultProvider: string;
providers: Array<ISearchProviderProps> | undefined;
}
interface ISearchBarProps {
search: ISearchProps;
}
export const handleQueryWithProvider = (
providers: Array<ISearchProviderProps> | undefined,
search: ISearchProps,
query: string,
) => {
let queryArray: Array<string> = query.split(" ");
@ -59,8 +64,8 @@ export const handleQueryWithProvider = (
let searchQuery: string = queryArray.join(" ");
let providerFound: boolean = false;
if (providers) {
providers.forEach((provider: ISearchProviderProps) => {
if (search.providers) {
search.providers.forEach((provider: ISearchProviderProps) => {
if (provider.prefix === prefix) {
providerFound = true;
window.location.href = provider.url + searchQuery;
@ -68,15 +73,14 @@ export const handleQueryWithProvider = (
});
}
if (!providerFound)
window.location.href = "https://google.com/search?q=" + query;
if (!providerFound) window.location.href = search.defaultProvider + query;
};
/**
* Renders a search bar
* @param {ISearchBarProps} props - The search providers for the search bar to use
* @param {ISearchBarProps} search - The search providers for the search bar to use
*/
const SearchBar = ({ providers }: ISearchBarProps) => {
const SearchBar = ({ search }: ISearchBarProps) => {
let [input, setInput] = useState<string>("");
let [buttonsHidden, setButtonsHidden] = useState<boolean>(true);
@ -86,9 +90,9 @@ const SearchBar = ({ providers }: ISearchBarProps) => {
var query: string = input || "";
if (query.split(" ")[0].includes("/")) {
handleQueryWithProvider(providers, query);
handleQueryWithProvider(search, query);
} else {
window.location.href = "https://google.com/search?q=" + query;
window.location.href = search.defaultProvider + query;
}
e.preventDefault();

View file

@ -3,12 +3,8 @@ import styled from "styled-components";
import Select, { ValueType } from "react-select";
import { ISearchProviderProps } from "./searchBar";
import selectedTheme, {
defaultTheme,
setTheme,
IThemeProps,
} from "../lib/theme";
import { ISearchProps } from "./searchBar";
import selectedTheme, { setTheme, IThemeProps } from "../lib/theme";
import { Button, SubHeadline } from "./elements";
import Modal from "./modal";
@ -21,6 +17,7 @@ export const FormContainer = styled.div`
export const Table = styled.table`
font-weight: 400;
background: none;
width: 100%;
color: ${selectedTheme.mainColor};
`;
@ -35,7 +32,7 @@ export const TableCell = styled.td`
export const HeadCell = styled.th`
font-weight: 700;
background: none;
text-align: left;
`;
export const Section = styled.div`
@ -48,6 +45,16 @@ export const SectionHeadline = styled(SubHeadline)`
margin-bottom: 0.5rem;
`;
const Text = styled.p`
font-weight: 700;
color: ${selectedTheme.accentColor};
`;
const Code = styled.p`
font-family: monospace;
color: ${selectedTheme.accentColor};
`;
export const SelectorStyle: any = {
container: (base: any): any => ({
...base,
@ -108,18 +115,18 @@ export const SelectorStyle: any = {
interface ISettingsProps {
themes: Array<IThemeProps> | undefined;
providers: Array<ISearchProviderProps> | undefined;
search: ISearchProps | undefined;
}
/**
* Handles the settings-modal
* @param {Array<IThemeProps>} themes - the list of themes a user can select between
* @param {Array<ISearchProviderProps>} providers - the list of search providers
* @param {ISearchProps} search - the list of search providers
*/
const Settings = ({ themes, providers }: ISettingsProps) => {
const [newTheme, setNewTheme] = useState<IThemeProps>(defaultTheme);
const Settings = ({ themes, search }: ISettingsProps) => {
const [newTheme, setNewTheme] = useState<IThemeProps>();
if (themes || providers) {
if (themes || search) {
return (
<Modal element="icon" icon="settings" title="Settings">
{themes && (
@ -137,7 +144,9 @@ const Settings = ({ themes, providers }: ISettingsProps) => {
/>
<Button
data-testid="button-submit"
onClick={() => setTheme(newTheme)}
onClick={() => {
if (newTheme) setTheme(newTheme);
}}
>
Apply
</Button>
@ -150,23 +159,31 @@ const Settings = ({ themes, providers }: ISettingsProps) => {
</FormContainer>
</Section>
)}
{providers && (
{search && (
<Section>
<SectionHeadline>Search Providers</SectionHeadline>
<Table>
<tbody>
<TableRow>
<HeadCell>Search Provider</HeadCell>
<HeadCell>Prefix</HeadCell>
</TableRow>
{providers.map((provider, index) => (
<TableRow key={provider.name + index}>
<TableCell>{provider.name}</TableCell>
<TableCell>{provider.prefix}</TableCell>
</TableRow>
))}
</tbody>
</Table>
<>
<Text>Default Search Provider</Text>
<Code>{search.defaultProvider}</Code>
</>
<>
{search.providers && (
<Table>
<tbody>
<TableRow>
<HeadCell>Search Provider</HeadCell>
<HeadCell>Prefix</HeadCell>
</TableRow>
{search.providers.map((provider, index) => (
<TableRow key={provider.name + index}>
<TableCell>{provider.name}</TableCell>
<TableCell>{provider.prefix}</TableCell>
</TableRow>
))}
</tbody>
</Table>
)}
</>
</Section>
)}
</Modal>

View file

@ -1,64 +1,67 @@
{
"providers": [
{
"name": "Allmusic",
"url": "https://www.allmusic.com/search/all/",
"prefix": "/a"
},
{
"name": "Discogs",
"url": "https://www.discogs.com/search/?q=",
"prefix": "/di"
},
{
"name": "Duck Duck Go",
"url": "https://duckduckgo.com/?q=",
"prefix": "/d"
},
{
"name": "iMDB",
"url": "https://www.imdb.com/find?q=",
"prefix": "/i"
},
{
"name": "TheMovieDB",
"url": "https://www.themoviedb.org/search?query=",
"prefix": "/m"
},
{
"name": "Reddit",
"url": "https://www.reddit.com/search?q=",
"prefix": "/r"
},
{
"name": "Qwant",
"url": "https://www.qwant.com/?q=",
"prefix": "/q"
},
{
"name": "Soundcloud",
"url": "https://soundcloud.com/search?q=",
"prefix": "/so"
},
{
"name": "Spotify",
"url": "https://open.spotify.com/search/results/",
"prefix": "/s"
},
{
"name": "TheTVDB",
"url": "https://www.thetvdb.com/search?q=",
"prefix": "/tv"
},
{
"name": "Trakt",
"url": "https://trakt.tv/search?query=",
"prefix": "/t"
},
{
"name": "YouTube",
"url": "https://youtube.com/results?search_query=",
"prefix": "/yt"
}
]
"search": {
"defaultProvider": "https://google.com/search?q=",
"providers": [
{
"name": "Allmusic",
"url": "https://www.allmusic.com/search/all/",
"prefix": "/a"
},
{
"name": "Discogs",
"url": "https://www.discogs.com/search/?q=",
"prefix": "/di"
},
{
"name": "Duck Duck Go",
"url": "https://duckduckgo.com/?q=",
"prefix": "/d"
},
{
"name": "iMDB",
"url": "https://www.imdb.com/find?q=",
"prefix": "/i"
},
{
"name": "TheMovieDB",
"url": "https://www.themoviedb.org/search?query=",
"prefix": "/m"
},
{
"name": "Reddit",
"url": "https://www.reddit.com/search?q=",
"prefix": "/r"
},
{
"name": "Qwant",
"url": "https://www.qwant.com/?q=",
"prefix": "/q"
},
{
"name": "Soundcloud",
"url": "https://soundcloud.com/search?q=",
"prefix": "/so"
},
{
"name": "Spotify",
"url": "https://open.spotify.com/search/results/",
"prefix": "/s"
},
{
"name": "TheTVDB",
"url": "https://www.thetvdb.com/search?q=",
"prefix": "/tv"
},
{
"name": "Trakt",
"url": "https://trakt.tv/search?query=",
"prefix": "/t"
},
{
"name": "YouTube",
"url": "https://youtube.com/results?search_query=",
"prefix": "/yt"
}
]
}
}

View file

@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from "react";
import { ISearchProviderProps } from "../components/searchBar";
import { ISearchProps } from "../components/searchBar";
import { IBookmarkGroupProps } from "../components/bookmarks";
import { IAppCategoryProps } from "../components/appCategory";
import { IAppProps } from "../components/app";
@ -22,8 +22,8 @@ export const handleResponse = (response: Response) => {
throw new Error(errorMessage);
};
export interface ISearchProviderDataProps {
providers: Array<ISearchProviderProps>;
export interface ISearchDataProps {
search: ISearchProps;
error: string | boolean;
}
@ -67,7 +67,10 @@ export const defaults = {
error: false,
},
search: {
providers: [],
search: {
defaultProvider: "https://google.com/search?q=",
providers: [],
},
error: false,
},
theme: {
@ -209,7 +212,7 @@ export const useFetcher = () => {
);
const [searchProviderData, setSearchProviderData] =
useState<ISearchProviderDataProps>(defaults.search);
useState<ISearchDataProps>(defaults.search);
const [themeData, setThemeData] = useState<IThemeDataProps>(defaults.theme);
@ -233,7 +236,7 @@ export const useFetcher = () => {
]: [
IAppDataProps,
IBookmarkDataProps,
ISearchProviderDataProps,
ISearchDataProps,
IThemeDataProps,
IImprintDataProps,
IGreeterDataProps,

View file

@ -26,14 +26,15 @@ const props: IImprintProps = {
};
describe("imprint.tsx", () => {
const location: Location = window.location;
beforeEach(() => {
delete global.window.location;
global.window = Object.create(window);
global.window.location = {
port: "123",
protocol: "http:",
hostname: "localhost",
href: "test",
// @ts-ignore
delete window.location;
window.location = {
...location,
reload: jest.fn(),
};
});

View file

@ -2,29 +2,32 @@ import { fireEvent, render } from "@testing-library/react";
import SearchBar, {
handleQueryWithProvider,
ISearchProviderProps,
ISearchBarProps,
ISearchProps,
} from "../../components/searchBar";
const providers: Array<ISearchProviderProps> = [
{
name: "Allmusic",
url: "https://www.allmusic.com/search/all/",
prefix: "/a",
},
{
name: "Discogs",
url: "https://www.discogs.com/search/?q=",
prefix: "/di",
},
{
name: "Duck Duck Go",
url: "https://duckduckgo.com/?q=",
prefix: "/d",
},
];
const props: ISearchProps = {
defaultProvider: "https://test.com?q=",
providers: [
{
name: "Allmusic",
url: "https://www.allmusic.com/search/all/",
prefix: "/a",
},
{
name: "Discogs",
url: "https://www.discogs.com/search/?q=",
prefix: "/di",
},
{
name: "Duck Duck Go",
url: "https://duckduckgo.com/?q=",
prefix: "/d",
},
],
};
const setup = () => {
const searchBar = render(<SearchBar providers={providers} />);
const searchBar = render(<SearchBar search={props} />);
const input = searchBar.getByTestId("search-input");
const clearButton = searchBar.getByTestId("search-clear");
const submitButton = searchBar.getByTestId("search-submit");
@ -37,37 +40,44 @@ const setup = () => {
};
};
const location: Location = window.location;
describe("searchBar.tsx", () => {
beforeEach(() => {
delete global.window.location;
global.window = Object.create(window);
global.window.location = {
port: "123",
protocol: "http:",
hostname: "localhost",
// @ts-ignore
delete window.location;
window.location = {
...location,
reload: jest.fn(),
};
});
it("Tests SearchBar rendering", () => {
const { asFragment } = render(<SearchBar providers={providers} />);
const { asFragment } = render(<SearchBar search={props} />);
expect(asFragment).toMatchSnapshot();
});
it("Tests handleQueryWithProvider", () => {
providers.forEach((provider: ISearchProviderProps) => {
handleQueryWithProvider(providers, provider.prefix + " test");
props.providers?.forEach((provider: ISearchProviderProps) => {
handleQueryWithProvider(props, provider.prefix + " test");
expect(window.location.href).toEqual(provider.url + "test");
});
});
it("Tests handleQueryWithProvider default", () => {
handleQueryWithProvider(providers, "test");
expect(window.location.href).toEqual("https://google.com/search?q=test");
handleQueryWithProvider(props, "test");
expect(window.location.href).toEqual(props.defaultProvider + "test");
});
it("Tests handleQueryWithProvider without providers", () => {
handleQueryWithProvider(undefined, "test");
expect(window.location.href).toEqual("https://google.com/search?q=test");
const test: ISearchProps = {
defaultProvider: "https://test.com?q=",
providers: undefined,
};
handleQueryWithProvider(test, "test");
expect(window.location.href).toEqual(test.defaultProvider + "test");
});
it("Tests SearchBar component with default search", () => {
@ -75,7 +85,7 @@ describe("searchBar.tsx", () => {
fireEvent.change(input, { target: { value: "test" } });
fireEvent.click(submitButton);
expect(window.location.href).toEqual("https://google.com/search?q=test");
expect(window.location.href).toEqual(props.defaultProvider + "test");
});
it("Tests SearchBar component with other search", () => {
@ -96,6 +106,6 @@ describe("searchBar.tsx", () => {
fireEvent.click(clearButton);
fireEvent.click(submitButton);
expect(window.location.href).toEqual("https://google.com/search?q=");
expect(window.location.href).toEqual(props.defaultProvider);
});
});

View file

@ -9,7 +9,7 @@ import Settings, {
SectionHeadline,
SelectorStyle,
} from "../../components/settings";
import { ISearchProviderProps } from "../../components/searchBar";
import { ISearchProps } from "../../components/searchBar";
import { IThemeProps } from "../../lib/theme";
const themes: Array<IThemeProps> = [
@ -29,46 +29,48 @@ const themes: Array<IThemeProps> = [
},
];
const providers: Array<ISearchProviderProps> = [
{
name: "Allmusic",
url: "https://www.allmusic.com/search/all/",
prefix: "/a",
},
{
name: "Discogs",
url: "https://www.discogs.com/search/?q=",
prefix: "/di",
},
{
name: "Duck Duck Go",
url: "https://duckduckgo.com/?q=",
prefix: "/d",
},
];
const search: ISearchProps = {
defaultProvider: "https://test.com?q=",
providers: [
{
name: "Allmusic",
url: "https://www.allmusic.com/search/all/",
prefix: "/a",
},
{
name: "Discogs",
url: "https://www.discogs.com/search/?q=",
prefix: "/di",
},
{
name: "Duck Duck Go",
url: "https://duckduckgo.com/?q=",
prefix: "/d",
},
],
};
const propsList = [
{
themes: themes,
providers: providers,
search: search,
},
{
themes: themes,
providers: undefined,
search: undefined,
},
{
themes: undefined,
providers: providers,
search: search,
},
{
themes: undefined,
providers: undefined,
search: undefined,
},
];
describe("settings.tsx", () => {
const location: Location = window.location;
8;
beforeEach(() => {
// @ts-ignore
@ -115,7 +117,7 @@ describe("settings.tsx", () => {
it("Tests settings rendering", () => {
propsList.forEach((props) => {
const settings = render(
<Settings themes={props.themes} providers={props.providers} />,
<Settings themes={props.themes} search={props.search} />,
);
expect(settings.asFragment).toMatchSnapshot();
@ -125,10 +127,7 @@ describe("settings.tsx", () => {
// TODO: Finish this test
it("Tests theme setting", () => {
const settings = render(
<Settings
themes={propsList[0].themes}
providers={propsList[0].providers}
/>,
<Settings themes={propsList[0].themes} search={propsList[0].search} />,
);
const toggleButton = settings.getByTestId("toggle-button");