Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • main
1 result

Target

Select target project
  • cpilkington3/react-redux-starter-code
  • jherman5/react-redux-starter-code
  • soft-core/soft-260/react-redux-starter-code
  • jadengoter/react-redux-starter-code
  • aherold5/react-redux-starter-code
  • jackmnolley/react-redux-starter-code
  • musama2/react-redux-starter-code
  • sfarahmand2/homework-3
  • eyehl2/ethan-hw-3
  • gseagren2/algorithm-explorer
  • ihopp2/soft-260-hw-4-fall-2024
11 results
Select Git revision
  • main
1 result
Show changes
Commits on Source (27)
Showing
with 557 additions and 0 deletions
# Disable line-ending conversions for this repository.
* -text
# dependencies
/node_modules
# testing
/coverage
# production
/build
# environments
.env.local
.env.development.local
.env.test.local
.env.production.local
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# misc
*~
.DS_Store
[submodule "stylelint-config"]
path = stylelint-config
url = git@git.unl.edu:soft-core/soft-260/stylelint-config.git
[submodule "eslint-config"]
path = eslint-config
url = git@git.unl.edu:soft-core/soft-260/eslint-config.git
# React Redux Starter Code
A minimal app to be used as starter code for labs and homework in the SOFT 260
course at UNL. The starter code demonstrates basic usage of the React Hooks
API, the Redux Toolkit (RTK), React Router, Jest, and the React Testing Library
(RTL) in the context of a React Redux progressive web app (PWA).
# Quick Start
Recursively clone this repository and `cd` into the root folder:
```
$ git clone --recursive git@git.unl.edu:soft-core/soft-260/react-redux-starter-code.git
$ cd react-redux-starter-code
```
(If you forget `--recursive` when cloning, you can `cd` into your clone and run
`git submodule update --init --recursive` instead.)
Install dependencies:
```
$ npm install
```
(Near the end you may see some warnings because `create-react-app` transitively
depends on some deprecated packages.)
Optionally run the linter and the test suite:
```
$ npm run lint
$ npm run test
```
And then serve the application locally:
```
$ npm start
```
When you are done, press control-c to stop the server.
# Folders and Files
The folders and files in the starter code are briefly described below. React
Redux applications use a model-view-controller (MVC) architecture (see
<https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller>), so in
these descriptions the terms "model", "view", and "controller" refer to those
roles from MVC.
## Submodules
* The Git submodule `stylelint-config` contains the stylelint configuration
for the coding style used in the `minimal-app` project.
* The Git submodule `eslint-config` contains the ESLint configuration for the
coding style used in the `minimal-app` project. Per `create-react-app`
convention, in a development build of the main app, a separate, weaker
coding style also warns at runtime about likely bugs.
## General Configuration
* The file `minimal-app/.gitignore` prevents non-source-code files from being
accidentally committed to the repository.
* The file `minimal-app/package.json` describes the project and its
dependencies. It can be edited to customize the inputs and processes used
in various software lifecyle tasks like linting, testing, or deploying.
* The file `minimal-app/package-lock.json` records the exact set of
dependencies used to satisfy the requirements in `minimal-app/package.json`.
It should not be hand-edited; use commands like `npm install` or `npm
uninstall` from the `minimal-app` directory to make changes.
* The folder `minimal-app/node_modules` contains the dependencies installed by
`npm`.
## Application Infrastructure
* The file `minimal-app/public/manifest.json` provides data needed to run the
web application as a PWA. (See
<https://developer.mozilla.org/en-US/docs/Web/Manifest> for more
information.)
* The file `minimal-app/public/logo.svg` is the image used for the app's icon.
(A maskable icon is recommended; see <https://web.dev/maskable-icon/> for
more information and <https://maskable.app/> to test an image.)
* The file `minimal-app/public/index.html` contains the skeleton document in
which the app's HTML is embedded.
* The file `minimal-app/src/index.js` specifies other wrappers around the
application proper than cannot be given in `index.html`, usually because
they involve React components, not pure HTML. In this case, the wrappers:
* Enable extra checks at development time using React strict mode. (See
<https://reactjs.org/docs/strict-mode.html> for more information.)
* Make the Redux store from `minimal-app/src/app/store.js` available to
the app's React components.
* Enable routing with React Router. (See
<https://reacttraining.com/react-router/web> for more information.)
* Apply the letterboxed portrait layout from `index.css`, which is
described next.
* The file `minimal-app/src/index.css` contains the CSS associated with
`index.html`. In this case the CSS forces a letterboxed portrait layout and
specifies a sans-serif font.
* The file `minimal-app/src/service-worker.js` acts as a proxy server, which
among other things makes it possible to run a PWA while offline. (See
<https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API> for
more information.)
* The file `minimal-app/src/serviceWorkerRegistration.js` registers the
service worker so that the browser knows about it.
## Model and Controller Code
* The file `minimal-app/src/app/store.js` combines the models provided by the
app's features to create a model for the whole app.
* The file `minimal-app/src/features/counter/counterSlice.js` implements a
Redux slice, the model and associated controllers for a particular feature,
which in this case is a simple counter. (See
<https://redux-toolkit.js.org/> for more information.)
## View Code
* The file `minimal-app/src/app.js` implements the React component
representing the entire app. In this case only one route, `/`, is
implemented, but the returned fragment is intentionally written so that it
is easy to add additional routes.
* The file `minimal-app/src/features/counter/counter.js` implements a React
component that counts the number of times a user has tapped a button. This
component uses the slice from `counterSlice.js`.
* The file `minimal-app/src/features/counter/counter.module.css` provides
styles for the React component in `counter.js`.
## Test Infrastructure
* The file `minimal-app/src/setupTests.js` provides setup code that applies to
every test case. In this case that code imports RTL's custom matchers from
testing React components.
* The file `minimal-app/src/testing/mockRedux.js` provides the ability to mock
the part of Redux used by React components under the React Hooks API so that
those view components can be tested independently of model and controller
code.
## Test Code
* The file `minimal-app/app.test.js` demonstrates a snapshot regression test
of the app component with mocked selectors. (See
<https://jestjs.io/docs/en/snapshot-testing> for more information.)
* The file `minimal-app/src/__snapshots__/app.test.js.snap` contains the
oracles for the snapshot tests in `app.test.js`. When the tests are run in
watch mode (using the command `npm test`), these oracles can be updated
interactively if a snapshot test fails due to changed requirements.
* The file `minimal-app/src/features/counter/counter.test.js` demonstrates
non-snapshot tests of the view code in the counter component.
* The file `minimal-app/src/features/counter/counterSlice.test.js`
demonstrates tests of model and controller code.
# Adaptation Checklist
When adapting this code for a new project, make sure to do at least the
following:
* Change the project name, version number, and description in
`minimal-app/package.json`.
* Change the short name, name, description, colors, and other settings in
`minimal-app/public/manifest.json`.
* Change the title, description, and theme color in
`minimal-app/public/index.html`.
* Rename the folder from `minimal-app` to something descriptive and change the
corresponding entries in the outer `package.json`.
* Rerun `npm install` to update `package-lock.json` based on the above changes.
Subproject commit 24df42fb655d234b83c93b0fb24d012e4d9ecb58
This diff is collapsed.
{
"name": "unit-conversion",
"version": "1.1.0",
"description": "An app that takes units and converts them to other units",
"private": true,
"license": "UNLICENSED",
"scripts": {
"postinstall:stylelint-config": "cd stylelint-config && npm install",
"postinstall:eslint-config": "cd eslint-config && npm install",
"postinstall:app": "cd unit-conversion && npm install",
"postinstall": "run-s postinstall:**",
"lint:app": "cd unit-conversion && npm run lint",
"lint": "run-s --continue-on-error lint:**",
"test-once:app": "cd unit-conversion && npm run test-once",
"test-once": "run-s --continue-on-error test-once:**",
"test": "run-s test-once",
"start": "cd unit-conversion && npm run start",
"build:app": "cd unit-conversion && npm run build",
"build": "run-s build:**"
},
"devDependencies": {
"ghooks": "^2.0.4",
"npm-run-all": "^4.1.5"
},
"config": {
"ghooks": {
"pre-commit": "npm run lint"
}
}
}
{
"folders": [
{
"path": "."
}
],
"settings": {
"files.eol": "LF",
"files.exclude": {
"**/node_modules": true
},
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true
}
}
Subproject commit 40b0b09694d1fa983446ff2768211c23f71f0f78
# dependencies
/node_modules
# testing
/coverage
# production
/build
# environments
.env.local
.env.development.local
.env.test.local
.env.production.local
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# misc
*~
.DS_Store
This diff is collapsed.
{
"name": "unit-conversion",
"version": "1.1.0",
"description": "An app that takes units and converts them to other units",
"private": true,
"license": "UNLICENSED",
"scripts": {
"lint:css": "stylelint \"**/*.css\" \"**/*.module.css\" \"!coverage/**\"",
"lint:js": "eslint --max-warnings 0 ./src",
"lint": "run-s --continue-on-error lint:**",
"test-once": "react-scripts test --watchAll=false --coverage",
"test": "react-scripts test --watchAll --coverage",
"start": "react-scripts start",
"build": "react-scripts build",
"eject": "react-scripts eject"
},
"homepage": ".",
"dependencies": {
"@reduxjs/toolkit": "^1.6.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@testing-library/user-event": "^13.1.9",
"classnames": "^2.3.1",
"npm-run-all": "^4.1.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.4",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"workbox-background-sync": "^5.1.3",
"workbox-broadcast-update": "^5.1.3",
"workbox-cacheable-response": "^5.1.3",
"workbox-core": "^5.1.3",
"workbox-expiration": "^5.1.3",
"workbox-google-analytics": "^5.1.3",
"workbox-navigation-preload": "^5.1.3",
"workbox-precaching": "^5.1.3",
"workbox-range-requests": "^5.1.3",
"workbox-routing": "^5.1.3",
"workbox-strategies": "^5.1.3",
"workbox-streams": "^5.1.3"
},
"devDependencies": {
"@unlsoft/eslint-config": "file:../eslint-config",
"@unlsoft/stylelint-config": "file:../stylelint-config",
"eslint-plugin-jest-dom": "^3.9.0",
"stylelint": "^13.13.1"
},
"stylelint": {
"extends": "@unlsoft/stylelint-config"
},
"eslintConfig": {
"extends": [
"react-app",
"@unlsoft/eslint-config/react"
]
},
"jest": {
"clearMocks": true,
"collectCoverageFrom": [
"src/features/**/*.js"
],
"resetMocks": false,
"restoreMocks": false
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<meta
name="description"
content="An app that takes units and converts them to other units"
/>
<meta name="theme-color" content="rgba(208 0 0 / 100%)" />
<link rel="icon" href="u.svg" />
<link rel="apple-touch-icon" href="u.svg" />
<title>Unit Conversion</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
{
"short_name": "Conversion App",
"name": "Unit Conversion",
"description": "An app that takes units and converts them to other units",
"icons": [
{
"src": "u.svg",
"type": "image/svg+xml",
"sizes": "192x192 512x512",
"purpose": "any maskable"
}
],
"start_url": ".",
"display": "standalone",
"orientation": "portrait",
"theme_color": "rgba(208 0 0 / 100%)",
"background_color": "rgba(211 211 211 / 100%)"
}
<svg id="Capa_1" enable-background="new 0 0 512 512" height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><g id="U"><path d="m337.255 281.038c.015 67.588-1.025 87.876-24.229 105.894-22.163 17.227-82.017 19.79-110.537-1.201-27.246-20.039-28.315-48.384-27.847-96.782l.103-288.949h-128.745v268.557c0 93.472 0 181.758 103.638 226.406 47.595 20.546 154.453 24.243 211.816 1.465 104.546-41.528 104.546-129.477 104.546-231.738v-264.69h-128.745z"/></g></svg>
\ No newline at end of file
// import { Route } from 'react-router-dom';
import { EndingNumberOutput } from './features/ending-number-output/endingNumberOutput.js';
import { NumberInputField } from './features/number-input-field/numberInputField.js';
import { UnitSelector } from './features/unit-selector/unitSelector.js';
import { Card } from './features/card/card.js';
/* What is this used for????
<Route exact path={'/'}>
</Route>
*/
export function App() {
return (
<>
<Card title="Input Unit Selector">
<UnitSelector type="Input"/>
</Card>
<Card title="Number Input">
<NumberInputField />
</Card>
<Card title="Output Unit Selector">
<UnitSelector type="Output"/>
</Card>
<Card title="Number Output">
<EndingNumberOutput />
</Card>
</>
);
}
import { configureStore } from '@reduxjs/toolkit';
import unitSelectorSlice from '../features/unit-selector/unitSelectorSlice.js';
import numberInputFieldSlice from '../features/number-input-field/numberInputFieldSlice.js';
export const store = configureStore({
reducer: {
[unitSelectorSlice.name]: unitSelectorSlice.reducer,
[numberInputFieldSlice.name]: numberInputFieldSlice.reducer,
},
});
* {
box-sizing: border-box;
}
.card {
display: flex;
justify-content: space-around;
margin: 7%;
min-height: 20%;
border-radius: 10px;
background-color: rgba(255 255 255 / 40%);
box-shadow:
0 2.8px 2.2px rgba(0 0 0 / 3.4%),
0 6.7px 5.3px rgba(0 0 0 / 4.8%),
0 12.5px 10px rgba(0 0 0 / 6%),
0 22.3px 17.9px rgba(0 0 0 / 7.2%),
0 41.8px 33.4px rgba(0 0 0 / 8.6%),
0 100px 80px rgba(0 0 0 / 12%);
padding-left: 20px;
padding-right: 20px;
}
.cardTitle {
align-self: flex-start;
}
.selectorContainer {
justify-self: center;
align-self: center;
}
.selector {
border-color: rgba(255 255 255 / 0%);
border-radius: 5px;
}
.inputFieldContainer {
justify-self: center;
align-self: center;
}
.inputField {
border-color: rgba(255 255 255 / 0%);
border-radius: 5px;
max-width: 75%;
}
.endingNumberHolder {
justify-self: center;
align-self: center;
}
import PropTypes from 'prop-types';
import './card.css';
export function Card(props) {
Card.propTypes = {
title: PropTypes.string.isRequired,
};
return (
<div className="card">
<h1 className="cardTitle">{props.title}</h1>
{props.children}
</div>
);
}
/* eslint-disable no-magic-numbers */
import { useSelector } from 'react-redux';
import { selectCurrentOutputUnit, selectCurrentInputUnit } from '../unit-selector/unitSelectorSlice';
import { selectNumberOfUnits } from '../number-input-field/numberInputFieldSlice';
export function EndingNumberOutput() {
const conversionTable = {
// To use these conversion multiply your specified unit with
// its number to get meters.
unitsToMeters: {
km: 1000,
m: 1.0,
cm: 1 / 100,
ft: 1 / 3.281,
in: 1 / 39.3701,
},
// Multiply a number of meters by one of these conversions to
// get that number of units.
metersToUnits: {
km: 1 / 1000,
m: 1.0,
cm: 100,
ft: 3.281,
in: 39.3701,
},
};
const currentInputUnit = useSelector(selectCurrentInputUnit);
const currentOutputUnit = useSelector(selectCurrentOutputUnit);
const numberOfUnits = parseFloat(useSelector(selectNumberOfUnits));
const numberOfMeters = numberOfUnits * conversionTable.unitsToMeters[currentInputUnit];
let convertedNumber = numberOfMeters * conversionTable.metersToUnits[currentOutputUnit];
if (isNaN(convertedNumber)) {
convertedNumber = 'Please enter number to convert to ';
}
return (
<div className="endingNumberHolder">
<h1>{convertedNumber} {currentOutputUnit}</h1>
</div>
);
}