Skip to content

Commit

Permalink
feat: define rings and quadrants via config file
Browse files Browse the repository at this point in the history
closes #96
  • Loading branch information
d-koppenhagen authored and bastianccm committed Nov 11, 2021
1 parent 8d28e4c commit 70ea8d5
Show file tree
Hide file tree
Showing 21 changed files with 1,092 additions and 953 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,22 @@ To change the logo, create a public folder in your application and put your `log

For reference have a look at [public/logo.svg](./public/logo.svg).

### Change the rings and quadrants config
To change the default rings and quadrants of the radar, you can place a custom `config.json` file within the `public` folder.
The content should look as follows:

```json
{
"quadrants": {
"languages-and-frameworks": "Languages & Frameworks",
"methods-and-patterns": "Methods & Patterns",
"platforms-and-aoe-services": "Platforms & Operations",
"tools": "Tools"
},
"rings":["all", "adopt", "trial", "assess", "hold"]
}
```

### Change the index.html
To change the index.html, create a public folder in your application and put your `index.html` in it.

Expand Down
9 changes: 9 additions & 0 deletions config_example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"quadrants": {
"languages-and-frameworks": "Languages & Frameworks",
"methods-and-patterns": "Methods & Patterns",
"platforms-and-aoe-services": "Platforms & Operations",
"tools": "Tools"
},
"rings":["all", "adopt", "trial", "assess", "hold"]
}
7 changes: 4 additions & 3 deletions dist_scripts/scripts/createStaticFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
Object.defineProperty(exports, "__esModule", { value: true });
var fs_1 = require("fs");
var radar_1 = require("./generateJson/radar");
var config_1 = require("../src/config");
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = "production";
process.env.NODE_ENV = "production";
Expand All @@ -50,7 +49,7 @@ process.on("unhandledRejection", function (err) {
throw err;
});
(function () { return __awaiter(void 0, void 0, void 0, function () {
var radar, e_1;
var radar, rawConf, config, e_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
Expand All @@ -61,7 +60,9 @@ process.on("unhandledRejection", function (err) {
radar = _a.sent();
fs_1.copyFileSync("build/index.html", "build/overview.html");
fs_1.copyFileSync("build/index.html", "build/help-and-about-tech-radar.html");
config_1.quadrants.forEach(function (quadrant) {
rawConf = fs_1.readFileSync("build/config.json", "utf-8");
config = JSON.parse(rawConf);
Object.keys(config.quadrants).forEach(function (quadrant) {
var destFolder = "build/" + quadrant;
fs_1.copyFileSync("build/index.html", destFolder + ".html");
if (!fs_1.existsSync(destFolder)) {
Expand Down
42 changes: 24 additions & 18 deletions dist_scripts/scripts/generateJson/radar.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
exports.createRadar = void 0;
var fs_extra_1 = require("fs-extra");
var fs_1 = require("fs");
var path = __importStar(require("path"));
var front_matter_1 = __importDefault(require("front-matter"));
// @ts-ignore esModuleInterop is activated in tsconfig.scripts.json, but IDE typescript uses default typescript config
var marked_1 = __importDefault(require("marked"));
var highlight_js_1 = __importDefault(require("highlight.js"));
var file_1 = require("./file");
var config_1 = require("../../src/config");
var paths_1 = require("../paths");
marked_1.default.setOptions({
highlight: function (code) { return highlight_js_1.default.highlightAuto(code).value; },
});
Expand All @@ -108,31 +109,36 @@ var createRadar = function () { return __awaiter(void 0, void 0, void 0, functio
}); };
exports.createRadar = createRadar;
var checkAttributes = function (fileName, attributes) {
if (attributes.ring && !config_1.rings.includes(attributes.ring)) {
throw new Error("Error: " + fileName + " has an illegal value for 'ring' - must be one of " + config_1.rings);
var rawConf = fs_1.readFileSync(path.resolve(paths_1.appBuild, 'config.json'), 'utf-8');
var config = JSON.parse(rawConf);
if (attributes.ring && !config.rings.includes(attributes.ring)) {
throw new Error("Error: " + fileName + " has an illegal value for 'ring' - must be one of " + config.rings);
}
if (attributes.quadrant && !config_1.quadrants.includes(attributes.quadrant)) {
throw new Error("Error: " + fileName + " has an illegal value for 'quadrant' - must be one of " + config_1.quadrants);
var quadrants = Object.keys(config.quadrants);
if (attributes.quadrant && !quadrants.includes(attributes.quadrant)) {
throw new Error("Error: " + fileName + " has an illegal value for 'quadrant' - must be one of " + quadrants);
}
return attributes;
};
var createRevisionsFromFiles = function (fileNames) {
var publicUrl = process.env.PUBLIC_URL;
return Promise.all(fileNames.map(function (fileName) {
return new Promise(function (resolve, reject) {
fs_extra_1.readFile(fileName, "utf8", function (err, data) {
if (err) {
reject(err);
}
else {
var fm = front_matter_1.default(data);
// add target attribute to external links
// todo: check path
var html = marked_1.default(fm.body.replace(/\]\(\//g, "](" + publicUrl + "/"));
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 }));
}
});
fs_extra_1.readFile(fileName, "utf8", function (err, data) { return __awaiter(void 0, void 0, void 0, function () {
var fm, html;
return __generator(this, function (_a) {
if (err) {
reject(err);
}
else {
fm = front_matter_1.default(data);
html = marked_1.default(fm.body.replace(/\]\(\//g, "](" + publicUrl + "/"));
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 }));
}
return [2 /*return*/];
});
}); });
});
}));
};
Expand Down
9 changes: 9 additions & 0 deletions public/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"quadrants": {
"languages-and-frameworks": "Languages & Frameworks",
"methods-and-patterns": "Methods & Patterns",
"platforms-and-aoe-services": "Platforms & Operations",
"tools": "Tools"
},
"rings":["all", "adopt", "trial", "assess", "hold"]
}
8 changes: 4 additions & 4 deletions scripts/createStaticFiles.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#!/usr/bin/env node

import { copyFileSync, mkdirSync, existsSync } from "fs";
import { copyFileSync, mkdirSync, existsSync, readFileSync } from "fs";
import { createRadar } from "./generateJson/radar";
import { quadrants } from "../src/config";

// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = "production";
Expand All @@ -22,8 +21,9 @@ process.on("unhandledRejection", (err) => {

copyFileSync("build/index.html", "build/overview.html");
copyFileSync("build/index.html", "build/help-and-about-tech-radar.html");

quadrants.forEach((quadrant) => {
const rawConf = readFileSync("build/config.json", "utf-8");
const config = JSON.parse(rawConf);
Object.keys(config.quadrants).forEach((quadrant) => {
const destFolder = `build/${quadrant}`;
copyFileSync("build/index.html", `${destFolder}.html`);
if (!existsSync(destFolder)) {
Expand Down
13 changes: 9 additions & 4 deletions scripts/generateJson/radar.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { readFile } from "fs-extra";
import { readFileSync } from "fs";
import * as path from "path";
import frontMatter from "front-matter";
// @ts-ignore esModuleInterop is activated in tsconfig.scripts.json, but IDE typescript uses default typescript config
import marked from "marked";
import highlight from "highlight.js";

import { radarPath, getAllMarkdownFiles } from "./file";
import { quadrants, rings } from "../../src/config";
import { Item, Revision, ItemAttributes, Radar } from "../../src/model";
import { appBuild } from "../paths";

type FMAttributes = ItemAttributes;

Expand All @@ -29,12 +30,16 @@ export const createRadar = async (): Promise<Radar> => {
};

const checkAttributes = (fileName: string, attributes: FMAttributes) => {
if (attributes.ring && !rings.includes(attributes.ring)) {
const rawConf = readFileSync(path.resolve(appBuild, 'config.json'), 'utf-8');
const config = JSON.parse(rawConf);

if (attributes.ring && !config.rings.includes(attributes.ring)) {
throw new Error(
`Error: ${fileName} has an illegal value for 'ring' - must be one of ${rings}`
`Error: ${fileName} has an illegal value for 'ring' - must be one of ${config.rings}`
);
}

const quadrants = Object.keys(config.quadrants);
if (attributes.quadrant && !quadrants.includes(attributes.quadrant)) {
throw new Error(
`Error: ${fileName} has an illegal value for 'quadrant' - must be one of ${quadrants}`
Expand All @@ -50,7 +55,7 @@ const createRevisionsFromFiles = (fileNames: string[]) => {
fileNames.map(
(fileName) =>
new Promise<Revision>((resolve, reject) => {
readFile(fileName, "utf8", (err, data) => {
readFile(fileName, "utf8", async (err, data) => {
if (err) {
reject(err);
} else {
Expand Down
11 changes: 9 additions & 2 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from "react-router-dom";
import { Item } from "../model";
import { Messages, MessagesProvider } from "../context/MessagesContext";
import { ConfigData } from "../config";

interface Params {
page: string;
Expand Down Expand Up @@ -40,9 +41,11 @@ const useQuery = () => new URLSearchParams(useLocation().search);
const RouterWithPageParam = ({
items,
releases,
config,
}: {
items: Item[];
releases: string[];
config: ConfigData;
}) => {
const { page } = useParams<Params>();
const query = useQuery();
Expand All @@ -53,6 +56,7 @@ const RouterWithPageParam = ({
search={query.get("search") || ""}
items={items}
releases={releases}
config={config}
/>
);
};
Expand All @@ -79,8 +83,11 @@ export default function App() {
const messages = useFetch<Messages>(
`${process.env.PUBLIC_URL}/messages.json`
);
const config = useFetch<ConfigData>(
`${process.env.PUBLIC_URL}/config.json`
);

if (data) {
if (data && config) {
const { items, releases } = data;
return (
<MessagesProvider messages={messages}>
Expand All @@ -93,7 +100,7 @@ export default function App() {
<HeaderWithPageParam />
</div>
<div className={classNames("page__content")}>
<RouterWithPageParam items={items} releases={releases} />
<RouterWithPageParam config={config} items={items} releases={releases} />
</div>
<div className="page__footer">
<FooterWithPageParam items={items} />
Expand Down
3 changes: 1 addition & 2 deletions src/components/Badge/Badge.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React, { MouseEventHandler } from "react";
import classNames from "classnames";
import "./badge.scss";
import { Ring } from "../../config";
type BadgeProps = {
onClick?: MouseEventHandler;
big?: boolean;
type: "big" | "all" | "empty" | Ring;
type: "big" | "all" | "empty" | string;
};

export default function Badge({
Expand Down
7 changes: 4 additions & 3 deletions src/components/PageIndex/PageIndex.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import React from "react";
import { formatRelease } from "../../date";
import { featuredOnly, Item } from "../../model";
import HeroHeadline from "../HeroHeadline/HeroHeadline";
import QuadrantGrid from "../QuadrantGrid/QuadrantGrid";
import Fadeable from "../Fadeable/Fadeable";
import SetTitle from "../SetTitle";
import { radarName, radarNameShort } from "../../config";
import { ConfigData, radarName, radarNameShort } from "../../config";
import { MomentInput } from "moment";
import { useMessages } from "../../context/MessagesContext";

type PageIndexProps = {
leaving: boolean;
onLeave: () => void;
items: Item[];
config: ConfigData;
releases: MomentInput[];
};

export default function PageIndex({
leaving,
onLeave,
items,
config,
releases,
}: PageIndexProps) {
const { pageIndex } = useMessages();
Expand All @@ -35,7 +36,7 @@ export default function PageIndex({
{radarName}
</HeroHeadline>
</div>
<QuadrantGrid items={featuredOnly(items)} />
<QuadrantGrid items={featuredOnly(items)} config={config} />
<div className="publish-date">
{publishedLabel} {formatRelease(newestRelease)}
</div>
Expand Down
7 changes: 4 additions & 3 deletions src/components/PageItem/PageItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import SetTitle from "../SetTitle";
import ItemRevisions from "../ItemRevisions/ItemRevisions";
import { useAnimations } from "./useAnimations";
import "./item-page.scss";
import { translate } from "../../config";
import { ConfigData, translate } from "../../config";
import {
groupByQuadrants,
Item,
Expand All @@ -29,11 +29,12 @@ const getItemsInRing = (pageName: string, items: Item[]) => {
type Props = {
pageName: string;
items: Item[];
config: ConfigData;
leaving: boolean;
onLeave: () => void;
};

const PageItem: React.FC<Props> = ({ pageName, items, leaving, onLeave }) => {
const PageItem: React.FC<Props> = ({ pageName, items, config, leaving, onLeave }) => {
const { pageItem } = useMessages();
const quadrantOverview = pageItem?.quadrantOverview || 'Quadrant Overview';

Expand All @@ -57,7 +58,7 @@ const PageItem: React.FC<Props> = ({ pageName, items, leaving, onLeave }) => {
className="item-page__header"
style={getAnimationState("navHeader")}
>
<h3 className="headline">{translate(item.quadrant)}</h3>
<h3 className="headline">{translate(config, item.quadrant)}</h3>
</div>
<ItemList
items={itemsInRing}
Expand Down
8 changes: 5 additions & 3 deletions src/components/PageItemMobile/PageItemMobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@ import Fadeable from "../Fadeable/Fadeable";
import SetTitle from "../SetTitle";
import ItemRevisions from "../ItemRevisions/ItemRevisions";

import { translate } from "../../config";
import { ConfigData, translate } from "../../config";
import { groupByQuadrants, Item } from "../../model";

type PageItemMobileProps = {
pageName: string;
items: Item[];
config: ConfigData;
leaving: boolean;
onLeave: () => void;
};

export default function PageItemMobile({
pageName,
items,
config,
leaving,
onLeave,
}: PageItemMobileProps) {
Expand Down Expand Up @@ -47,7 +49,7 @@ export default function PageItemMobile({
<div className="mobile-item-page__header">
<div className="split">
<div className="split__left">
<h3 className="headline">{translate(item.quadrant)}</h3>
<h3 className="headline">{translate(config, item.quadrant)}</h3>
<h1 className="hero-headline hero-headline--inverse">
{item.title}
</h1>
Expand All @@ -73,7 +75,7 @@ export default function PageItemMobile({
<ItemList items={itemsInRing} activeItem={item}>
<div className="split">
<div className="split__left">
<h3 className="headline">{translate(item.quadrant)}</h3>
<h3 className="headline">{translate(config, item.quadrant)}</h3>
</div>
<div className="split__right">
<Link className="icon-link" pageName={item.quadrant}>
Expand Down
Loading

0 comments on commit 70ea8d5

Please sign in to comment.