Jaime Ramírez
2020-06-01 88bc9874f5270409803dfccac6f4fba5268bbafa
Added news
8 files added
7 files modified
236 ■■■■■ changed files
adopt-a-pup/web-app/package.json 2 ●●● patch | view | raw | blame | history
adopt-a-pup/web-app/public/photos/d1.jpeg patch | view | raw | blame | history
adopt-a-pup/web-app/src/App.css 5 ●●●●● patch | view | raw | blame | history
adopt-a-pup/web-app/src/App.test.tsx 3 ●●●● patch | view | raw | blame | history
adopt-a-pup/web-app/src/App.tsx 16 ●●●● patch | view | raw | blame | history
adopt-a-pup/web-app/src/Components/AnimalList.test.tsx 26 ●●●●● patch | view | raw | blame | history
adopt-a-pup/web-app/src/Components/NavList.tsx 4 ●●●● patch | view | raw | blame | history
adopt-a-pup/web-app/src/Layout.tsx 2 ●●● patch | view | raw | blame | history
adopt-a-pup/web-app/src/Services/AnimalFakeService.ts 32 ●●●●● patch | view | raw | blame | history
adopt-a-pup/web-app/src/Services/AnimalService.ts 14 ●●●●● patch | view | raw | blame | history
adopt-a-pup/web-app/src/Services/NewsFakeService.ts 12 ●●●●● patch | view | raw | blame | history
adopt-a-pup/web-app/src/Services/NewsService.ts 10 ●●●●● patch | view | raw | blame | history
adopt-a-pup/web-app/src/Views/NewsView.test.tsx 26 ●●●●● patch | view | raw | blame | history
adopt-a-pup/web-app/src/Views/NewsView.tsx 80 ●●●●● patch | view | raw | blame | history
adopt-a-pup/web-app/src/Views/SheltersView.tsx 4 ●●●● patch | view | raw | blame | history
adopt-a-pup/web-app/package.json
@@ -20,7 +20,7 @@
    "typescript": "~3.7.2"
  },
  "scripts": {
    "start": "react-scripts start",
    "start": "REACT_APP_NEWS_ENABLED=1 react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
adopt-a-pup/web-app/public/photos/d1.jpeg
adopt-a-pup/web-app/src/App.css
@@ -12,8 +12,10 @@
    monospace;
}
.logo {
  height: 2rem;
}
/*
.centered {
  text-align: center;
}
@@ -51,4 +53,3 @@
  animation-name: popupFadeIn;
  animation-duration: .3s;
}
*/
adopt-a-pup/web-app/src/App.test.tsx
@@ -2,8 +2,9 @@
import { render } from "@testing-library/react";
import App from "./App";
test("renders learn react link", () => {
    const { getByText } = render(<App />);
    const linkElement = getByText(/learn react/i);
    const linkElement = getByText(/Adopt a pup/i);
    expect(linkElement).toBeInTheDocument();
});
adopt-a-pup/web-app/src/App.tsx
@@ -9,24 +9,31 @@
// import ShelterRESTService from "./Services/ShelterRESTService";
import AnimalList from "./Components/AnimalList";
import SheltersView from "./Views/SheltersView";
import NewsView from "./Views/NewsView";
import ShelterFakeService from "./Services/ShelterFakeService";
import NewsFakeService from "./Services/NewsFakeService";
// import ShelterRESTService from "./Services/ShelterRESTService";
// Services to connect to backends
const shelterService = new ShelterFakeService();
// Uncomment to use a real backend
// const shelterService = new ShelterRESTService(SERVICE_BASE_URL);
const newsService = new NewsFakeService();
// The main React component that runs the whole webapp
export default class App extends Component {
    render() {
        const enableNews = process.env.REACT_APP_NEWS_ENABLED;
        return (
            <Router basename="/frontend">
                <Switch>
                    <Structure>
                        <Route path="/" exact >
                            Main
                            Adopt a pup
                        </Route>
                        <Route path="/shelters" exact>
                            <SheltersView shelterService={shelterService} />
@@ -34,6 +41,11 @@
                        <Route path="/your-animals" exact>
                            <AnimalList />
                        </Route>
                        {enableNews &&
                        <Route path="/news" exact>
                            <NewsView newsService={newsService} />
                        </Route>
                        }
                    </Structure>
                </Switch>
            </Router>
adopt-a-pup/web-app/src/Components/AnimalList.test.tsx
New file
@@ -0,0 +1,26 @@
import React from "react";
import { render } from "@testing-library/react";
import AnimalList from "./AnimalList";
import NewsFakeService from "../Services/NewsFakeService";
describe("AnimalList", () => {
    test("Shows a message before loading results", () => {
        const newsService = new NewsFakeService();
        const { getByText } = render(<AnimalList newsService={newsService} />);
        const linkElement = getByText(/No results found/i);
        expect(linkElement).toBeInTheDocument();
    });
    test("Shows the loaded results", async() => {
        const newsService = new NewsFakeService();
        const { findByText } = render(<AnimalList newsService={newsService} />);
        const linkElement = await findByText(/News 1/i);
        expect(linkElement).toBeInTheDocument();
    });
});
adopt-a-pup/web-app/src/Components/NavList.tsx
@@ -13,6 +13,7 @@
    public render() {
        const { pathname } = window.location;
        const enableNews = process.env.REACT_APP_NEWS_ENABLED;
        return (
            <Nav theme="dark">
                <NavList>
@@ -25,6 +26,9 @@
                    <NavItem id="your-animals" isActive={pathname.endsWith("/your-animals")}>
                        <Link to="/your-animals" >Your Animals</Link>
                    </NavItem>
                    {enableNews && <NavItem id="news" isActive={pathname.endsWith("/news")}>
                        <Link to="/news" >News</Link>
                    </NavItem>}
                </NavList>
            </Nav>
        );
adopt-a-pup/web-app/src/Layout.tsx
@@ -32,7 +32,7 @@
        const Header = (
            <PageHeader
                logo={<Brand src={imgBrand} alt="Patternfly Logo" />}
                logo={<Brand src={imgBrand} alt="Red Hat Training Logo" className="logo"/>}
                logoProps={logoProps}
                showNavToggle
                isNavOpen={isNavOpen}
adopt-a-pup/web-app/src/Services/AnimalFakeService.ts
New file
@@ -0,0 +1,32 @@
import { AnimalService, Animal } from "./AnimalService";
export default class AnimalFakeService implements AnimalService {
    public async create(): Promise<void> {
        throw new Error("Method not implemented.");
    }
    public async getAllAdoptable(): Promise<Animal[]> {
        return [
            {
                animalId: "a1",
                animalName: "Dog 1",
                breed: "Shepherd",
                shelterId: "s1",
                adoptable: true
            }
        ];
    }
    public async getById(id: string): Promise<Animal> {
        return {
            animalId: id,
            animalName: "Dog 1",
            breed: "Shepherd",
            shelterId: "s1",
            adoptable: true
        };
    }
}
adopt-a-pup/web-app/src/Services/AnimalService.ts
New file
@@ -0,0 +1,14 @@
export interface AnimalService {
    create(): Promise<void>;
    getAllAdoptable(): Promise<Animal[]>;
    getById(id: string): Promise<Animal>;
}
export interface Animal {
    animalId: string;
    animalName: string;
    shelterId: string;
    breed: string;
    adoptable: boolean;
}
adopt-a-pup/web-app/src/Services/NewsFakeService.ts
New file
@@ -0,0 +1,12 @@
import { NewsService, News } from "./NewsService";
export default class NewsFakeService implements NewsService {
    public async getAll(): Promise<News[]> {
        return [
            { id: "n1", title: "News 1", timestamp: "1970-01-01 00:00:01" },
            { id: "n2", title: "News 2", timestamp: "1970-01-01 00:00:01" }
        ];
    }
}
adopt-a-pup/web-app/src/Services/NewsService.ts
New file
@@ -0,0 +1,10 @@
export interface NewsService {
    getAll(): Promise<any[]>;
}
export interface News {
    id: string;
    title: string;
    timestamp: string;
}
adopt-a-pup/web-app/src/Views/NewsView.test.tsx
New file
@@ -0,0 +1,26 @@
import React from "react";
import { render } from "@testing-library/react";
import NewsView from "./NewsView";
import NewsFakeService from "../Services/NewsFakeService";
describe("NewsView", () => {
    test("Shows a message before loading results", () => {
        const newsService = new NewsFakeService();
        const { getByText } = render(<NewsView newsService={newsService} />);
        const linkElement = getByText(/No results found/i);
        expect(linkElement).toBeInTheDocument();
    });
    test("Shows the loaded results", async() => {
        const newsService = new NewsFakeService();
        const { findByText } = render(<NewsView newsService={newsService} />);
        const linkElement = await findByText(/News 1/i);
        expect(linkElement).toBeInTheDocument();
    });
});
adopt-a-pup/web-app/src/Views/NewsView.tsx
New file
@@ -0,0 +1,80 @@
import React from "react";
import { Table, TableHeader, TableBody } from "@patternfly/react-table";
import { EmptyState, EmptyStateIcon, EmptyStateBody, EmptyStateVariant, Bullseye, Title } from "@patternfly/react-core";
import { ErrorCircleOIcon } from "@patternfly/react-icons";
import { NewsService, News } from "../Services/NewsService";
type NewsViewProps = {
    newsService: NewsService;
}
type NewsViewState = {
    news: News[]
}
export default class NewsView extends React.Component<NewsViewProps, NewsViewState> {
    constructor(props: NewsViewProps) {
        super(props);
        this.state = {
            news: []
        };
    }
    public async componentDidMount() {
        const news = await this.props.newsService.getAll();
        this.setState({
            news
        });
    }
    public render() {
        return (
            <Table caption="Latest News" rows={this.getRows()} cells={this.getColumns()}>
                <TableHeader />
                <TableBody />
            </Table>
        );
    }
    private getColumns(): [string, string] {
        return ["Timestamp", "Story"];
    }
    private getRows() {
        if (this.state.news.length === 0) {
            return this.getRowsForEmptyTable();
        }
        return this.state.news.map(this.newsToRow);
    }
    private newsToRow(newsItem: News) {
        return { cells: [newsItem.timestamp, newsItem.title] };
    }
    private getRowsForEmptyTable() {
        return  [{
            heightAuto: true,
            cells: [
                {
                    props: { colSpan: 2 },
                    title: (
                        <Bullseye>
                            <EmptyState variant={EmptyStateVariant.small}>
                                <EmptyStateIcon icon={ErrorCircleOIcon} />
                                <Title headingLevel="h2" size="lg">
                                    No results found
                                </Title>
                                <EmptyStateBody>
                                    Unable to get news from external feed.
                                </EmptyStateBody>
                            </EmptyState>
                        </Bullseye>
                    )
                },
            ]
        }];
    }
}
adopt-a-pup/web-app/src/Views/SheltersView.tsx
@@ -5,12 +5,12 @@
import { PageSection, PageSectionVariants, Text, TextContent } from "@patternfly/react-core";
type ShelterListProps = {
type SheltersViewProps = {
    shelterService: ShelterService;
}
export default class SheltersView extends React.Component<ShelterListProps> {
export default class SheltersView extends React.Component<SheltersViewProps> {
    public render() {
        return (