Add basic typing to config objects
Also fixed the blips to be clickable
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import React from 'react';
|
||||
import { quadrantsMap, ringsMap, chartConfig, blipFlags } from '../..//config';
|
||||
import {FlagType, Ring} from '../../model';
|
||||
import { quadrantsMap, chartConfig } from '../../config';
|
||||
import Link from '../Link/Link';
|
||||
import { NewBlip, ChangedBlip, DefaultBlip } from './BlipShapes';
|
||||
|
||||
/*
|
||||
@@ -11,7 +13,7 @@ const generateCoordinates = (enrichedBlip, xScale, yScale) => {
|
||||
const pi = Math.PI,
|
||||
ringRadius = chartConfig.ringsAttributes[enrichedBlip.ringPosition - 1].radius,
|
||||
previousRingRadius = enrichedBlip.ringPosition == 1 ? 0 : chartConfig.ringsAttributes[enrichedBlip.ringPosition - 2].radius,
|
||||
ringPadding = 0.6;
|
||||
ringPadding = 0.7;
|
||||
|
||||
// radian between 0 and 90 degrees
|
||||
const randomDegree = ((Math.random() * 90) * pi) / 180;
|
||||
@@ -39,7 +41,7 @@ const distanceBetween = (point1, point2) => {
|
||||
return Math.sqrt((a * a) + (b * b));
|
||||
};
|
||||
|
||||
export default function BlipPoints({blips, xScale, yScale, navigate}) {
|
||||
export default function BlipPoints({blips, xScale, yScale}) {
|
||||
|
||||
const enrichedBlips = blips.reduce((list, blip) => {
|
||||
if (!blip.ring || !blip.quadrant) {
|
||||
@@ -47,8 +49,8 @@ export default function BlipPoints({blips, xScale, yScale, navigate}) {
|
||||
return list;
|
||||
}
|
||||
let enrichedBlip = { ...blip,
|
||||
ringPosition: ringsMap[blip.ring].position,
|
||||
quadrantPosition: quadrantsMap[blip.quadrant].position,
|
||||
ringPosition: Ring[blip.ring],
|
||||
colour: quadrantsMap[blip.quadrant].colour,
|
||||
txtColour: quadrantsMap[blip.quadrant].txtColour
|
||||
};
|
||||
@@ -76,11 +78,6 @@ export default function BlipPoints({blips, xScale, yScale, navigate}) {
|
||||
return list;
|
||||
}, []);
|
||||
|
||||
const handleClick = (pageName, event) => {
|
||||
event.preventDefault();
|
||||
navigate(pageName);
|
||||
}
|
||||
|
||||
const renderBlip = (blip, index) => {
|
||||
const props = {
|
||||
blip,
|
||||
@@ -89,13 +86,12 @@ export default function BlipPoints({blips, xScale, yScale, navigate}) {
|
||||
'data-background-color': blip.colour,
|
||||
'data-text-color': blip.txtColour,
|
||||
'data-tip': blip.title,
|
||||
onClick: handleClick.bind(this, `${blip.quadrant}/${blip.name}`),
|
||||
key: index
|
||||
}
|
||||
switch (blip.flag) {
|
||||
case blipFlags.new.name:
|
||||
case FlagType.new:
|
||||
return <NewBlip {...props} />;
|
||||
case blipFlags.changed.name:
|
||||
case FlagType.changed:
|
||||
return <ChangedBlip {...props} />;
|
||||
default:
|
||||
return <DefaultBlip {...props} />;
|
||||
@@ -105,7 +101,9 @@ export default function BlipPoints({blips, xScale, yScale, navigate}) {
|
||||
return (
|
||||
<g className="blips">
|
||||
{enrichedBlips.map((blip, index) => (
|
||||
renderBlip(blip, index)
|
||||
<Link pageName={`${blip.quadrant}/${blip.name}`} key={index}>
|
||||
{renderBlip(blip, index)}
|
||||
</Link>
|
||||
))}
|
||||
</g>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import * as d3 from "d3";
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import { chartConfig, quadrantsMap, ringsMap } from '../../config';
|
||||
import { Ring } from '../../model';
|
||||
import { chartConfig, quadrantsMap } from '../../config';
|
||||
import { YAxis, XAxis } from './Axes';
|
||||
import QuadrantRings from './QuadrantRings';
|
||||
import BlipPoints from './BlipPoints';
|
||||
@@ -9,8 +10,8 @@ import BlipPoints from './BlipPoints';
|
||||
import './chart.scss';
|
||||
|
||||
const RingLabel = ({ring, xScale, yScale}) => {
|
||||
const ringRadius = chartConfig.ringsAttributes[ring.position - 1].radius,
|
||||
previousRingRadius = ring.position == 1 ? 0 : chartConfig.ringsAttributes[ring.position - 2].radius,
|
||||
const ringRadius = chartConfig.ringsAttributes[ring - 1].radius,
|
||||
previousRingRadius = ring == 1 ? 0 : chartConfig.ringsAttributes[ring - 2].radius,
|
||||
|
||||
// middle point in between two ring arcs
|
||||
distanceFromCentre = previousRingRadius + (ringRadius - previousRingRadius) / 2;
|
||||
@@ -19,11 +20,11 @@ const RingLabel = ({ring, xScale, yScale}) => {
|
||||
<g className="ring-label">
|
||||
{/* Right hand-side label */}
|
||||
<text x={xScale(distanceFromCentre)} y={yScale(0)} textAnchor="middle" dy=".35em">
|
||||
{ring.displayName}
|
||||
{Ring[ring]}
|
||||
</text>
|
||||
{/* Left hand-side label */}
|
||||
<text x={xScale(-distanceFromCentre)} y={yScale(0)} textAnchor="middle" dy=".35em">
|
||||
{ring.displayName}
|
||||
{Ring[ring]}
|
||||
</text>
|
||||
</g>
|
||||
);
|
||||
@@ -51,8 +52,8 @@ export default function RadarChart({ blips }) {
|
||||
<QuadrantRings key={index} quadrant={quadrantsMap[id]} xScale={xScale} />
|
||||
))}
|
||||
|
||||
{Object.keys(ringsMap).map((id, index) => (
|
||||
<RingLabel key={index} ring={ringsMap[id]} xScale={xScale} yScale={yScale} />
|
||||
{[1, 2, 3, 4].map((ring, index) => (
|
||||
<RingLabel key={index} ring={ring} xScale={xScale} yScale={yScale} />
|
||||
))}
|
||||
|
||||
<BlipPoints blips={blips} xScale={xScale} yScale={yScale}/>
|
||||
|
||||
@@ -14,3 +14,7 @@
|
||||
padding: 5px 10px;
|
||||
border-radius: 11px;
|
||||
}
|
||||
|
||||
.chart .ring-label {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import './flag.scss';
|
||||
import {FlagType} from "../../model";
|
||||
import {blipFlags} from "../../config";
|
||||
|
||||
interface ItemFlag {
|
||||
flag: FlagType;
|
||||
@@ -10,7 +9,7 @@ interface ItemFlag {
|
||||
export default function Flag({ item, short = false }: { item: ItemFlag; short?: boolean }) {
|
||||
const ucFirst = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
|
||||
|
||||
if (item.flag !== blipFlags.default.name) {
|
||||
if (item.flag !== FlagType.default) {
|
||||
let name = item.flag.toUpperCase();
|
||||
let title = ucFirst(item.flag);
|
||||
if (short === true) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import { formatRelease } from '../../date';
|
||||
import { featuredOnly, Item } from '../../model';
|
||||
import { featuredOnly, Item, HomepageOption } from '../../model';
|
||||
import HeroHeadline from '../HeroHeadline/HeroHeadline';
|
||||
import QuadrantGrid from '../QuadrantGrid/QuadrantGrid';
|
||||
import RadarGrid from '../RadarGrid/RadarGrid';
|
||||
import Fadeable from '../Fadeable/Fadeable';
|
||||
import SetTitle from '../SetTitle';
|
||||
import { radarName, radarNameShort } from '../../config';
|
||||
import { radarName, radarNameShort, homepageContent } from '../../config';
|
||||
import { MomentInput } from 'moment';
|
||||
|
||||
type PageIndexProps = {
|
||||
@@ -19,16 +19,20 @@ type PageIndexProps = {
|
||||
export default function PageIndex({ leaving, onLeave, items, releases }: PageIndexProps) {
|
||||
const newestRelease = releases.slice(-1)[0];
|
||||
const numberOfReleases = releases.length;
|
||||
const showChart = homepageContent === HomepageOption.chart || homepageContent === HomepageOption.both;
|
||||
const showColumns = homepageContent === HomepageOption.columns || homepageContent === HomepageOption.both;
|
||||
return (
|
||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||
<SetTitle title={radarNameShort} />
|
||||
<div className='headline-group'>
|
||||
<HeroHeadline alt={`Version #${numberOfReleases}`}>{radarName}</HeroHeadline>
|
||||
</div>
|
||||
{showChart && (
|
||||
<RadarGrid blips={featuredOnly(items)} />
|
||||
{/*
|
||||
)}
|
||||
{showColumns && (
|
||||
<QuadrantGrid items={featuredOnly(items)} />
|
||||
*/}
|
||||
)}
|
||||
<div className='publish-date'>Published {formatRelease(newestRelease)}</div>
|
||||
</Fadeable>
|
||||
);
|
||||
|
||||
@@ -7,8 +7,8 @@ import Search from '../Search/Search';
|
||||
import Fadeable from '../Fadeable/Fadeable';
|
||||
import SetTitle from '../SetTitle';
|
||||
import Flag from '../Flag/Flag';
|
||||
import { groupByFirstLetter, Item } from '../../model';
|
||||
import { quadrantsMap, Ring } from '../../config';
|
||||
import { groupByFirstLetter, Item, Ring } from '../../model';
|
||||
import { quadrantsMap } from '../../config';
|
||||
|
||||
const containsSearchTerm = (text = '', term = '') => {
|
||||
// TODO search refinement
|
||||
@@ -75,10 +75,10 @@ export default function PageOverview({ rings, search: searchProp, items, leaving
|
||||
</div>
|
||||
<div className='split__right'>
|
||||
<div className='nav'>
|
||||
{rings.map((ringName) => (
|
||||
<div className='nav__item' key={ringName}>
|
||||
<Badge big onClick={handleRingClick(ringName)} type={isRingActive(ringName) ? ringName : 'empty'}>
|
||||
{ringName}
|
||||
{Object.keys(rings).map((key) => (
|
||||
<div className='nav__item' key={Ring[key]}>
|
||||
<Badge big onClick={handleRingClick(Ring[key])} type={isRingActive(Ring[key]) ? Ring[key] : 'empty'}>
|
||||
{Ring[key]}
|
||||
</Badge>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { groupByQuadrants, Item, Group } from '../../model';
|
||||
import { quadrants } from '../../config';
|
||||
import { quadrantsMap } from '../../config';
|
||||
import QuadrantSection from '../QuadrantSection/QuadrantSection';
|
||||
import './quadrant-grid.scss';
|
||||
const renderQuadrant = (quadrantName: string, groups: Group) => {
|
||||
@@ -13,5 +13,5 @@ const renderQuadrant = (quadrantName: string, groups: Group) => {
|
||||
|
||||
export default function QuadrantGrid({ items }: { items: Item[] }) {
|
||||
const groups = groupByQuadrants(items);
|
||||
return <div className='quadrant-grid'>{quadrants.map((quadrantName) => renderQuadrant(quadrantName, groups))}</div>;
|
||||
return <div className='quadrant-grid'>{Object.keys(quadrantsMap).map((quadrantName) => renderQuadrant(quadrantName, groups))}</div>;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { rings, quadrantsMap, Ring, showEmptyRings } from '../../config';
|
||||
import { quadrantsMap, showEmptyRings } from '../../config';
|
||||
import Badge from '../Badge/Badge';
|
||||
import Link from '../Link/Link';
|
||||
import IconLink from '../IconLink/IconLink';
|
||||
import ItemList from '../ItemList/ItemList';
|
||||
import Flag from '../Flag/Flag';
|
||||
import { Group } from '../../model';
|
||||
import { Group, Ring } from '../../model';
|
||||
import './quadrant-section.scss';
|
||||
const renderList = (ringName: Ring, quadrantName: string, groups: Group, big: boolean) => {
|
||||
const itemsInRing = groups[quadrantName][ringName] || [];
|
||||
@@ -65,7 +65,7 @@ export default function QuadrantSection({ quadrantName, groups, big = false, sho
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className='quadrant-section__rings'>{rings.map((ringName) => renderRing(ringName, quadrantName, groups, big))}</div>
|
||||
<div className='quadrant-section__rings'>{Object.keys(Ring).map((key) => renderRing(Ring[key], quadrantName, groups, big))}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import PageHelp from './PageHelp/PageHelp';
|
||||
import PageQuadrant from './PageQuadrant/PageQuadrant';
|
||||
import PageItem from './PageItem/PageItem';
|
||||
import PageItemMobile from './PageItemMobile/PageItemMobile';
|
||||
import {quadrantsMap, getItemPageNames, isMobileViewport, rings} from '../config';
|
||||
import {Item} from '../model';
|
||||
import {quadrantsMap, getItemPageNames, isMobileViewport} from '../config';
|
||||
import {Item, Ring} from '../model';
|
||||
|
||||
type RouterProps = {
|
||||
pageName: string
|
||||
@@ -75,7 +75,7 @@ export default function Router({pageName, items, releases, search}: RouterProps)
|
||||
case page.index:
|
||||
return <PageIndex leaving={leaving} items={items} onLeave={handlePageLeave} releases={releases}/>;
|
||||
case page.overview:
|
||||
return <PageOverview items={items} rings={rings} search={search} leaving={leaving}
|
||||
return <PageOverview items={items} rings={Object.values(Ring)} search={search} leaving={leaving}
|
||||
onLeave={handlePageLeave}/>;
|
||||
case page.help:
|
||||
return <PageHelp leaving={leaving} onLeave={handlePageLeave}/>;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {Item} from './model';
|
||||
import {Item, HomepageOption} from './model';
|
||||
|
||||
export const radarName = process.env.RADAR_NAME || 'AOE Technology Radar'
|
||||
export const radarNameShort = radarName;
|
||||
|
||||
export const homepageContent = HomepageOption.both; // by defaul show both versions so that people can choose which one they like more (or keep both)
|
||||
|
||||
// Quadrants positions start from the top right and go clockwise
|
||||
export const quadrantsMap = {
|
||||
'methods-and-patterns': {
|
||||
@@ -11,7 +13,7 @@ export const quadrantsMap = {
|
||||
colour: '#248EA6',
|
||||
txtColour: 'white',
|
||||
position: 1,
|
||||
description: 'Optional description goes here'
|
||||
description: 'Here we put information on methods and patterns concerning development, continuous x, testing, organization, architecture, etc.'
|
||||
},
|
||||
'platforms-and-aoe-services': {
|
||||
id: 'platforms-and-aoe-services',
|
||||
@@ -19,7 +21,7 @@ export const quadrantsMap = {
|
||||
colour: '#F25244',
|
||||
txtColour: '#444444',
|
||||
position: 2,
|
||||
description: 'Optional description goes here'
|
||||
description: '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.'
|
||||
},
|
||||
'tools': {
|
||||
id: 'tools',
|
||||
@@ -27,7 +29,7 @@ export const quadrantsMap = {
|
||||
colour: '#F2A25C',
|
||||
txtColour: 'white',
|
||||
position: 3,
|
||||
description: 'Optional descrption goes here'
|
||||
description: 'Here we put different software tools - from small helpers to bigger software projects'
|
||||
},
|
||||
'languages-and-frameworks': {
|
||||
id: 'languages-and-frameworks',
|
||||
@@ -35,7 +37,7 @@ export const quadrantsMap = {
|
||||
colour: '#84BFA4',
|
||||
txtColour: '#444444',
|
||||
position: 4,
|
||||
description: 'Optional description goes here'
|
||||
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."
|
||||
},
|
||||
};
|
||||
|
||||
@@ -44,50 +46,13 @@ export const chartConfig = {
|
||||
scale: [-16, 16],
|
||||
blipSize: 12, // in px, be careful when increasing this value as it may cause a lot of calculations during placing the blips on the chart
|
||||
ringsAttributes: [ // order from the centre outwards
|
||||
{ radius: 8, arcWidth: 6 }, // radius values are based on the scale (not px!)
|
||||
{ radius: 8, arcWidth: 6 }, // radius values are based on the scale (not px!), arc width is in px
|
||||
{ radius: 11, arcWidth: 4 },
|
||||
{ radius: 14, arcWidth: 2 },
|
||||
{ radius: 16, arcWidth: 2 }
|
||||
]
|
||||
};
|
||||
|
||||
export const rings = [
|
||||
'all',
|
||||
'adopt',
|
||||
'trial',
|
||||
'assess',
|
||||
'hold'
|
||||
] as const;
|
||||
|
||||
// rings positions start at the centre and go outwards
|
||||
export const ringsMap = {
|
||||
'adopt': {
|
||||
displayName: 'ADOPT',
|
||||
position: 1
|
||||
},
|
||||
'trial': {
|
||||
displayName: 'EXPLORE',
|
||||
position: 2
|
||||
},
|
||||
'assess': {
|
||||
displayName: 'ENDURE',
|
||||
position: 3
|
||||
},
|
||||
'hold': {
|
||||
displayName: 'RETIRE',
|
||||
position: 4
|
||||
}
|
||||
};
|
||||
|
||||
// TODO replace with TS enum
|
||||
export const blipFlags = {
|
||||
new: { name: 'new', short: 'N' },
|
||||
changed: { name: 'changed', short: 'C' },
|
||||
default: { name: 'default', short: '' }
|
||||
}
|
||||
|
||||
export type Ring = typeof rings[number]
|
||||
|
||||
export const getItemPageNames = (items: Item[]) => items.map(item => `${item.quadrant}/${item.name}`);
|
||||
|
||||
export const showEmptyRings = false;
|
||||
|
||||
20
src/model.ts
20
src/model.ts
@@ -1,4 +1,16 @@
|
||||
import { Ring } from "./config"
|
||||
export enum HomepageOption {
|
||||
chart,
|
||||
columns,
|
||||
both
|
||||
}
|
||||
|
||||
export enum Ring {
|
||||
all = 0,
|
||||
adopt = 1,
|
||||
trial = 2,
|
||||
assess = 3,
|
||||
hold = 4
|
||||
}
|
||||
|
||||
export type ItemAttributes = {
|
||||
name: string
|
||||
@@ -8,7 +20,11 @@ export type ItemAttributes = {
|
||||
featured: boolean
|
||||
}
|
||||
|
||||
export type FlagType = 'new' | 'changed' | 'default'
|
||||
export enum FlagType {
|
||||
new = 'new',
|
||||
changed = 'changed',
|
||||
default = 'default'
|
||||
}
|
||||
|
||||
export type Item = ItemAttributes & {
|
||||
featured: boolean
|
||||
|
||||
@@ -3,9 +3,9 @@ import path from 'path';
|
||||
import frontmatter from 'front-matter';
|
||||
import marked from 'marked';
|
||||
import hljs from 'highlight.js';
|
||||
import { quadrantsMap, ringsMap, blipFlags } from '../src/config';
|
||||
import { quadrantsMap } from '../src/config';
|
||||
import { radarPath, getAllMarkdownFiles } from './file';
|
||||
import { Item, Revision, ItemAttributes, Radar } from '../src/model';
|
||||
import { Item, Revision, ItemAttributes, Radar, FlagType, Ring } from '../src/model';
|
||||
|
||||
type FMAttributes = ItemAttributes
|
||||
|
||||
@@ -28,10 +28,9 @@ export const createRadar = async (): Promise<Radar> => {
|
||||
|
||||
const checkAttributes = (fileName: string, attributes: FMAttributes) => {
|
||||
const validQuadrants = Object.keys(quadrantsMap);
|
||||
const validRings = Object.keys(ringsMap);
|
||||
|
||||
if (attributes.ring && !validRings.includes(attributes.ring)) {
|
||||
throw new Error(`Error: ${fileName} has an illegal value for 'ring' - must be one of ${validRings}`);
|
||||
if (attributes.ring && !Ring[attributes.ring]) {
|
||||
throw new Error(`Error: ${fileName} has an illegal value for 'ring' - must be one of ${Object.values(Ring)}`);
|
||||
}
|
||||
|
||||
if (attributes.quadrant && !validQuadrants.includes(attributes.quadrant)) {
|
||||
@@ -116,12 +115,12 @@ const ignoreEmptyRevisionBody = (revision: Revision, item: Item) => {
|
||||
|
||||
const addRevisionToItem = (
|
||||
item: Item = {
|
||||
flag: 'default',
|
||||
flag: FlagType.default,
|
||||
featured: true,
|
||||
revisions: [],
|
||||
name: '',
|
||||
title: '',
|
||||
ring: 'trial',
|
||||
ring: Ring.trial,
|
||||
quadrant: '',
|
||||
body: '',
|
||||
info: '',
|
||||
@@ -168,10 +167,10 @@ const hasItemChanged = (item: Item, allReleases: string[]) =>
|
||||
|
||||
const getItemFlag = (item: Item, allReleases: string[]): string => {
|
||||
if (isNewItem(item, allReleases)) {
|
||||
return blipFlags.new.name;
|
||||
return FlagType.new;
|
||||
}
|
||||
if (hasItemChanged(item, allReleases)) {
|
||||
return blipFlags.changed.name;
|
||||
return FlagType.changed;
|
||||
}
|
||||
return blipFlags.default.name;
|
||||
return FlagType.default;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user