Commit f0039635 authored by Mikkel Marstein's avatar Mikkel Marstein
Browse files

Merge branch 'Hent-Data-Fra-Backend-Med-Api' into 'master'

Hent data fra backend med api

See merge request !7
parents 85839fed cdd100d1
......@@ -2,9 +2,29 @@
const Country = require('./country');
module.exports = {
country: async () => {
country: async (args) => {
try {
const countryFetched = await Country.find();
let kontinent = String(args.continent_contains || "")
let search = String(args.search || "")
sortOrder = {};
if(args.sortBy !== null){
switch(args.sortBy){
case("name"):
sortOrder = { name: (args.order === "ASC" ? 1 : -1)};
break;
case("capital"):
sortOrder = { capital: (args.order === "ASC" ? 1 : -1)};
break;
}
}
let start = parseInt(args.start || 0);
let limit = parseInt(args.limit || -1);
const countryFetched = await Country.find({
continent: {$regex: kontinent, '$options' : 'i'},
name: {$regex: search, '$options' : 'i'}})
.limit(limit === -1 ? '$count' : limit)
.skip(start)
.sort(sortOrder);
return countryFetched.map((country) => {
return {
...country._doc,
......@@ -14,5 +34,17 @@ module.exports = {
} catch (error) {
throw error;
}
},
TotalCountires: async (args) => {
try {
let kontinent = String(args.continent_contains || "")
let search = String(args.search || "")
const countryFetched = await Country.find({
continent: {$regex: kontinent, '$options' : 'i'},
name: {$regex: search, '$options' : 'i'}});
return countryFetched.length;
} catch (error) {
throw error;
}
}
}
}
\ No newline at end of file
......@@ -6,20 +6,30 @@ module.exports = buildSchema(`
_id: ID!
name: String!
native: String
continent: String!
continent: String
capital: String!
currency: String
languages: [String]
}
type TotalCountires{
sum: Int
}
input CountryInput {
navn: String!
capital: String!
}
type Query {
country:[Country!]
country(continent_contains: [String], start: Int, limit: Int, order: String, sortBy: String, search: String): [Country!]
TotalCountires(continent_contains: [String], start: Int, limit: Int, order: String, sortBy: String, search: String): Int
}
input NameOrder {
nameOrder: String
}
input ContinentFilter {
OR: [ContinentFilter!]
continent_contains: String
name_contains: String
}
type Mutation {
createCountry(country:CountryInput): Country
......
......@@ -45,5 +45,9 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"cypress": "^8.7.0",
"jest": "^27.3.1"
}
}
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
import { CardContainer } from './components/display/CardContainer';
import Layout from './components/Layout';
import ReactDOM from 'react-dom';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
const { getByText } = render(
<div className="App">
<Layout title="s">
<CardContainer/>
</Layout>
</div>
);
const SearchButton = getByText("Search");
expect(SearchButton).toBeInTheDocument();
});
test("it renders without crashing", () => {
const spanTest = document.createElement("li");
ReactDOM.render(
<div className="App">
<Layout title="s">
<CardContainer/>
</Layout>
</div>,
spanTest
);
ReactDOM.unmountComponentAtNode(spanTest);
});
......@@ -3,7 +3,8 @@ import { Header } from './header/Header';
import "./Layout.css"
import { Searchbar } from './searchbar/searchbar';
import { Filter } from './searchbar/Filter';
import { getData } from '../utils/APIUtil';
import { filterStore } from '../utils/filterStore';
import { Provider } from 'react-redux';
interface IProps {
title: string
......@@ -12,18 +13,18 @@ const Layout: React.FC<IProps> = ({
title,
children
}) => {
getData()
return (
<div className="Layout">
<Provider store={filterStore}>
<React.Fragment >
<Header>
<Searchbar/>
<Filter/>
</Header>
<div className="Layout__Content">
<main>{children}</main>
</div>
</React.Fragment>
</Provider>
</div>
);
};
......
.ContinentCheckbox{
display: flex;
align-items: flex-start;
gap:30px;
margin-top: 200px;
}
\ No newline at end of file
import { fireEvent, getByTestId, render,screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import React, { useState } from "react";
import App from "../../App";
import { ContinentCheckbox } from "./ContinentCheckbox";
describe("<ContinentCheckbox />", () => {
test('render component', () => {
render(<ContinentCheckbox />);
const asia = screen.getByText("Asia");
expect(asia).toBeInTheDocument();
expect(asia).not.toBeChecked();
userEvent.click(asia);
//expect(asia.getAttribute).toBeFalsy;
});
})
import React, { FunctionComponent, useState } from 'react';
import { Button, ButtonGroup, Form } from 'react-bootstrap';
import { searchbarStore } from './SearchbarStore';
//import './CountryCard.css';
interface Iprops{
handleClick: (value: string) => void
}
export const ContinentCheckbox: FunctionComponent<Iprops> = ({ handleClick
}) => {
return <>
<div className="ContinentCheckbox"></div>
<ButtonGroup aria-label="Basic example">
<Button variant="secondary" onClick={() => {handleClick("AS")}}>Asia</Button>
<Button variant="secondary" onClick={() => {handleClick("EU")}}>Europa</Button>
<Button variant="secondary" onClick={() => {handleClick("AF")}}>Afrika</Button>
<Button variant="secondary" onClick={() =>{handleClick("SA")}}>Sør-Amerika</Button>
<Button variant="secondary" onClick={() =>{handleClick("NA")}}>Nord-Amerika</Button>
<Button variant="secondary" onClick={() =>{handleClick("OC")}}>Oseania</Button>
</ButtonGroup>
</>
}
......@@ -10,7 +10,7 @@
display: inline-block;
position: absolute;
left: 40rem;
top: 2rem;
top: 1.5rem;
z-index: 20;
}
.Filter__Title, .Filter__Container__closed:hover {
......@@ -26,16 +26,18 @@
.Filter__Big_Buttons{
display: flex;
flex-flow: row;
height: 10vmin;
height: 2vmin;
width: 50vmin;
justify-Content: space-around;
align-Items: center;
}
Button{
margin: 5px;
z-index: 20;
height: 55px;
}
@media only screen and (max-width: 1000px) {
.Filter__Big_Container{
display: none;
......
import { CollapseFilled, ExpandFilled, Settings } from '@navikt/ds-icons';
import React, { FunctionComponent, useState } from 'react';
import { Button } from 'react-bootstrap';
import { Button, Form } from 'react-bootstrap';
import { ContinentCheckbox } from './ContinentCheckbox';
import './Filter.css';
type IProps = {}
export const Filter: FunctionComponent<IProps> = () => {
import './ContinentCheckbox.css'
import { filterStore } from '../../utils/filterStore';
type IProps = {
onButtonClick: (value: string[]) => void
filterClick: (value: string) => void
}
export const Filter: FunctionComponent<IProps> = ({onButtonClick, filterClick}) => {
const [hidden, setHidden] = useState(true);
const filterOptions = <>
<Button variant="secondary">Name</Button>
<Button variant="secondary">Official languages</Button>
<Button variant="secondary">Option 3</Button>
<div className={"Sorting"}>
<Button variant="secondary"
onClick={()=> onButtonClick(["name", "ASC"])}>Name abc</Button>
<Button variant="secondary"
onClick={()=> onButtonClick(["name", "DESC"])}>Name cba</Button>
<Button variant="secondary"
onClick={()=> onButtonClick(["capital", "ASC"])}>Capital abc</Button>
<Button variant="secondary"
onClick={()=> onButtonClick(["capital", "DESC"])}>Capital cba</Button>
</div>
<div className={"ContinentCheckbox"}>
<ContinentCheckbox handleClick={filterClick}/>
</div>
</>;
return (
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FocusInput matches snapshot 1`] = `
<div
class="searchbar"
>
<input
class="form-control"
placeholder="Enter Country"
type="text"
/>
<div
class="input-group-append"
>
<button
class="btn btn-outline-secondary"
id="search_button"
type="button"
>
Search
</button>
</div>
</div>
`;
[
{
"AD": {
"name": "Andorra",
"native": "Andorra",
"phone": "376",
"continent": "EU",
"capital": "Andorra la Vella",
"currency": "EUR",
"languages": [
"ca"
]
},
"AE": {
"name": "United Arab Emirates",
"native": "دولة الإمارات العربية المتحدة",
"phone": "971",
"continent": "AS",
"capital": "Abu Dhabi",
"currency": "AED",
"languages": [
"ar"
]
},
"AF": {
"name": "Afghanistan",
"native": "افغانستان",
"phone": "93",
"continent": "AS",
"capital": "Kabul",
"currency": "AFN",
"languages": [
"ps",
"uz",
"tk"
]
},
"AG": {
"name": "Antigua and Barbuda",
"native": "Antigua and Barbuda",
"phone": "1268",
"continent": "NA",
"capital": "Saint John's",
"currency": "XCD",
"languages": [
"en"
]
},
"AI": {
"name": "Anguilla",
"native": "Anguilla",
"phone": "1264",
"continent": "NA",
"capital": "The Valley",
"currency": "XCD",
"languages": [
"en"
]
}
}
]
\ No newline at end of file
......@@ -22,3 +22,8 @@
margin-top: 1vmin;
}
}
.page_counter{
position: fixed;
bottom: 20px;
z-index: 40;
}
import React from "react";
import { fireEvent, render } from "@testing-library/react";
import { Searchbar } from './searchbar';
import App from "../../App";
import { Provider } from "react-redux";
import { searchbarStore } from "./SearchbarStore";
test('FocusInput matches snapshot', () => {
const { container } = render(<Searchbar />)
expect(container.firstChild).toMatchSnapshot();
});
describe('Component: Searchvar', () => {
const items =["Norway", "India", "Germany"];
describe("<searchbar />", () => {
// test('Check placholer value', () => {
// const container = render(<Searchbar />)
// expect(container.find('.searchbar').at(0).props().placeholder).toEqual("Enter Country")
});
});
function shallow(arg0: JSX.Element) {
throw new Error("Function not implemented.");
}
/*
test('calls correct function on click', () => {
const onClick = jest.fn();
const jo = render(getById("search_button") />)
expect(onClick).toHaveBeenCalled();
});
*/
//});
import './searchbar.css'
import React from 'react';
import { Country } from '../../interfaces/Country';
import './SearchbarStore.tsx'
import { searchbarStore } from './SearchbarStore';
import { Search } from '@navikt/ds-icons';
import { getData } from '../../utils/APIUtil';
import { getAllData, getNumberOfResults } from '../../utils/APIUtil';
import { store } from '../display/Store';
import { connect } from 'react-redux';
import { Filter } from './Filter';
const PAGE_LIMIT: number = 20;
interface IProps {
items?: string[]
}
interface IState {
active: boolean,
items: string[]
search_text: string,
page_number: number,
filter_order: string[],
continent_filter: string,
totalItems: number
}
export class Searchbar extends React.Component<IProps, IState> {
constructor(props: IState) {
super(props);
super(props);
this.state = {
active: false,
items: []
search_text: "",
page_number: 0,
filter_order: [],
continent_filter: "",
totalItems: 0
};
}
createFilter(): string{
let filter: string = "(";
if(this.state.filter_order[0] !== ""){
filter+=`sortBy: "${this.state.filter_order[0]}", order: "${this.state.filter_order[1]}",`
}
filter+=`start: ${this.state.page_number*PAGE_LIMIT}, limit: ${PAGE_LIMIT}, `
filter += `continent_contains: "${this.state.continent_filter}", `;
filter += `search: "${this.state.search_text}"`;
filter += ")";
return filter;
}
sendNewCall(){
getAllData(this.createFilter()).then((data) => {
searchbarStore.dispatch({ type: 'setState', NewCountry: data });
})
}
gotoPage(page: number){
if(page === this.state.page_number){
return;
}
store.dispatch({ type: 'setState', newValue: -1 });
this.setState({page_number: page})
}
onSearch() {
getData().then((data) => {
store.dispatch({ type: 'setState', newValue: -1 });
this.setState({page_number: 0})
}
componentDidUpdate(nextProps: any, nextState: any) {
getNumberOfResults(this.createFilter()).then((data) => {
this.setState({totalItems: data})
})
this.sendNewCall()
}
componentDidMount(){
getAllData(`(start: ${this.state.page_number*PAGE_LIMIT}, limit: ${PAGE_LIMIT})`).then((data) => {
searchbarStore.dispatch({ type: 'setState', NewCountry: data });
})
getNumberOfResults().then((data) => {
this.setState({totalItems: data})
})
}
render() {
return (
<>
<div className="searchbar">
<input type="text" className="form-control" placeholder="Enter Country" />
<input type="text" className="form-control" placeholder="Enter Country" onInput={(e)=>{this.setState({search_text: e.currentTarget.value});this.setState({page_number: 0});}}/>
<div className="input-group-append">
<button onClick={() => this.onSearch()}
className="btn btn-outline-secondary" type="button"><Search/>Search</button>
<button id="search_button" onClick={() => this.onSearch()}
className="btn btn-outline-secondary" type="button"> Search</button>
</div>
{this.state.active && <div>Jeg er active</div>}
</div>)
{this.state.active }
</div>
<div className={"page_counter"}>
{[...Array(Math.ceil(this.state.totalItems/PAGE_LIMIT))].map((e, i) =>
<button key={i} className="btn btn-outline-secondary"
onClick={()=> this.gotoPage(i)}>{i+1}</button>)}