feat(react): update to react 18

This commit is contained in:
Bastian Ike
2022-05-13 12:49:09 +02:00
committed by Bastian
parent f4c5c9edec
commit f28aad8bcb
35 changed files with 27496 additions and 16761 deletions

2
.env
View File

@@ -1,2 +0,0 @@
REACT_APP_RADAR_NAME=AOE Technology Radar
PUBLIC_URL=/build

3
.eslintrc Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": ["react-app", "react-app/jest"]
}

19
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Test AOE Technology Radar
on:
pull_request:
push:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/setup-node@v3
with:
node-version: 'lts/*'
- run: yarn
- run: yarn lint
- run: yarn build
- run: git diff --quiet || (echo 'workspace is dirty after rebuilding' ; git diff ; exit 1)

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@
/node_modules /node_modules
/.pnp /.pnp
.pnp.js .pnp.js
/.yarn
# testing # testing
/coverage /coverage

View File

@@ -6,7 +6,7 @@ The repository is now found here: https://github.com/AOEpeople/techradar
The AOE Tech radar is deployed here: https://www.aoe.com/techradar/index.html The AOE Tech radar is deployed here: https://www.aoe.com/techradar/index.html
## Usage for your own radar? ## Create your own radar
The generator is free to use under Open Source License - in fact there are already some other Radars published based on our Radar and there are also Contributions back. The generator is free to use under Open Source License - in fact there are already some other Radars published based on our Radar and there are also Contributions back.
However, please be aware: However, please be aware:
@@ -14,44 +14,44 @@ However, please be aware:
- It would be nice to mention in radar that the generator is based on this repository. - It would be nice to mention in radar that the generator is based on this repository.
- Also, when you want to reuse the CSS and Styling: Change the font (it is a licensed font) and the colors (It using AOE CI) - Also, when you want to reuse the CSS and Styling: Change the font (it is a licensed font) and the colors (It using AOE CI)
## Use and build the radar ### Use and build the radar
> Set the environment variable `PUBLIC_URL` properly. For more information see [Host the application under a sub path](#host-the-application-under-a-sub-path) Create a new yarn3 project and add the tech radar as a dependency
Add the tech radar as a dependency
``` ```
yarn add aoe_technology_radar npm i aoe_technology_radar
``` ```
Build the radar Build the radar
``` ```
yarn aoe_technology_radar-buildRadar npx aoe_technology_radar-buildRadar
``` ```
Generate json file based on md files Generate the `rd.json` file containing the radar data
``` ```
yarn aoe_technology_radar-generateJson npx aoe_technology_radar-generateJson
``` ```
Serve Serve
``` ```
cd build
python3 -m http.server 8080 python3 -m http.server 8080
``` ```
Then open here: http://localhost:8080/build Then open here: http://localhost:8080/
### Run a prepared static version ### Run a prepared static version
To have a better SEO ranking, you can generate a html file for every page.
To have a better SEO ranking or deploy to S3, you can generate a html file for every page.
Requirements Requirements
* Build the radar * Build the radar
* Generate the json file * Generate the `rd.json` file
``` ```
yarn aoe_technology_radar-createStaticFiles npx aoe_technology_radar-createStaticFiles
``` ```
## Usage ## Authoring Techradar contents
For a new Technology Radar release, create a folder of the release date (YYYY-MM-DD) under `/radar`. For a new Technology Radar release, create a folder of the release date (YYYY-MM-DD) under `./radar`.
In each release folder create a folder for every quadrant and place the items there. In each release folder create a folder for every quadrant and place the items there.
@@ -100,19 +100,19 @@ You can customize the following parts of the tech radar.
Set the environment variable `REACT_APP_RADAR_NAME`. The default is "AOE Technology Radar". Set the environment variable `REACT_APP_RADAR_NAME`. The default is "AOE Technology Radar".
### Host the application under a sub path ### Host the application under a sub path
To host the application under a sub path, set the environment variable `PUBLIC_URL`, e.g. "/techradar". The default is "/build". To host the application under a sub path, set the environment variable `PUBLIC_URL`, e.g. "/techradar". The default is "/".
### Change the favicon ### Change the favicon
To change the favicon, create a public folder in your application and put your `favicon.ico` in it. To change the favicon, create a folder named `public` and put your `favicon.ico` in it.
### Change the logo ### Change the logo
To change the logo, create a public folder in your application and put your `logo.svg` in it. To change the logo, create a folder named `public` and put your `logo.svg` in it.
For reference have a look at [public/logo.svg](./public/logo.svg). For reference have a look at [public/logo.svg](./public/logo.svg).
### Change the date format ### Change the date format
By default the Date format used in the app is `"MMMM YYYY"`. By default the Date format used in the app is `"MMMM YYYY"`.
You can change this by editing the config file as shown below. You can change this by editing the `config.js` file as shown below.
Please be sure you are entering a valid [moment.js format string](https://momentjs.com/docs/#/displaying/format). Please be sure you are entering a valid [moment.js format string](https://momentjs.com/docs/#/displaying/format).
```json ```json
@@ -125,7 +125,6 @@ Please be sure you are entering a valid [moment.js format string](https://moment
For reference have a look at [public/logo.svg](./public/logo.svg). For reference have a look at [public/logo.svg](./public/logo.svg).
### Edit from published radar ### Edit from published radar
You can activate the `editLink` feature which will display a small edit button next to a technology which let's you jump directly to a gitlab / github / etc. edit page: You can activate the `editLink` feature which will display a small edit button next to a technology which let's you jump directly to a gitlab / github / etc. edit page:
```json ```json
@@ -287,19 +286,14 @@ To add a help page, create a public folder in your application and put a `messag
> For more information see the source code of the [Messages Context](./src/context/MessagesContext/index.tsx). > For more information see the source code of the [Messages Context](./src/context/MessagesContext/index.tsx).
## Development ## Development
For local development you need a `rd.json` in the public folder. You can use [rd_example.json](./rd_example.json). Then simply start the dev server:
For several customizations you need a `messages.json` in the public folder. You can use [messages_example.json](./messages_example.json).
Then simply start the dev server
``` ```
yarn start npm run start
``` ```
### Change scripts ### Change scripts
If you change one of the scripts in the scripts' folder, you have to compile them to JavaScript. If you change one of the scripts in the scripts' folder, you have to compile them to JavaScript.
Therefore, run `yarn build:scripts` and commit the results in dist_scripts. Therefore, run `npm run build:scripts` and commit the results in dist_scripts.
To make it more robust the script will be executed on commit. To make it more robust the script will be executed on commit.

13
dist_scripts/scripts/buildRadar.js Normal file → Executable file
View File

@@ -2,7 +2,11 @@
"use strict"; "use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) { }) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
o[k2] = m[k]; o[k2] = m[k];
@@ -36,7 +40,7 @@ fs.removeSync(paths.templateNodeModules);
fs.ensureSymlinkSync(paths.appNodeModules, paths.templateNodeModules); fs.ensureSymlinkSync(paths.appNodeModules, paths.templateNodeModules);
var runCommand = function (command) { var runCommand = function (command) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var executedCommand = child_process_1.spawn(command, { var executedCommand = (0, child_process_1.spawn)(command, {
stdio: "inherit", stdio: "inherit",
shell: true, shell: true,
}); });
@@ -60,10 +64,11 @@ var buildTemplate = function () {
var packageManager = fs.existsSync(paths.appYarnLock) ? "yarn" : "npm"; var packageManager = fs.existsSync(paths.appYarnLock) ? "yarn" : "npm";
fs.emptyDirSync(paths.templateBuild); fs.emptyDirSync(paths.templateBuild);
process.chdir(paths.template); process.chdir(paths.template);
return runCommand(packageManager + " run build"); return runCommand("".concat(packageManager, " run build"));
}; };
buildTemplate().then(function () { buildTemplate().then(function () {
fs.copySync(paths.templateBuild, paths.appBuild); fs.copySync(paths.templateBuild, paths.appBuild);
fs.ensureDirSync(paths.appPublic);
fs.copySync(paths.appPublic, paths.appBuild); fs.copySync(paths.appPublic, paths.appBuild);
console.log(paths.appBuild + " was created and can be deployed."); console.log("".concat(paths.appBuild, " was created and can be deployed."));
}); });

18
dist_scripts/scripts/createStaticFiles.js Normal file → Executable file
View File

@@ -55,22 +55,22 @@ process.on("unhandledRejection", function (err) {
case 0: case 0:
_a.trys.push([0, 2, , 3]); _a.trys.push([0, 2, , 3]);
console.log("starting static"); console.log("starting static");
return [4 /*yield*/, radar_1.createRadar()]; return [4 /*yield*/, (0, radar_1.createRadar)()];
case 1: case 1:
radar = _a.sent(); radar = _a.sent();
fs_1.copyFileSync("build/index.html", "build/overview.html"); (0, fs_1.copyFileSync)("build/index.html", "build/overview.html");
fs_1.copyFileSync("build/index.html", "build/help-and-about-tech-radar.html"); (0, fs_1.copyFileSync)("build/index.html", "build/help-and-about-tech-radar.html");
rawConf = fs_1.readFileSync("build/config.json", "utf-8"); rawConf = (0, fs_1.readFileSync)("build/config.json", "utf-8");
config = JSON.parse(rawConf); config = JSON.parse(rawConf);
Object.keys(config.quadrants).forEach(function (quadrant) { Object.keys(config.quadrants).forEach(function (quadrant) {
var destFolder = "build/" + quadrant; var destFolder = "build/".concat(quadrant);
fs_1.copyFileSync("build/index.html", destFolder + ".html"); (0, fs_1.copyFileSync)("build/index.html", "".concat(destFolder, ".html"));
if (!fs_1.existsSync(destFolder)) { if (!(0, fs_1.existsSync)(destFolder)) {
fs_1.mkdirSync(destFolder); (0, fs_1.mkdirSync)(destFolder);
} }
}); });
radar.items.forEach(function (item) { radar.items.forEach(function (item) {
fs_1.copyFileSync("build/index.html", "build/" + item.quadrant + "/" + item.name + ".html"); (0, fs_1.copyFileSync)("build/index.html", "build/".concat(item.quadrant, "/").concat(item.name, ".html"));
}); });
console.log("created static files."); console.log("created static files.");
return [3 /*break*/, 3]; return [3 /*break*/, 3];

8
dist_scripts/scripts/generateJson.js Normal file → Executable file
View File

@@ -2,7 +2,11 @@
"use strict"; "use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) { }) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
o[k2] = m[k]; o[k2] = m[k];
@@ -96,7 +100,7 @@ var generateJson = function () { return __awaiter(void 0, void 0, void 0, functi
}); }; }); };
generateJson() generateJson()
.then(function () { .then(function () {
console.log(paths.appRdJson + " created."); console.log("".concat(paths.appRdJson, " created."));
}) })
.catch(function (err) { .catch(function (err) {
if (err && err.message) { if (err && err.message) {

View File

@@ -1,7 +1,11 @@
"use strict"; "use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) { }) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
o[k2] = m[k]; o[k2] = m[k];
@@ -18,10 +22,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
__setModuleDefault(result, mod); __setModuleDefault(result, mod);
return result; return result;
}; };
var __spreadArray = (this && this.__spreadArray) || function (to, from) { var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
to[j] = from[i]; if (ar || !(i in from)) {
return to; if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.save = exports.getAllMarkdownFiles = exports.buildPath = exports.jsPath = exports.faviconPath = exports.stylesPath = exports.radarPath = exports.relativePath = void 0; exports.save = exports.getAllMarkdownFiles = exports.buildPath = exports.jsPath = exports.faviconPath = exports.stylesPath = exports.radarPath = exports.relativePath = void 0;
@@ -41,7 +49,7 @@ var radarPath = function () {
for (var _i = 0; _i < arguments.length; _i++) { for (var _i = 0; _i < arguments.length; _i++) {
pathInSrc[_i] = arguments[_i]; pathInSrc[_i] = arguments[_i];
} }
return exports.relativePath.apply(void 0, __spreadArray(["radar"], pathInSrc)); return exports.relativePath.apply(void 0, __spreadArray(["radar"], pathInSrc, false));
}; };
exports.radarPath = radarPath; exports.radarPath = radarPath;
var stylesPath = function () { var stylesPath = function () {
@@ -49,7 +57,7 @@ var stylesPath = function () {
for (var _i = 0; _i < arguments.length; _i++) { for (var _i = 0; _i < arguments.length; _i++) {
pathInSrc[_i] = arguments[_i]; pathInSrc[_i] = arguments[_i];
} }
return exports.relativePath.apply(void 0, __spreadArray(["styles"], pathInSrc)); return exports.relativePath.apply(void 0, __spreadArray(["styles"], pathInSrc, false));
}; };
exports.stylesPath = stylesPath; exports.stylesPath = stylesPath;
var faviconPath = function () { var faviconPath = function () {
@@ -57,7 +65,7 @@ var faviconPath = function () {
for (var _i = 0; _i < arguments.length; _i++) { for (var _i = 0; _i < arguments.length; _i++) {
pathInSrc[_i] = arguments[_i]; pathInSrc[_i] = arguments[_i];
} }
return exports.relativePath.apply(void 0, __spreadArray(["assets/favicon.ico"], pathInSrc)); return exports.relativePath.apply(void 0, __spreadArray(["assets/favicon.ico"], pathInSrc, false));
}; };
exports.faviconPath = faviconPath; exports.faviconPath = faviconPath;
var jsPath = function () { var jsPath = function () {
@@ -65,7 +73,7 @@ var jsPath = function () {
for (var _i = 0; _i < arguments.length; _i++) { for (var _i = 0; _i < arguments.length; _i++) {
pathInSrc[_i] = arguments[_i]; pathInSrc[_i] = arguments[_i];
} }
return exports.relativePath.apply(void 0, __spreadArray(["js"], pathInSrc)); return exports.relativePath.apply(void 0, __spreadArray(["js"], pathInSrc, false));
}; };
exports.jsPath = jsPath; exports.jsPath = jsPath;
var buildPath = function () { var buildPath = function () {
@@ -73,7 +81,7 @@ var buildPath = function () {
for (var _i = 0; _i < arguments.length; _i++) { for (var _i = 0; _i < arguments.length; _i++) {
pathInDist[_i] = arguments[_i]; pathInDist[_i] = arguments[_i];
} }
return exports.relativePath.apply(void 0, __spreadArray(["build"], pathInDist)); return exports.relativePath.apply(void 0, __spreadArray(["build"], pathInDist, false));
}; };
exports.buildPath = buildPath; exports.buildPath = buildPath;
var getAllMarkdownFiles = function (folder) { var getAllMarkdownFiles = function (folder) {
@@ -82,7 +90,7 @@ var getAllMarkdownFiles = function (folder) {
exports.getAllMarkdownFiles = getAllMarkdownFiles; exports.getAllMarkdownFiles = getAllMarkdownFiles;
var getAllFiles = function (folder, predicate) { var getAllFiles = function (folder, predicate) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var walker = walk_1.walk(folder, { followLinks: false }); var walker = (0, walk_1.walk)(folder, { followLinks: false });
var files = []; var files = [];
walker.on("file", function (root, fileStat, next) { walker.on("file", function (root, fileStat, next) {
if (predicate(fileStat.name)) { if (predicate(fileStat.name)) {
@@ -106,6 +114,6 @@ var getAllFiles = function (folder, predicate) {
}; };
var isMarkdownFile = function (name) { return name.match(/\.md$/) !== null; }; var isMarkdownFile = function (name) { return name.match(/\.md$/) !== null; };
var save = function (data, fileName) { var save = function (data, fileName) {
return fs_extra_1.outputFile(exports.buildPath(fileName), data); return (0, fs_extra_1.outputFile)((0, exports.buildPath)(fileName), data);
}; };
exports.save = save; exports.save = save;

View File

@@ -12,7 +12,11 @@ var __assign = (this && this.__assign) || function () {
}; };
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) { }) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
o[k2] = m[k]; o[k2] = m[k];
@@ -65,10 +69,14 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
} }
}; };
var __spreadArray = (this && this.__spreadArray) || function (to, from) { var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
to[j] = from[i]; if (ar || !(i in from)) {
return to; if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}; };
var __importDefault = (this && this.__importDefault) || function (mod) { var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod }; return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -92,7 +100,7 @@ var createRadar = function () { return __awaiter(void 0, void 0, void 0, functio
var fileNames, revisions, allReleases, items, flaggedItems; var fileNames, revisions, allReleases, items, flaggedItems;
return __generator(this, function (_a) { return __generator(this, function (_a) {
switch (_a.label) { switch (_a.label) {
case 0: return [4 /*yield*/, file_1.getAllMarkdownFiles(file_1.radarPath())]; case 0: return [4 /*yield*/, (0, file_1.getAllMarkdownFiles)((0, file_1.radarPath)())];
case 1: case 1:
fileNames = _a.sent(); fileNames = _a.sent();
return [4 /*yield*/, createRevisionsFromFiles(fileNames)]; return [4 /*yield*/, createRevisionsFromFiles(fileNames)];
@@ -101,6 +109,7 @@ var createRadar = function () { return __awaiter(void 0, void 0, void 0, functio
allReleases = getAllReleases(revisions); allReleases = getAllReleases(revisions);
items = createItems(revisions); items = createItems(revisions);
flaggedItems = flagItem(items, allReleases); flaggedItems = flagItem(items, allReleases);
items.forEach(function (item) { return checkAttributes(item.name, item); });
return [2 /*return*/, { return [2 /*return*/, {
items: flaggedItems, items: flaggedItems,
releases: allReleases, releases: allReleases,
@@ -110,14 +119,14 @@ var createRadar = function () { return __awaiter(void 0, void 0, void 0, functio
}); }; }); };
exports.createRadar = createRadar; exports.createRadar = createRadar;
var checkAttributes = function (fileName, attributes) { var checkAttributes = function (fileName, attributes) {
var rawConf = fs_1.readFileSync(path.resolve(paths_1.appBuild, 'config.json'), 'utf-8'); var rawConf = (0, fs_1.readFileSync)(path.resolve(paths_1.appBuild, 'config.json'), 'utf-8');
var config = JSON.parse(rawConf); var config = JSON.parse(rawConf);
if (attributes.ring && !config.rings.includes(attributes.ring)) { if (!config.rings.includes(attributes.ring)) {
throw new Error("Error: " + fileName + " has an illegal value for 'ring' - must be one of " + config.rings); throw new Error("Error: ".concat(fileName, " has an illegal value for 'ring' - must be one of ").concat(config.rings));
} }
var quadrants = Object.keys(config.quadrants); var quadrants = Object.keys(config.quadrants);
if (attributes.quadrant && !quadrants.includes(attributes.quadrant)) { if (!quadrants.includes(attributes.quadrant)) {
throw new Error("Error: " + fileName + " has an illegal value for 'quadrant' - must be one of " + quadrants); throw new Error("Error: ".concat(fileName, " has an illegal value for 'quadrant' - must be one of ").concat(quadrants));
} }
return attributes; return attributes;
}; };
@@ -125,17 +134,17 @@ var createRevisionsFromFiles = function (fileNames) {
var publicUrl = process.env.PUBLIC_URL; var publicUrl = process.env.PUBLIC_URL;
return Promise.all(fileNames.map(function (fileName) { return Promise.all(fileNames.map(function (fileName) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
fs_extra_1.readFile(fileName, "utf8", function (err, data) { return __awaiter(void 0, void 0, void 0, function () { (0, fs_extra_1.readFile)(fileName, "utf8", function (err, data) { return __awaiter(void 0, void 0, void 0, function () {
var fm, html; var fm, html;
return __generator(this, function (_a) { return __generator(this, function (_a) {
if (err) { if (err) {
reject(err); reject(err);
} }
else { else {
fm = front_matter_1.default(data); fm = (0, front_matter_1.default)(data);
html = marked_1.marked(fm.body.replace(/\]\(\//g, "](" + publicUrl + "/")); html = (0, marked_1.marked)(fm.body.replace(/\]\(\//g, "](".concat(publicUrl, "/")));
html = html.replace(/a href="http/g, 'a target="_blank" rel="noopener noreferrer" href="http'); html = html.replace(/a href="http/g, 'a target="_blank" rel="noopener noreferrer" href="http');
resolve(__assign(__assign(__assign({}, itemInfoFromFilename(fileName)), checkAttributes(fileName, fm.attributes)), { fileName: fileName, body: html })); resolve(__assign(__assign(__assign({}, itemInfoFromFilename(fileName)), fm.attributes), { fileName: fileName, body: html }));
} }
return [2 /*return*/]; return [2 /*return*/];
}); });
@@ -155,7 +164,7 @@ var getAllReleases = function (revisions) {
.reduce(function (allReleases, _a) { .reduce(function (allReleases, _a) {
var release = _a.release; var release = _a.release;
if (!allReleases.includes(release)) { if (!allReleases.includes(release)) {
return __spreadArray(__spreadArray([], allReleases), [release]); return __spreadArray(__spreadArray([], allReleases, true), [release], false);
} }
return allReleases; return allReleases;
}, []) }, [])
@@ -190,7 +199,7 @@ var addRevisionToItem = function (item, revision) {
}; } }; }
var newItem = __assign(__assign(__assign({}, item), revision), { body: ignoreEmptyRevisionBody(revision, item) }); var newItem = __assign(__assign(__assign({}, item), revision), { body: ignoreEmptyRevisionBody(revision, item) });
if (revisionCreatesNewHistoryEntry(revision, item)) { if (revisionCreatesNewHistoryEntry(revision, item)) {
newItem = __assign(__assign({}, newItem), { revisions: __spreadArray([revision], newItem.revisions) }); newItem = __assign(__assign({}, newItem), { revisions: __spreadArray([revision], newItem.revisions, true) });
} }
return newItem; return newItem;
}; };

View File

@@ -4,21 +4,21 @@ exports.appNodeModules = exports.appYarnLock = exports.appPublic = exports.appBu
var path_1 = require("path"); var path_1 = require("path");
var fs_1 = require("fs"); var fs_1 = require("fs");
exports.radarJson = "rd.json"; exports.radarJson = "rd.json";
var appDirectory = fs_1.realpathSync(process.cwd()); var appDirectory = (0, fs_1.realpathSync)(process.cwd());
var resolveApp = function (relativePath) { var resolveApp = function (relativePath) {
if (relativePath === void 0) { relativePath = ""; } if (relativePath === void 0) { relativePath = ""; }
return path_1.resolve(appDirectory, relativePath); return (0, path_1.resolve)(appDirectory, relativePath);
}; };
var templateDirectory = fs_1.realpathSync(__dirname); var templateDirectory = (0, fs_1.realpathSync)(__dirname);
var resolveTemplate = function (relativePath) { var resolveTemplate = function (relativePath) {
if (relativePath === void 0) { relativePath = ""; } if (relativePath === void 0) { relativePath = ""; }
return path_1.resolve(templateDirectory, "../..", relativePath); return (0, path_1.resolve)(templateDirectory, "../..", relativePath);
}; };
exports.template = resolveTemplate(); exports.template = resolveTemplate(); // this repository
exports.templateBuild = resolveTemplate("build"); exports.templateBuild = resolveTemplate("build"); // build folder in this repository
exports.templateNodeModules = resolveTemplate("node_modules"); exports.templateNodeModules = resolveTemplate("node_modules"); // node_modules folder in this repository
exports.appRdJson = resolveApp("build/" + exports.radarJson); exports.appRdJson = resolveApp("build/".concat(exports.radarJson)); // build/rd.json in project
exports.appBuild = resolveApp("build"); exports.appBuild = resolveApp("build"); // build folder in project
exports.appPublic = resolveApp("public"); exports.appPublic = resolveApp("public"); // public folder in project
exports.appYarnLock = resolveApp("yarn.lock"); exports.appYarnLock = resolveApp("yarn.lock"); // yarn.lock in project
exports.appNodeModules = resolveApp("node_modules"); exports.appNodeModules = resolveApp("node_modules"); // node_modules folder in project

View File

@@ -10,10 +10,14 @@ var __assign = (this && this.__assign) || function () {
}; };
return __assign.apply(this, arguments); return __assign.apply(this, arguments);
}; };
var __spreadArray = (this && this.__spreadArray) || function (to, from) { var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
to[j] = from[i]; if (ar || !(i in from)) {
return to; if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.getFirstLetter = exports.groupByFirstLetter = exports.groupByQuadrants = exports.nonFeaturedOnly = exports.featuredOnly = exports.FlagType = exports.HomepageOption = void 0; exports.getFirstLetter = exports.groupByFirstLetter = exports.groupByQuadrants = exports.nonFeaturedOnly = exports.featuredOnly = exports.FlagType = exports.HomepageOption = void 0;
@@ -47,7 +51,7 @@ exports.groupByQuadrants = groupByQuadrants;
var groupByFirstLetter = function (items) { var groupByFirstLetter = function (items) {
var index = items.reduce(function (letterIndex, item) { var index = items.reduce(function (letterIndex, item) {
var _a; var _a;
return (__assign(__assign({}, letterIndex), (_a = {}, _a[exports.getFirstLetter(item)] = addItemToList(letterIndex[exports.getFirstLetter(item)], item), _a))); return (__assign(__assign({}, letterIndex), (_a = {}, _a[(0, exports.getFirstLetter)(item)] = addItemToList(letterIndex[(0, exports.getFirstLetter)(item)], item), _a)));
}, {}); }, {});
return Object.keys(index) return Object.keys(index)
.sort() .sort()
@@ -64,11 +68,11 @@ var addItemToQuadrant = function (quadrant, item) {
}; };
var addItemToList = function (list, item) { var addItemToList = function (list, item) {
if (list === void 0) { list = []; } if (list === void 0) { list = []; }
return __spreadArray(__spreadArray([], list), [item]); return __spreadArray(__spreadArray([], list, true), [item], false);
}; };
var addItemToRing = function (ring, item) { var addItemToRing = function (ring, item) {
if (ring === void 0) { ring = []; } if (ring === void 0) { ring = []; }
return __spreadArray(__spreadArray([], ring), [item]); return __spreadArray(__spreadArray([], ring, true), [item], false);
}; };
var getFirstLetter = function (item) { var getFirstLetter = function (item) {
return item.title.substr(0, 1).toUpperCase(); return item.title.substr(0, 1).toUpperCase();

View File

@@ -1,8 +0,0 @@
module.exports = {
"*.{json,md,yml,scss}": ["prettier --write"],
"*.{js,ts,tsx}": [
"eslint",
"prettier --write",
"react-scripts test --watchAll=false --findRelatedTests",
],
};

View File

@@ -1,126 +0,0 @@
{
"footerFootnote": "AOE is a leading global provider of services for digital transformation and digital business models. AOE relies exclusively on established Enterprise Open Source technologies. This leads to innovative solutions, digital products and portals in agile software projects, and helps build long-lasting, strategic partnerships with our customers.",
"legalInformationLabel": "Legal Information",
"legalInformationLink": "https://www.aoe.com/en/imprint.html",
"pageHelp": {
"headlinePrefix": "How to use the",
"paragraphs": [
{
"headline": "Introduction",
"values": [
"Technology is moving fast and new technologies and innovations appear continuously.",
"It's essential for a development and technology company such as AOE to constantly improve and keep track with the latest useful innovations. It is important to openly look for innovations and new technologies and to question established technologies and methods every now and then.",
"But, it is also important to wisely choose which technologies to use in our daily work and in the different projects we are carrying out. As we all know: There is no silver bullet."
]
},
{
"headline": "What is the AOE Technology Radar",
"values": [
"The Tech Radar is an overview of different technologies - from languages, frameworks, tools and patterns to platforms - that we consider \"new or mentionable\". The radar therefore doesn't provide an overview of all established technologies - but it focuses on items that have recently gained in importance or changed."
]
},
{
"headline": "How it is created",
"values": [
"The items in the technology radar are raised by the different teams and therefore a lot of the items are related to the work and challenges the teams face in the different projects. In fact, we don't include anything on the radar, which we haven't already tried ourselves at least once.",
"There have been a lot of valuable discussions in different expert groups about the classification and details of each of technologies and innovations. And the result of all this can be found in the latest technology radar."
]
},
{
"headline": "How should it be used",
"values": [
"The radar acts as an overview of technologies that we think everyone in the teams should currently know about.",
"Its goal is to act as a guide and inspiration for the daily work in the teams. Its purpose is also to provide helpful information and a bird's-eye perspective - so that decisions can be taken with a much deeper understanding of the subject matter. This results in more-informed and better-aligned decisions.",
"We also hope that developers outside of AOE find the information in our technology overview inspirational.",
"We group or categorize the items in 4 quadrants - (sometimes, when it's not 100% clear where a item belongs, we choose the best fit)."
]
}
],
"quadrants": [
{
"description": "We've placed development languages (such as Scala or Golang) here, as well as more low-level development frameworks (such as Play or Symfony), which are useful for implementing custom software of all kinds.",
"name": "Languages and Frameworks"
},
{
"description": "Here we put different software tools - from small helpers to bigger software projects",
"name": "Tools"
},
{
"description": "Patterns are so important, and a lot of them are valid for a long time (compared to some tools or frameworks). So, this is the category where we put information on methods and patterns concerning development, continuous x, testing, organization, architecture, etc.",
"name": "Methods and Patterns"
},
{
"description": "(including AOE internal Services): Here we include infrastructure platforms and services. We also use this category to communicate news about AOE services that we want all AOE teams to be aware of.",
"name": "Platforms and Operations"
}
],
"quadrantsPreDescription": "The quadrants are:",
"rings": [
{
"description": "We can clearly recommend this technology. We have used it for longer period of time in many teams and it has proven to be stable and useful.",
"name": "Adopt"
},
{
"description": "We have used it with success and recommend to have a closer look at the technology in this ring. The goal of items here is to look at them more closely, with the goal to bring them to the adopt level.",
"name": "Trial"
},
{
"description": "We have tried it out and we find it promising. We recommend having a look at these items when you face a specific need for the technology in your project.",
"name": "Assess"
},
{
"description": "This category is a bit special. Unlike the others, we recommend to stop doing or using something. That does not mean that they are bad and it often might be ok to use them in existing projects. But we move things here if we think we shouldn't do them anymore - because we see better options or alternatives now.",
"name": "Hold"
}
],
"ringsPreDescription": "Each of the items is classified in one of these rings:",
"sourcecodeLink": {
"description": "Contributions and source code of the AOE Tech Radar are on github:",
"href": "https://github.com/AOEpeople/aoe_technology_radar",
"name": "AOE Tech Radar on Github"
}
},
"pageIndex": {
"publishedLabel": "Quadrant Overview"
},
"pageItem": {
"quadrantOverview": "Quadrant Overview"
},
"pageOverview": {
"title": "Technologies Overview"
},
"revisionsText": "Revisions:",
"searchLabel": "Search",
"searchPlaceholder": "What are you looking for?",
"socialLinks": [
{
"href": "https://www.facebook.com/aoepeople",
"iconName": "facebook"
},
{
"href": "https://twitter.com/aoepeople",
"iconName": "twitter"
},
{
"href": "https://www.linkedin.com/company/aoe",
"iconName": "linkedIn"
},
{
"href": "https://www.xing.com/company/aoe",
"iconName": "xing"
},
{
"href": "https://www.instagram.com/aoepeople",
"iconName": "instagram"
},
{
"href": "https://www.youtube.com/user/aoepeople",
"iconName": "youtube"
},
{
"href": "https://github.com/aoepeople",
"iconName": "github"
}
],
"socialLinksLabel": "Follow us:"
}

27203
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,6 @@
"name": "aoe_technology_radar", "name": "aoe_technology_radar",
"version": "3.2.1", "version": "3.2.1",
"description": "AOE Technology Radar", "description": "AOE Technology Radar",
"private": false,
"author": "AOE GmbH <contact-de@aoe.com> (http://www.aoe.com)", "author": "AOE GmbH <contact-de@aoe.com> (http://www.aoe.com)",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": { "repository": {
@@ -10,63 +9,59 @@
"url": "https://github.com/AOEpeople/aoe_technology_radar.git" "url": "https://github.com/AOEpeople/aoe_technology_radar.git"
}, },
"bin": { "bin": {
"aoe_technology_radar-generateJson": "dist_scripts/scripts/generateJson.js",
"aoe_technology_radar-buildRadar": "dist_scripts/scripts/buildRadar.js", "aoe_technology_radar-buildRadar": "dist_scripts/scripts/buildRadar.js",
"aoe_technology_radar-createStaticFiles": "dist_scripts/scripts/createStaticFiles.js" "aoe_technology_radar-createStaticFiles": "dist_scripts/scripts/createStaticFiles.js",
"aoe_technology_radar-generateJson": "dist_scripts/scripts/generateJson.js"
}, },
"scripts": { "scripts": {
"prepare": "husky install && yarn build:scripts", "prepare": "husky install && npm run build:scripts",
"start": "react-scripts start", "start": "react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"build:scripts": "tsc --project tsconfig.scripts.json", "build:scripts": "tsc --project tsconfig.scripts.json",
"test": "react-scripts test", "test": "react-scripts test --watchAll=false",
"ts:check": "tsc --noEmit", "ts:check": "tsc --noEmit",
"lint": "yarn ts:check && eslint src/**/*.tsx" "lint": "npm run ts:check && eslint src/**/*.tsx"
}, },
"dependencies": { "dependencies": {
"@apideck/better-ajv-errors": "0.3.3",
"@testing-library/jest-dom": "5.16.4",
"@testing-library/react": "13.2.0",
"@types/d3": "7.1.0", "@types/d3": "7.1.0",
"@types/fs-extra": "9.0.13", "@types/fs-extra": "9.0.13",
"@types/marked": "2.0.3", "@types/jest": "27.5.1",
"@types/node": "16.10.1", "@types/node": "17.0.33",
"@types/react": "17.0.24", "@types/react": "18.0.9",
"@types/react-dom": "17.0.9", "@types/react-dom": "18.0.4",
"@types/react-router-dom": "5.3.0", "@types/sanitize-html": "2.6.2",
"@types/react-tooltip": "4.2.4",
"@types/sanitize-html": "2.5.0",
"@types/walk": "2.3.1", "@types/walk": "2.3.1",
"classnames": "2.3.1", "classnames": "2.3.1",
"d3": "7.2.1", "d3": "7.4.4",
"front-matter": "4.0.2", "front-matter": "4.0.2",
"fs-extra": "10.0.0", "fs-extra": "10.1.0",
"highlight.js": "11.2.0", "highlight.js": "11.5.1",
"marked": "4.0.10", "husky": "8.0.1",
"moment": "2.29.1", "marked": "4.0.15",
"query-string": "7.0.1", "moment": "2.29.3",
"react": "17.0.2", "postcss-normalize": "^10.0.1",
"react-dom": "17.0.2", "query-string": "7.1.1",
"react-icons": "4.2.0", "react": "18.1.0",
"react-router-dom": "5.3.0", "react-dom": "18.1.0",
"react-scripts": "4.0.3", "react-icons": "4.3.1",
"react-router-dom": "6.3.0",
"react-scripts": "5.0.1",
"react-tooltip": "4.2.21", "react-tooltip": "4.2.21",
"sanitize-html": "2.5.2", "sanitize-html": "2.7.0",
"sass": "1.42.1", "sass": "1.51.0",
"typescript": "4.3.5", "typescript": "4.6.4",
"walk": "2.3.15" "walk": "2.3.15",
"yaml": "2.0.1"
}, },
"devDependencies": { "devDependencies": {
"@testing-library/dom": "^8.6.0", "@typescript-eslint/parser": "5.23.0",
"@testing-library/jest-dom": "^5.11.4", "eslint": "8.15.0",
"@testing-library/react": "^11.1.0", "eslint-config-react-app": "7.0.1",
"@testing-library/user-event": "^12.1.10", "eslint-plugin-react": "7.29.4",
"husky": "^7.0.2", "yallist": "4.0.0"
"lint-staged": "^11.1.2",
"prettier": "^2.4.1"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
}, },
"browserslist": { "browserslist": {
"production": [ "production": [

View File

@@ -1 +1,4 @@
module.exports = {}; module.exports = {
importOrderSeparation: true,
importOrderSortSpecifiers: true,
};

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
@@ -14,12 +15,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>%REACT_APP_RADAR_NAME%</title> <title>%REACT_APP_RADAR_NAME%</title>
</head> </head>
<body> <body>
<noscript <noscript>You need to enable JavaScript to view the %REACT_APP_RADAR_NAME%.</noscript>
>You need to enable JavaScript to view the %REACT_APP_RADAR_NAME%.</noscript
>
<div id="root"></div> <div id="root"></div>
</body> </body>
</html> </html>

File diff suppressed because it is too large Load Diff

View File

@@ -52,6 +52,7 @@ const buildTemplate = () => {
buildTemplate().then(() => { buildTemplate().then(() => {
fs.copySync(paths.templateBuild, paths.appBuild); fs.copySync(paths.templateBuild, paths.appBuild);
fs.ensureDirSync(paths.appPublic)
fs.copySync(paths.appPublic, paths.appBuild); fs.copySync(paths.appPublic, paths.appBuild);
console.log(`${paths.appBuild} was created and can be deployed.`); console.log(`${paths.appBuild} was created and can be deployed.`);
}); });

View File

@@ -23,6 +23,8 @@ export const createRadar = async (): Promise<Radar> => {
const items = createItems(revisions); const items = createItems(revisions);
const flaggedItems = flagItem(items, allReleases); const flaggedItems = flagItem(items, allReleases);
items.forEach(item => checkAttributes(item.name, item))
return { return {
items: flaggedItems, items: flaggedItems,
releases: allReleases, releases: allReleases,
@@ -33,14 +35,14 @@ const checkAttributes = (fileName: string, attributes: FMAttributes) => {
const rawConf = readFileSync(path.resolve(appBuild, 'config.json'), 'utf-8'); const rawConf = readFileSync(path.resolve(appBuild, 'config.json'), 'utf-8');
const config = JSON.parse(rawConf); const config = JSON.parse(rawConf);
if (attributes.ring && !config.rings.includes(attributes.ring)) { if (!config.rings.includes(attributes.ring)) {
throw new Error( throw new Error(
`Error: ${fileName} has an illegal value for 'ring' - must be one of ${config.rings}` `Error: ${fileName} has an illegal value for 'ring' - must be one of ${config.rings}`
); );
} }
const quadrants = Object.keys(config.quadrants); const quadrants = Object.keys(config.quadrants);
if (attributes.quadrant && !quadrants.includes(attributes.quadrant)) { if (!quadrants.includes(attributes.quadrant)) {
throw new Error( throw new Error(
`Error: ${fileName} has an illegal value for 'quadrant' - must be one of ${quadrants}` `Error: ${fileName} has an illegal value for 'quadrant' - must be one of ${quadrants}`
); );
@@ -70,7 +72,7 @@ const createRevisionsFromFiles = (fileNames: string[]) => {
resolve({ resolve({
...itemInfoFromFilename(fileName), ...itemInfoFromFilename(fileName),
...checkAttributes(fileName, fm.attributes), ...fm.attributes,
fileName, fileName,
body: html, body: html,
} as Revision); } as Revision);

View File

@@ -8,11 +8,11 @@ const templateDirectory = realpathSync(__dirname);
const resolveTemplate = (relativePath = "") => const resolveTemplate = (relativePath = "") =>
resolve(templateDirectory, "../..", relativePath); resolve(templateDirectory, "../..", relativePath);
export const template = resolveTemplate(); export const template = resolveTemplate(); // this repository
export const templateBuild = resolveTemplate("build"); export const templateBuild = resolveTemplate("build"); // build folder in this repository
export const templateNodeModules = resolveTemplate("node_modules"); export const templateNodeModules = resolveTemplate("node_modules"); // node_modules folder in this repository
export const appRdJson = resolveApp(`build/${radarJson}`); export const appRdJson = resolveApp(`build/${radarJson}`); // build/rd.json in project
export const appBuild = resolveApp("build"); export const appBuild = resolveApp("build"); // build folder in project
export const appPublic = resolveApp("public"); export const appPublic = resolveApp("public"); // public folder in project
export const appYarnLock = resolveApp("yarn.lock"); export const appYarnLock = resolveApp("yarn.lock"); // yarn.lock in project
export const appNodeModules = resolveApp("node_modules"); export const appNodeModules = resolveApp("node_modules"); // node_modules folder in project

View File

@@ -5,9 +5,9 @@ import Footer from "./Footer/Footer";
import Router from "./Router"; import Router from "./Router";
import { import {
BrowserRouter, BrowserRouter,
Switch, Routes,
Route, Route,
Redirect, Navigate,
useParams, useParams,
useLocation, useLocation,
} from "react-router-dom"; } from "react-router-dom";
@@ -15,10 +15,6 @@ import { Item } from "../model";
import { Messages, MessagesProvider } from "../context/MessagesContext"; import { Messages, MessagesProvider } from "../context/MessagesContext";
import { ConfigData } from "../config"; import { ConfigData } from "../config";
interface Params {
page: string;
}
const useFetch = <D extends unknown>(url: string): D | undefined => { const useFetch = <D extends unknown>(url: string): D | undefined => {
const [data, setData] = React.useState<D>(); const [data, setData] = React.useState<D>();
@@ -38,6 +34,10 @@ const useFetch = <D extends unknown>(url: string): D | undefined => {
const useQuery = () => new URLSearchParams(useLocation().search); const useQuery = () => new URLSearchParams(useLocation().search);
const usePage = (params: Record<string, string | undefined>) => {
return (params['*'] || '').replace(".html", "");
};
const RouterWithPageParam = ({ const RouterWithPageParam = ({
items, items,
releases, releases,
@@ -47,12 +47,12 @@ const RouterWithPageParam = ({
releases: string[]; releases: string[];
config: ConfigData; config: ConfigData;
}) => { }) => {
const { page } = useParams<Params>(); const page = usePage(useParams());
const query = useQuery(); const query = useQuery();
return ( return (
<Router <Router
pageName={page} pageName={page || ""}
search={query.get("search") || ""} search={query.get("search") || ""}
items={items} items={items}
releases={releases} releases={releases}
@@ -62,15 +62,15 @@ const RouterWithPageParam = ({
}; };
const HeaderWithPageParam = () => { const HeaderWithPageParam = () => {
const { page } = useParams<Params>(); const page = usePage(useParams());
return <Header pageName={page} />; return <Header pageName={page || ""} />;
}; };
const FooterWithPageParam = ({ items }: { items: Item[] }) => { const FooterWithPageParam = ({ items }: { items: Item[] }) => {
const { page } = useParams<Params>(); const page = usePage(useParams());
return <Footer pageName={page} items={items} />; return <Footer pageName={page || ""} items={items} />;
}; };
interface Data { interface Data {
@@ -83,35 +83,41 @@ export default function App() {
const messages = useFetch<Messages>( const messages = useFetch<Messages>(
`${process.env.PUBLIC_URL}/messages.json` `${process.env.PUBLIC_URL}/messages.json`
); );
const config = useFetch<ConfigData>( const config = useFetch<ConfigData>(`${process.env.PUBLIC_URL}/config.json`);
`${process.env.PUBLIC_URL}/config.json`
);
if (data && config) { if (data && config) {
const { items, releases } = data; const { items, releases } = data;
return ( return (
<MessagesProvider messages={messages}> <MessagesProvider messages={messages}>
<BrowserRouter basename={`${process.env.PUBLIC_URL}`}> <BrowserRouter basename={`${process.env.PUBLIC_URL}`}>
<Switch> <Routes>
<Route path={"/:page(.+).html"}> <Route
path={"/*"}
element={
<div> <div>
<div className="page"> <div className="page">
<div className="page__header"> <div className="page__header">
<HeaderWithPageParam /> <HeaderWithPageParam />
</div> </div>
<div className={classNames("page__content")}> <div className={classNames("page__content")}>
<RouterWithPageParam config={config} items={items} releases={releases} /> <RouterWithPageParam
config={config}
items={items}
releases={releases}
/>
</div> </div>
<div className="page__footer"> <div className="page__footer">
<FooterWithPageParam items={items} /> <FooterWithPageParam items={items} />
</div> </div>
</div> </div>
</div> </div>
</Route> }
<Route path={"/"}> />
<Redirect to={"/index.html"} /> <Route
</Route> path={"/"}
</Switch> element={<Navigate replace to={"/index.html"} />}
/>
</Routes>
</BrowserRouter> </BrowserRouter>
</MessagesProvider> </MessagesProvider>
); );

View File

@@ -5,7 +5,7 @@ import Link from "../Link/Link";
import LogoLink from "../LogoLink/LogoLink"; import LogoLink from "../LogoLink/LogoLink";
import Search from "../Search/Search"; import Search from "../Search/Search";
import { radarNameShort } from "../../config"; import { radarNameShort } from "../../config";
import { useHistory } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import qs from "query-string"; import qs from "query-string";
import { useMessages } from "../../context/MessagesContext"; import { useMessages } from "../../context/MessagesContext";
@@ -13,7 +13,7 @@ export default function Header({ pageName }: { pageName: string }) {
const [searchOpen, setSearchOpen] = useState(false); const [searchOpen, setSearchOpen] = useState(false);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const { searchLabel, pageHelp, pageOverview } = useMessages(); const { searchLabel, pageHelp, pageOverview } = useMessages();
const history = useHistory(); const navigate = useNavigate();
const searchRef = useRef<HTMLInputElement>(null); const searchRef = useRef<HTMLInputElement>(null);
const openSearch = () => { const openSearch = () => {
@@ -29,7 +29,7 @@ export default function Header({ pageName }: { pageName: string }) {
}; };
const handleSearchSubmit = () => { const handleSearchSubmit = () => {
history.push({ navigate({
pathname: "/overview.html", pathname: "/overview.html",
search: qs.stringify({ search: search }), search: qs.stringify({ search: search }),
}); });
@@ -55,20 +55,20 @@ export default function Header({ pageName }: { pageName: string }) {
<div className="nav__item"> <div className="nav__item">
<Link pageName="help-and-about-tech-radar" className="icon-link"> <Link pageName="help-and-about-tech-radar" className="icon-link">
<span className="icon icon--question icon-link__icon" /> <span className="icon icon--question icon-link__icon" />
{pageHelp.headlinePrefix || 'How to use' } {radarNameShort}? {pageHelp.headlinePrefix || "How to use"} {radarNameShort}?
</Link> </Link>
</div> </div>
)} )}
<div className="nav__item"> <div className="nav__item">
<Link pageName="overview" className="icon-link"> <Link pageName="overview" className="icon-link">
<span className="icon icon--overview icon-link__icon" /> <span className="icon icon--overview icon-link__icon" />
{ pageOverview?.title || 'Technologies Overview' } {pageOverview?.title || "Technologies Overview"}
</Link> </Link>
</div> </div>
<div className="nav__item"> <div className="nav__item">
<button className="icon-link" onClick={handleOpenClick}> <button className="icon-link" onClick={handleOpenClick}>
<span className="icon icon--search icon-link__icon" /> <span className="icon icon--search icon-link__icon" />
{ searchLabel || 'Search' } {searchLabel || "Search"}
</button> </button>
<div className={classNames("nav__search", { "is-open": searchOpen })}> <div className={classNames("nav__search", { "is-open": searchOpen })}>
<Search <Search

View File

@@ -6,7 +6,10 @@ interface Props {
secondary?: boolean; secondary?: boolean;
} }
const HeadlineGroup: React.FC<Props> = ({ children, secondary = false }) => ( const HeadlineGroup: React.FC<React.PropsWithChildren<Props>> = ({
children,
secondary = false,
}) => (
<div <div
className={classNames("headline-group", { className={classNames("headline-group", {
"headline-group--secondary": secondary, "headline-group--secondary": secondary,

View File

@@ -5,7 +5,10 @@ interface Props {
alt?: string; alt?: string;
} }
const HeroHeadline: React.FC<Props> = ({ children, alt }) => ( const HeroHeadline: React.FC<React.PropsWithChildren<Props>> = ({
children,
alt,
}) => (
<div className="hero-headline"> <div className="hero-headline">
{children} <span className="hero-headline__alt">{alt}</span> {children} <span className="hero-headline__alt">{alt}</span>
</div> </div>

View File

@@ -8,6 +8,6 @@ describe("Item", () => {
it("Should render the item", () => { it("Should render the item", () => {
render(<Item item={testItem} />, { wrapper: MemoryRouter }); render(<Item item={testItem} />, { wrapper: MemoryRouter });
expect(screen.queryByText(testItem.title)).toBeInTheDocument(); expect(screen.getByText(testItem.title)).toBeInTheDocument();
}); });
}); });

View File

@@ -11,7 +11,7 @@ type ItemListProps = {
itemStyle?: React.CSSProperties[]; itemStyle?: React.CSSProperties[];
}; };
const ItemList: React.FC<ItemListProps> = ({ const ItemList: React.FC<React.PropsWithChildren<ItemListProps>> = ({
children, children,
items, items,
activeItem, activeItem,

View File

@@ -30,7 +30,12 @@ export default function PageQuadrant({
<HeadlineGroup> <HeadlineGroup>
<HeroHeadline>{translate(config, pageName)}</HeroHeadline> <HeroHeadline>{translate(config, pageName)}</HeroHeadline>
</HeadlineGroup> </HeadlineGroup>
<QuadrantSection groups={groups} quadrantName={pageName} config={config} big /> <QuadrantSection
groups={groups}
quadrantName={pageName}
config={config}
big
/>
</Fadeable> </Fadeable>
); );
} }

View File

@@ -1,15 +1,12 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import PageIndex from "./PageIndex/PageIndex"; import PageIndex from "./PageIndex/PageIndex";
import PageOverview from "./PageOverview/PageOverview"; import PageOverview from "./PageOverview/PageOverview";
import PageHelp from "./PageHelp/PageHelp"; import PageHelp from "./PageHelp/PageHelp";
import PageQuadrant from "./PageQuadrant/PageQuadrant"; import PageQuadrant from "./PageQuadrant/PageQuadrant";
import PageItem from "./PageItem/PageItem"; import PageItem from "./PageItem/PageItem";
import PageItemMobile from "./PageItemMobile/PageItemMobile"; import PageItemMobile from "./PageItemMobile/PageItemMobile";
import { import { ConfigData, getItemPageNames, isMobileViewport } from "../config";
ConfigData,
getItemPageNames,
isMobileViewport,
} from "../config";
import { Item } from "../model"; import { Item } from "../model";
type RouterProps = { type RouterProps = {
@@ -30,7 +27,11 @@ enum page {
notFound, notFound,
} }
const getPageByName = (items: Item[], pageName: string, config: ConfigData): page => { const getPageByName = (
items: Item[],
pageName: string,
config: ConfigData
): page => {
if (pageName === "index") { if (pageName === "index") {
return page.index; return page.index;
} }
@@ -55,7 +56,7 @@ export default function Router({
items, items,
releases, releases,
search, search,
config config,
}: RouterProps) { }: RouterProps) {
const [statePageName, setStatePageName] = useState(pageName); const [statePageName, setStatePageName] = useState(pageName);
const [leaving, setLeaving] = useState(false); const [leaving, setLeaving] = useState(false);
@@ -63,7 +64,8 @@ export default function Router({
useEffect(() => { useEffect(() => {
const nowLeaving = const nowLeaving =
getPageByName(items, pageName, config) !== getPageByName(items, statePageName, config); getPageByName(items, pageName, config) !==
getPageByName(items, statePageName, config);
if (nowLeaving) { if (nowLeaving) {
setLeaving(true); setLeaving(true);
setNextPageName(pageName); setNextPageName(pageName);

View File

@@ -15,14 +15,14 @@ interface PageHelp {
paragraphs: Paragraph[]; paragraphs: Paragraph[];
quadrantsPreDescription?: string; quadrantsPreDescription?: string;
quadrants: Quadrant[]; quadrants: Quadrant[];
rings: {name: string, description: string }[]; rings: { name: string; description: string }[];
ringsPreDescription?: string; ringsPreDescription?: string;
sourcecodeLink?: { sourcecodeLink?: {
href: string; href: string;
name: string; name: string;
description: string; description: string;
}; };
headlinePrefix?: string headlinePrefix?: string;
} }
interface PageOverview { interface PageOverview {
@@ -54,10 +54,9 @@ export interface Messages {
const MessagesContext = createContext<Messages | undefined>(undefined); const MessagesContext = createContext<Messages | undefined>(undefined);
export const MessagesProvider: FC<{ messages?: Messages }> = ({ export const MessagesProvider: FC<
messages, React.PropsWithChildren<{ messages?: Messages }>
children, > = ({ messages, children }) => (
}) => (
<MessagesContext.Provider value={messages}> <MessagesContext.Provider value={messages}>
{children} {children}
</MessagesContext.Provider> </MessagesContext.Provider>

View File

@@ -1,11 +1,12 @@
import React from "react"; import React from "react";
import ReactDOM from "react-dom";
import App from "./components/App"; import App from "./components/App";
import { createRoot } from "react-dom/client";
import "./index.scss"; import "./index.scss";
ReactDOM.render( const container = document.getElementById("root")!;
const root = createRoot(container);
root.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode>, </React.StrictMode>
document.getElementById("root")
); );

View File

@@ -16,5 +16,3 @@ describe("sanitize", () => {
expect(res.__html).toEqual("<a href=\"https://example.org\">Example.org</a>"); expect(res.__html).toEqual("<a href=\"https://example.org\">Example.org</a>");
}); });
}); });

12522
yarn.lock

File diff suppressed because it is too large Load Diff