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,4 +1,6 @@
{ {
"search": {
"defaultProvider": "https://google.com/search?q=",
"providers": [ "providers": [
{ {
"name": "Allmusic", "name": "Allmusic",
@ -62,3 +64,4 @@
} }
] ]
} }
}

View file

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

View file

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

View file

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

View file

@ -1,4 +1,6 @@
{ {
"search": {
"defaultProvider": "https://google.com/search?q=",
"providers": [ "providers": [
{ {
"name": "Allmusic", "name": "Allmusic",
@ -62,3 +64,4 @@
} }
] ]
} }
}

View file

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

View file

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

View file

@ -2,10 +2,12 @@ import { fireEvent, render } from "@testing-library/react";
import SearchBar, { import SearchBar, {
handleQueryWithProvider, handleQueryWithProvider,
ISearchProviderProps, ISearchProviderProps,
ISearchBarProps, ISearchProps,
} from "../../components/searchBar"; } from "../../components/searchBar";
const providers: Array<ISearchProviderProps> = [ const props: ISearchProps = {
defaultProvider: "https://test.com?q=",
providers: [
{ {
name: "Allmusic", name: "Allmusic",
url: "https://www.allmusic.com/search/all/", url: "https://www.allmusic.com/search/all/",
@ -21,10 +23,11 @@ const providers: Array<ISearchProviderProps> = [
url: "https://duckduckgo.com/?q=", url: "https://duckduckgo.com/?q=",
prefix: "/d", prefix: "/d",
}, },
]; ],
};
const setup = () => { const setup = () => {
const searchBar = render(<SearchBar providers={providers} />); const searchBar = render(<SearchBar search={props} />);
const input = searchBar.getByTestId("search-input"); const input = searchBar.getByTestId("search-input");
const clearButton = searchBar.getByTestId("search-clear"); const clearButton = searchBar.getByTestId("search-clear");
const submitButton = searchBar.getByTestId("search-submit"); const submitButton = searchBar.getByTestId("search-submit");
@ -37,37 +40,44 @@ const setup = () => {
}; };
}; };
const location: Location = window.location;
describe("searchBar.tsx", () => { describe("searchBar.tsx", () => {
beforeEach(() => { beforeEach(() => {
delete global.window.location; // @ts-ignore
global.window = Object.create(window); delete window.location;
global.window.location = {
port: "123", window.location = {
protocol: "http:", ...location,
hostname: "localhost", reload: jest.fn(),
}; };
}); });
it("Tests SearchBar rendering", () => { it("Tests SearchBar rendering", () => {
const { asFragment } = render(<SearchBar providers={providers} />); const { asFragment } = render(<SearchBar search={props} />);
expect(asFragment).toMatchSnapshot(); expect(asFragment).toMatchSnapshot();
}); });
it("Tests handleQueryWithProvider", () => { it("Tests handleQueryWithProvider", () => {
providers.forEach((provider: ISearchProviderProps) => { props.providers?.forEach((provider: ISearchProviderProps) => {
handleQueryWithProvider(providers, provider.prefix + " test"); handleQueryWithProvider(props, provider.prefix + " test");
expect(window.location.href).toEqual(provider.url + "test"); expect(window.location.href).toEqual(provider.url + "test");
}); });
}); });
it("Tests handleQueryWithProvider default", () => { it("Tests handleQueryWithProvider default", () => {
handleQueryWithProvider(providers, "test"); handleQueryWithProvider(props, "test");
expect(window.location.href).toEqual("https://google.com/search?q=test"); expect(window.location.href).toEqual(props.defaultProvider + "test");
}); });
it("Tests handleQueryWithProvider without providers", () => { it("Tests handleQueryWithProvider without providers", () => {
handleQueryWithProvider(undefined, "test"); const test: ISearchProps = {
expect(window.location.href).toEqual("https://google.com/search?q=test"); 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", () => { it("Tests SearchBar component with default search", () => {
@ -75,7 +85,7 @@ describe("searchBar.tsx", () => {
fireEvent.change(input, { target: { value: "test" } }); fireEvent.change(input, { target: { value: "test" } });
fireEvent.click(submitButton); 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", () => { it("Tests SearchBar component with other search", () => {
@ -96,6 +106,6 @@ describe("searchBar.tsx", () => {
fireEvent.click(clearButton); fireEvent.click(clearButton);
fireEvent.click(submitButton); 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, SectionHeadline,
SelectorStyle, SelectorStyle,
} from "../../components/settings"; } from "../../components/settings";
import { ISearchProviderProps } from "../../components/searchBar"; import { ISearchProps } from "../../components/searchBar";
import { IThemeProps } from "../../lib/theme"; import { IThemeProps } from "../../lib/theme";
const themes: Array<IThemeProps> = [ const themes: Array<IThemeProps> = [
@ -29,7 +29,9 @@ const themes: Array<IThemeProps> = [
}, },
]; ];
const providers: Array<ISearchProviderProps> = [ const search: ISearchProps = {
defaultProvider: "https://test.com?q=",
providers: [
{ {
name: "Allmusic", name: "Allmusic",
url: "https://www.allmusic.com/search/all/", url: "https://www.allmusic.com/search/all/",
@ -45,30 +47,30 @@ const providers: Array<ISearchProviderProps> = [
url: "https://duckduckgo.com/?q=", url: "https://duckduckgo.com/?q=",
prefix: "/d", prefix: "/d",
}, },
]; ],
};
const propsList = [ const propsList = [
{ {
themes: themes, themes: themes,
providers: providers, search: search,
}, },
{ {
themes: themes, themes: themes,
providers: undefined, search: undefined,
}, },
{ {
themes: undefined, themes: undefined,
providers: providers, search: search,
}, },
{ {
themes: undefined, themes: undefined,
providers: undefined, search: undefined,
}, },
]; ];
describe("settings.tsx", () => { describe("settings.tsx", () => {
const location: Location = window.location; const location: Location = window.location;
8;
beforeEach(() => { beforeEach(() => {
// @ts-ignore // @ts-ignore
@ -115,7 +117,7 @@ describe("settings.tsx", () => {
it("Tests settings rendering", () => { it("Tests settings rendering", () => {
propsList.forEach((props) => { propsList.forEach((props) => {
const settings = render( const settings = render(
<Settings themes={props.themes} providers={props.providers} />, <Settings themes={props.themes} search={props.search} />,
); );
expect(settings.asFragment).toMatchSnapshot(); expect(settings.asFragment).toMatchSnapshot();
@ -125,10 +127,7 @@ describe("settings.tsx", () => {
// TODO: Finish this test // TODO: Finish this test
it("Tests theme setting", () => { it("Tests theme setting", () => {
const settings = render( const settings = render(
<Settings <Settings themes={propsList[0].themes} search={propsList[0].search} />,
themes={propsList[0].themes}
providers={propsList[0].providers}
/>,
); );
const toggleButton = settings.getByTestId("toggle-button"); const toggleButton = settings.getByTestId("toggle-button");