@conorheffron/ironoc-frontend 5.5.9 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@conorheffron/ironoc-frontend",
3
- "version": "5.5.9",
3
+ "version": "7.0.0",
4
4
  "private": false,
5
5
  "dependencies": {
6
- "@testing-library/jest-dom": "^5.17.0",
7
- "@testing-library/react": "^13.4.0",
8
6
  "@testing-library/user-event": "^13.5.0",
7
+ "axios": "^0.28.0",
9
8
  "bootstrap": "5.1",
10
9
  "react": "^18.3.1",
11
10
  "react-bootstrap": "^2.10.5",
@@ -15,7 +14,6 @@
15
14
  "react-cookie": "^7.2.2",
16
15
  "react-dom": "^18.3.1",
17
16
  "react-router-dom": "^5.3.0",
18
- "react-scripts": "^5.0.1",
19
17
  "reactstrap": "^8.10.0",
20
18
  "web-vitals": "^4.2.3"
21
19
  },
@@ -45,6 +43,13 @@
45
43
  },
46
44
  "devDependencies": {
47
45
  "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
46
+ "@testing-library/jest-dom": "^6.6.3",
47
+ "@testing-library/react": "^16.1.0",
48
+ "jest": "^27.5.1",
49
+ "react-scripts": "^5.0.1",
48
50
  "web-vitals": "^4.2.3"
51
+ },
52
+ "resolutions": {
53
+ "react-scripts/@svgr/webpack": "^6.2.1"
49
54
  }
50
55
  }
package/src/App.css CHANGED
@@ -73,3 +73,24 @@
73
73
  height: 13;
74
74
  width: 51;
75
75
  }
76
+
77
+ .carousel-caption h3, h5 {
78
+ color: navy;
79
+ }
80
+
81
+ p a {
82
+ color: yellow;
83
+ }
84
+
85
+ .carousel-caption h3, h5 {
86
+ background-color:yellow;
87
+ width: 50%;
88
+ height: auto;
89
+ margin: 0 auto;
90
+ }
91
+
92
+ .carousel-item img {
93
+ width: 50%;
94
+ height: auto;
95
+ margin: 0 auto;
96
+ }
package/src/App.js CHANGED
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
2
2
  import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
3
3
  import './App.css';
4
4
  import Home from './components/Home';
5
+ import CoffeeHome from './components/CoffeeHome';
5
6
  import NotFound from './components/NotFound';
6
7
  import About from './components/About';
7
8
  import RepoDetails from './components/RepoDetails';
@@ -19,6 +20,7 @@ class App extends Component {
19
20
  <Route path='/projects' exact={true} component={RepoDetails}/>
20
21
  <Route path='/projects/:id' component={RepoDetails}/>
21
22
  <Route path='/issues/:id/:repo' component={RepoIssues}/>
23
+ <Route path='/brews' exact={true} component={CoffeeHome}/>
22
24
  <Route path="*" component={NotFound} />
23
25
  </Switch>
24
26
  </Router>
package/src/App.test.js CHANGED
@@ -1,8 +1,97 @@
1
- import { render, screen } from '@testing-library/react';
1
+ import React from 'react';
2
+ import { render, screen, waitFor } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
2
4
  import Home from './components/Home';
5
+ import axios from 'axios';
6
+ import App from './App';
7
+ import CoffeeCarousel from './components/CoffeeCarousel';
8
+ import CoffeeHome from './components/CoffeeHome';
9
+ import NotFound from './components/NotFound';
10
+
11
+ // Mocking axios
12
+ jest.mock('axios');
3
13
 
4
14
  test('renders learn react link', () => {
5
15
  render(<Home />);
6
16
  const element = screen.getByText(/Home/i);
7
17
  expect(element).toBeInTheDocument();
8
18
  });
19
+
20
+ // Sample data for testing
21
+ const coffeeItems = [
22
+ {
23
+ title: 'Espresso',
24
+ ingredients: ['Water', 'Coffee beans'],
25
+ image: 'https://example.com/espresso.jpg',
26
+ },
27
+ {
28
+ title: 'Cappuccino',
29
+ ingredients: ['Espresso', 'Steamed milk', 'Foam milk'],
30
+ image: 'https://example.com/cappuccino.jpg',
31
+ },
32
+ ];
33
+
34
+ describe('CoffeeCarousel', () => {
35
+ test('renders carousel with coffee items', () => {
36
+ render(<CoffeeCarousel items={coffeeItems} />);
37
+
38
+ // Check that the carousel items are rendered
39
+ coffeeItems.forEach((item) => {
40
+ expect(screen.getByText(item.title)).toBeInTheDocument();
41
+ expect(screen.getByAltText(item.title)).toBeInTheDocument();
42
+ expect(screen.getByText(item.ingredients.join(', '))).toBeInTheDocument();
43
+ });
44
+ });
45
+
46
+ test('renders carousel with correct number of items', () => {
47
+ render(<CoffeeCarousel items={coffeeItems} />);
48
+
49
+ // Check that the correct number of carousel items are rendered
50
+ const carouselItems = screen.getAllByRole('img');
51
+
52
+ expect(carouselItems.length).toBe(coffeeItems.length);
53
+ });
54
+ });
55
+
56
+ describe('CoffeeHome', () => {
57
+ beforeEach(() => {
58
+ axios.get.mockResolvedValue({ data: coffeeItems });
59
+ });
60
+
61
+ test('renders AppNavbar component', () => {
62
+ render(<App />);
63
+ expect(screen.getByRole('banner')).toBeInTheDocument();
64
+ });
65
+
66
+ test('displays loading state initially', () => {
67
+ render(<CoffeeHome />);
68
+ expect(screen.getByText('Loading...')).toBeInTheDocument();
69
+ });
70
+
71
+ test('renders CoffeeCarousel component with coffee items', async () => {
72
+ render(<CoffeeHome />);
73
+
74
+ // Wait for the coffee items to be fetched and rendered
75
+ await waitFor(() => {
76
+ expect(screen.getByText('Espresso')).toBeInTheDocument();
77
+ expect(screen.getByText('Cappuccino')).toBeInTheDocument();
78
+ });
79
+ });
80
+ });
81
+
82
+ describe('NotFound', () => {
83
+ test('renders AppNavbar component', () => {
84
+ render(<NotFound />);
85
+ expect(screen.getByRole('banner')).toBeInTheDocument();
86
+ });
87
+
88
+ test('displays 404 error message', () => {
89
+ render(<NotFound />);
90
+ expect(screen.getByText('404 - Page Not Found')).toBeInTheDocument();
91
+ });
92
+
93
+ test('displays the apology message', () => {
94
+ render(<NotFound />);
95
+ expect(screen.getByText('Sorry, the page you are looking for could not be found.')).toBeInTheDocument();
96
+ });
97
+ });
package/src/AppNavbar.js CHANGED
@@ -18,6 +18,7 @@ export default function AppNavBar() {
18
18
  <DropdownMenu end>
19
19
  <DropdownItem href="/about">About</DropdownItem>
20
20
  <DropdownItem href="/portfolio">Portfolio</DropdownItem>
21
+ <DropdownItem href="/brews">Brews</DropdownItem>
21
22
  <DropdownItem divider />
22
23
  <DropdownItem href="https://linktr.ee/conorheffron" target="_blank" rel="noreferrer">Link Tree</DropdownItem>
23
24
  </DropdownMenu>
@@ -0,0 +1,15 @@
1
+ import Button from 'react-bootstrap/Button';
2
+ import Spinner from 'react-bootstrap/Spinner';
3
+ import { Container } from 'reactstrap';
4
+ import AppNavbar from './AppNavbar';
5
+
6
+ function LoadingSpinner() {
7
+ return (
8
+ <Container fluid>
9
+ <Button variant="primary" disabled>
10
+ <Spinner as="span" animation="grow" size="sm" role="status" aria-hidden="true"/>Loading...</Button>
11
+ </Container>
12
+ );
13
+ }
14
+
15
+ export default LoadingSpinner;
@@ -27,7 +27,8 @@ class About extends Component {
27
27
  <br /><br />
28
28
  I believe in continuous learning & practical skills that can be demonstrated in a positive & collaborative
29
29
  manner (open source is great!). When not learning or working, I like jogging/cycling, music, cooking,
30
- pretending to be a caffeine connoisseur, & searching for new forms of salsa verde / green sauce!
30
+ pretending to be a <a href="/brews">caffeine connoisseur</a>, & searching for new forms of
31
+ salsa verde / green sauce!
31
32
  <br /><br />
32
33
  Let's connect and explore exciting
33
34
  opportunities together! See above & beyond for contact details and further information.
@@ -35,7 +36,7 @@ class About extends Component {
35
36
  <a class="strava-badge" href='https://strava.com/athletes/2582329' target="_clean">
36
37
  Follow me on
37
38
  <img class="strava-badge-img" src='https://badges.strava.com/logo-strava.png' alt='Strava' />
38
- </a>
39
+ </a><br />
39
40
  </header>
40
41
  </Container>
41
42
  <Footer/>
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { Carousel } from 'react-bootstrap';
3
+ import '.././App.css';
4
+
5
+ const CoffeeCarousel = ({ items }) => {
6
+ return (
7
+ <Carousel className="App-header">
8
+ {items.map((item, index) => (
9
+ <Carousel.Item key={index}>
10
+ <img src={item.image} alt={item.title}/>
11
+ <Carousel.Caption>
12
+ <h3>{item.title}</h3>
13
+ <h5>{item.ingredients.join(', ')}</h5>
14
+ </Carousel.Caption>
15
+ </Carousel.Item>
16
+ ))}
17
+ </Carousel>
18
+ );
19
+ };
20
+
21
+ export default CoffeeCarousel;
@@ -0,0 +1,39 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { Button, Container, InputGroup, Table } from 'reactstrap';
3
+ import axios from 'axios';
4
+ import AppNavbar from '.././AppNavbar';
5
+ import CoffeeCarousel from './CoffeeCarousel';
6
+ import Footer from '.././Footer';
7
+ import LoadingSpinner from '.././LoadingSpinner';
8
+
9
+ function CoffeeHome() {
10
+ const [coffeeItems, setCoffeeItems] = useState([]);
11
+
12
+ useEffect(() => {
13
+ axios.get('/coffees')
14
+ .then(response => setCoffeeItems(response.data))
15
+ .catch(error => console.error('Error fetching coffee details:', error));
16
+ }, []);
17
+
18
+ return (
19
+ <div className="App">
20
+ <AppNavbar/>
21
+ <Container>
22
+ {coffeeItems.length > 0 ? (
23
+ <>
24
+ <br /><br />
25
+ <CoffeeCarousel items={coffeeItems} />
26
+ </>
27
+ ) : (
28
+ <>
29
+ <br /><br /><br />
30
+ <LoadingSpinner/>
31
+ </>
32
+ )}
33
+ </Container>
34
+ <Footer/>
35
+ </div>
36
+ );
37
+ }
38
+
39
+ export default CoffeeHome;
@@ -24,7 +24,7 @@ class ControlledCarousel extends Component {
24
24
  <h1><u>ironoc-db</u></h1>
25
25
  <h2>Sample Data Manager Service with UI</h2>
26
26
  <br /><br />
27
- <h3>Tech Stack:</h3>
27
+ <h4>Tech Stack:</h4>
28
28
  <h4>Java & Spring Boot, Thymeleaf Templating Engine, & MySQL.</h4>
29
29
  </Carousel.Caption>
30
30
  </a>
@@ -36,19 +36,19 @@ class ControlledCarousel extends Component {
36
36
  <h1><u>booking-sys</u></h1>
37
37
  <h2>Sample Reservations & Viewer System</h2>
38
38
  <br /><br />
39
- <h3>Tech Stack:</h3>
39
+ <h4>Tech Stack:</h4>
40
40
  <h4>Python & Django Web App, JavaScript, SQLite3 or MySQL database.</h4>
41
41
  </Carousel.Caption>
42
42
  </a>
43
43
  </Carousel.Item>
44
44
  <Carousel.Item interval={500}>
45
- <a href="https://github.com/cph33/nba-stats" target="_blank" rel="noreferrer">
45
+ <a href="https://github.com/conorheffron/nba-stats" target="_blank" rel="noreferrer">
46
46
  <img className="d-block w-100" src={navy} alt="navy3" />
47
47
  <Carousel.Caption>
48
48
  <h1><u>nba-stats</u></h1>
49
49
  <h2>NBA Analytics (Seasons 2015 - 2023): Player Statistics</h2>
50
50
  <br /><br />
51
- <h3>Tech Stack:</h3>
51
+ <h4>Tech Stack:</h4>
52
52
  <h4>Jupyter Notebooks, Python, Pandas, & Requests / JSON API.</h4>
53
53
  </Carousel.Caption>
54
54
  </a>
@@ -60,7 +60,7 @@ class ControlledCarousel extends Component {
60
60
  <h1><u>cbio-skin-canc</u></h1>
61
61
  <h2>Skin Cancer Dataset Analysis</h2>
62
62
  <br /><br />
63
- <h3>Tech Stack:</h3>
63
+ <h4>Tech Stack:</h4>
64
64
  <h4>R, dplyr, plotly, knitr, testthat, covr, GIT.</h4>
65
65
  </Carousel.Caption>
66
66
  </a>
@@ -72,7 +72,7 @@ class ControlledCarousel extends Component {
72
72
  <h1><u>gene-expr</u></h1>
73
73
  <h2>Breast Cancer Dataset Analysis</h2>
74
74
  <br /><br />
75
- <h3>Tech Stack:</h3>
75
+ <h4>Tech Stack:</h4>
76
76
  <h4>R, ggplot2, dplyr, deseq2-analysis, & R markdown.</h4>
77
77
  </Carousel.Caption>
78
78
  </a>
@@ -84,7 +84,7 @@ class ControlledCarousel extends Component {
84
84
  <h1><u>bio-cell-red-edge</u></h1>
85
85
  <h2>Edge Detection of Biological Cell (Image Processing Script)</h2>
86
86
  <br /><br />
87
- <h3>Tech Stack:</h3>
87
+ <h4>Tech Stack:</h4>
88
88
  <h4>Python, sci-kit-image, matplotlib.pyplot, & scipy.ndimage.</h4>
89
89
  </Carousel.Caption>
90
90
  </a>
@@ -96,7 +96,7 @@ class ControlledCarousel extends Component {
96
96
  <h1><u>global-max-sim-matrix</u></h1>
97
97
  <h2>Compute Global Maximum Similarity Matrix</h2>
98
98
  <br /><br />
99
- <h3>Tech Stack:</h3>
99
+ <h4>Tech Stack:</h4>
100
100
  <h4>R Package, testthat, stringr, & devtools.
101
101
  </h4>
102
102
  </Carousel.Caption>
@@ -16,18 +16,19 @@ class Home extends Component {
16
16
  <br /><br />
17
17
  <a href="/"><img src={logo} className="App-logo" alt="iRonoc"/></a>
18
18
  <p id="my-intro"> Welcome to my personal portfolio site.<br />
19
- Please use the navigation bar to view different features such as about me, my link tree, a carousel
20
- that scrolls through highlighted projects & the GitHub project manager (PM) tool which is built
19
+ Please use the navigation bar to view different features such as <a href="/about">about</a> me, my
20
+ &nbsp;<a href="https://linktr.ee/conorheffron">link tree</a>, a <a href="/portfolio">carousel</a>
21
+ &nbsp;that scrolls through highlighted projects & the GitHub project manager (PM) tool which is built
21
22
  against the iRonoc API.
22
23
  <br /><br />
23
24
  The GitHub PM tool allows you to view & navigate the backlog of issues & bugs for a given project
24
25
  repository for the corresponding user or organisation account. There is an option to search by user ID
25
26
  or to drill down to a specific repository name via search or 'List Issues' icon in the 'Actions' column
26
- of the <a href="https://www.ironoc.net/projects/conorheffron">projects component view</a>.
27
+ of the <a href="/projects/conorheffron">projects component view</a>.
27
28
  <br /><br />
28
- The ironoc API is documented with <a href="https://www.ironoc.net/swagger-ui-ironoc.html">Open API</a>
29
- & sample GET requests that return raw JSON responses are available for demonstration purposes only i.e.
30
- <a href="https://www.ironoc.net/get-repo-issue/conorheffron/ironoc/">Issues JSON Sample</a>.</p>
29
+ The ironoc API is documented with &nbsp;<a href="/swagger-ui-ironoc.html">Open API</a>
30
+ &nbsp; & sample GET requests that return raw JSON responses are available for demonstration
31
+ purposes only i.e. &nbsp;<a href="/get-repo-issue/conorheffron/ironoc/">Issues JSON Sample</a>.</p>
31
32
  </header>
32
33
  </Container>
33
34
  <Footer/>
@@ -5,6 +5,7 @@ import Form from 'react-bootstrap/Form';
5
5
  import AppNavbar from '.././AppNavbar';
6
6
  import Footer from '.././Footer';
7
7
  import { Link } from 'react-router-dom';
8
+ import LoadingSpinner from '.././LoadingSpinner';
8
9
 
9
10
  class RepoDetails extends Component {
10
11
 
@@ -39,7 +40,16 @@ class RepoDetails extends Component {
39
40
  const {repoDetailList, isLoading} = this.state;
40
41
 
41
42
  if (isLoading) {
42
- return <p>Loading...</p>;
43
+ return (
44
+ <div className="App">
45
+ <AppNavbar/>
46
+ <Container>
47
+ <br /><br /><br />
48
+ <LoadingSpinner/>
49
+ </Container>
50
+ <Footer/>
51
+ </div>
52
+ );
43
53
  }
44
54
 
45
55
  let gitUser = this.props.match.params.id;
@@ -4,6 +4,7 @@ import '.././App.css';
4
4
  import Form from 'react-bootstrap/Form';
5
5
  import AppNavbar from '.././AppNavbar';
6
6
  import Footer from '.././Footer';
7
+ import LoadingSpinner from '.././LoadingSpinner';
7
8
 
8
9
  class RepoIssues extends Component {
9
10
 
@@ -39,8 +40,18 @@ class RepoIssues extends Component {
39
40
  render() {
40
41
  const {repoIssueList, isLoading} = this.state;
41
42
  if (isLoading) {
42
- return <p>Loading...</p>;
43
+ return (
44
+ <div className="App">
45
+ <AppNavbar/>
46
+ <Container>
47
+ <br /><br /><br />
48
+ <LoadingSpinner/>
49
+ </Container>
50
+ <Footer/>
51
+ </div>
52
+ );
43
53
  }
54
+
44
55
  const repoList = repoIssueList.map(issue => {
45
56
  let username = this.props.match.params.id
46
57
  let repository = this.props.match.params.repo