A ReactJS implementation of a single-page portfolio website for showcasing categorized creative projects. Intended as an base template for future adaptation.
Live Demo: https://www.garysmith.ca/demos/react-portfolio/
This project has been tested with the following stack:
- Node 16.10.0
- npm 7.24.1
- npx 7.24.1
With the above dependencies install, you can run this application on your local command line for testing or development as follows:
git clone https://github.com/garyesmith/react-portfolio.gitcd react-portfolionpm installnpm start
Then visit http://localhost:3000/demos/react-portfolio in your browser (if it does not open automatically)
The Jest Javascript Testing Framework has been used to create basic unit tests for most components in this application. Test cases are located in the files named with the convention Component.test.js in the /src/components folder, where Component.js defines the component to be tested.
To run all test cases, execute the following command from within the project folder:
npm test
To run tests for a single component, specify the name of the component's test file, for example:
npm test Component.test.js
After execution, the results (with passed cases in green and failed cases in red) will be output to the console, along with explanations for any failures. When components are updated in ways that change their output, these test cases will likely need to be updated as well.
- Edit
package.jsonand set thehomepagevalue to be the path where you will host the app inside your webserver docroot. - Run
npm run buildon the command line from inside the project folder. - FTP or push the contents of the resulting
/sourcefolder to your webserver docroot, at the folder location specified in step 1, above. - In a browser, open the URL that corresponds to the location where you have deployed the app.
This application includes a placeholder administration route at /admin, intended to eventually house tools for creating, editing, and deleting content on the portfolio.
Access to this route is protected by Auth0 authentication. To make this route available, you will need to perform the following configuration:
On the Auth0 website:
- Register a free account at Auth0.
- Create an application: choose type Single Page Web Application and then React.
- Under Settings for your application, copy/obtain the Domain and Client ID values for use below.
- Enter your Portfolio application's domain and/or server paths into the Allowed Callback URLs, Allowed Logout URLs, and Allowed Web Origins boxes. Comma-separate multiple domain paths as necessary. Be sure to include the /admin route as one of the allowed callbacks. If you are also testing locally, you can add
http://localhost:3000domain paths here as well. - Scroll to the bottom of the form and click the
Save Changesbutton.
On your server:
-
Create an
.envfile in the root of your application by copying the provided.env-samplefile:mv .env-sample .env -
Edit the
.envfile and paste the Domain and Client ID values obtained in the Auth0 application form asREACT_APP_AUTH0_DOMAINandREACT_APP_AUTH0_CLIENT_IDrespectively; then save the file. -
Restart your application with
npm startto ensure the.envchanges are applied to the build. -
Open the Portfolio app in a browser, add
/adminto the end of the URL, and confirm you can log in. If you receive errors, double check that you have the correct Allowed URLs in the Auth0 app as described in the previous section.
Restricting access
By default, Auth0 will permit anyone with a valid social media account to register and log in to the application. Since this path is intended to be a restricted admin panel, this behaviour is not desired. You can refer to the extensive Auth0 documentation to consider various ways to restrict access, such as using an invite-only setup, or restricting signups using rules or actions.
One quick way to restrict access to a small whitelist of trusted email addresses is to create Rule inside the Auth0 control panel under Auth Pipeline > Rules, as described here. You might also want to create a Force email verification rule for additional security.
The sample demo reads configuration values, categories and project details from static JSON files included with this repository. On a production site, it should be straightforward to change the fetch calls in App.js to instead read data from API endpoints that return JSON in the correct format.
To change the logo image displayed in the header, replace the default image at /src/images/logo.png with your preferred image.
Colors used for the navigation bar can be customized by assigning valid color values to these SCSS variables defined at the top of the /components/Navbar.scss file:
- $navBackgroundColor - The default background color the navbar.
- $navTextColor - The default text color for the navbar links.
- $navSelectedBackgroundColor - The background color for the currently active navbar link.
- $navSelectedTextColor - The text color for the currently actively navbar link.
The file /data/config.json defines a single JSON object describing the overall configuration of the app, as in the following example:
{
"siteName": "React Portfolio",
"siteDescription": "A project portfolio template built with React",
"siteIntroduction": "<b>Welcome!</b> Here might be some text explaining who you are and what sorts of projects you are showcasing in this porfolio. Lorem ipsum dolor sit amet, lorem ipsum.",
"footerText": "Gary E. Smith.",
"showSourceCodeLink": true
}
The configuration object must specify:
- A
siteNamestring containing the name of the portfolio site. - A
siteDescriptionstring containing a short descrption of the site. - An optional
siteIntroductionstring of HTML to display above the main project index. - An
footerTextstring specifying text to display after the copyright year in the footer. - A
showSourceCodeLinkboolean value specifying whether to display a link to this GitHub repo in the footer.
The file /data/categories.json defines an array of project category objects, with each category containing values defined as in the following example:
[
{
"id": "1",
"name": "Web Development",
"tag": "web-dev"
}
]
There may be any number of categories defined in the array.
For each category you must specify:
- A unique
idstring. - A descriptive
namestring for the category, which will appear in the navbar and subheadings. - A URL-friendly
tagthat contains only lowercase alphanumeric characters and hyphens, which will appear in the URL hash.
The file /data/projects.json defines an array of project details, with each project containing values defined as in the following example:
[
{
"id": "1",
"title": "My Project",
"tag": "my-project",
"category": "1",
"image": "project-image.png",
"description": "This is the description of a project.",
"body": "<p>Lorem ipsum dolor sit amet.</p>"
}
]
There may be any number of projects defined in the array.
For each project you must specify:
- A unique
idstring. - A descriptive
titlestring. - A URL-friendly
tagthat contains only lowercase alphanumeric characters and hyphens. - A
categorythat matches oneidfield in the/data/categories.jsonfile, as described above. - An
imagestring that matches the name of an image file in the/src/imagesfolder. - A
descriptionstring that provides a short summary of the project. - A
navBackgroundColorstring that specifies the background color to use for navbar elements. This value should be a valid color definition in CSS, ex.#4444aaorblue. - A
bodystring containing HTML that describes the project in detail.
The site is implemented as a standard ReactJS application with nine functional components. The App component files are located in the document root; all the other component files are located within the /components subfolder.
This component is at the top-level of the component hierarchy and is the parent of all other components in this app. It handles the following tasks:
-
On initial mounting, this component uses the Effect Hook to read static data from the
config.json,projects.jsonandcategories.jsondata files discussed in the Config data section, above. -
Using the BrowserRouter library, this component defines two routes to output different content for the site index view (via the
/route) and the project detail view (via the/project/:tagroute). -
Depending on the route, this component returns JSX to initiate rendering of all children elements, including the header, navbar, body content, and footer.
Returns JSX to display the footer, populating it with the current year, as well at the footerText value defined in config.json.
If the showSourceCodeLink in config.json is set to true, an HTML link to this repository is also included.
Returns JSX to display the header, populating it with the siteName and siteDescription values defined in config.json.
Returns JSX to display the site navigation bar, populating it with category names defined in categories.json.
Note that this component uses SASS to easily allow the colors of the navbar to be customized (see section 6.0 above for details.)
The navigation links render as URL hashes, and the component automatically scrolls the browser window to the selected subheading.
Returns JSX to display any number of related project summary boxes beneath a category subheading.
The parent App component maps through the categories and outputs instances of the ProjectCategory component by passing the category's id, tag and name, along with the projects and categories defined in the configuration files.
The ProjectCategory component outputs the correct category heading, and then loops to return instances of the Project component for all projects in that category.
Returns JSX to display an summary box for a single project. The summary box outputs the project's image, title and description and links to the ProjectDetails component.
Returns JSX to display the full details of a single project, as defined in the projects.json configuration file. The BrowserRouter route defined in the App component displays this component under its own URL path, uniquely identifying it by the project's tag property. The component then outputs the image, title, and body of the project.
Returns JSX to display an Auth0 Log in button, and a Cancel button. The Log in button initiates a call to Auth0 to request secure login. The Cancel button redirect the user to the front page of the application.
When the current user is already logged in, this component does not render any JSX.
Returns JSX to display placeholder text to indicate the the current user is successfully logged in with Auth0. This component will be expanded in the future to provide functionality related to creating, editing and deleting Portfolio content.
When the Logout button is clicked, a call to Auth0 is initiated to end the current session, and the user is redirected to the front page of the application.
When the current user is not logged in, this component does not render any JSX.
