diff --git a/workspaces/tech-radar/.changeset/witty-parents-cover.md b/workspaces/tech-radar/.changeset/witty-parents-cover.md new file mode 100644 index 0000000000..022c87f719 --- /dev/null +++ b/workspaces/tech-radar/.changeset/witty-parents-cover.md @@ -0,0 +1,5 @@ +--- +'@backstage-community/plugin-tech-radar': major +--- + +The plugin now responds better to small screens. The number of columns in the legend adjusts based on screen width; it will have 3 columns on wide screens and 1 column on narrow screens. The columns will never overlap and the legend will not disappear on very narrow screens. diff --git a/workspaces/tech-radar/plugins/tech-radar/src/components/Radar/Radar.test.tsx b/workspaces/tech-radar/plugins/tech-radar/src/components/Radar/Radar.test.tsx index b76eb20a16..effbd24bf6 100644 --- a/workspaces/tech-radar/plugins/tech-radar/src/components/Radar/Radar.test.tsx +++ b/workspaces/tech-radar/plugins/tech-radar/src/components/Radar/Radar.test.tsx @@ -23,7 +23,12 @@ import Radar, { Props } from './Radar'; const minProps: Props = { width: 500, height: 200, - quadrants: [{ id: 'languages', name: 'Languages' }], + quadrants: [ + { id: 'infrastructure', name: 'Infrastructure' }, + { id: 'frameworks', name: 'Frameworks' }, + { id: 'languages', name: 'Languages' }, + { id: 'process', name: 'Process' }, + ], rings: [{ id: 'use', name: 'USE', color: '#93c47d' }], entries: [ { diff --git a/workspaces/tech-radar/plugins/tech-radar/src/components/Radar/Radar.tsx b/workspaces/tech-radar/plugins/tech-radar/src/components/Radar/Radar.tsx index 55b8a01784..304c8b0f6d 100644 --- a/workspaces/tech-radar/plugins/tech-radar/src/components/Radar/Radar.tsx +++ b/workspaces/tech-radar/plugins/tech-radar/src/components/Radar/Radar.tsx @@ -17,7 +17,15 @@ import React, { useMemo, useRef, useState } from 'react'; import type { Entry, Quadrant, Ring } from '../../utils/types'; import RadarPlot from '../RadarPlot'; -import { adjustEntries, adjustQuadrants, adjustRings } from './utils'; +import { + adjustEntries, + adjustQuadrants, + adjustRings, + determineColumnCount, + determineColumnWidth, + determineLegendWidth, + margin, +} from './utils'; export type Props = { width: number; @@ -36,10 +44,9 @@ const Radar = ({ entries, ...props }: Props): JSX.Element => { - // Radius with optional buffer for the footer if rings have descriptions - const radius = - Math.min(width, height) / 2 - - (rings.some(ring => ring.description) ? 16 : 0); + const columnWidth = determineColumnWidth(width); + const legendWidth = determineLegendWidth(width, columnWidth); + const columnCount = determineColumnCount(legendWidth, columnWidth); // State const [activeEntry, setActiveEntry] = useState(); @@ -47,9 +54,18 @@ const Radar = ({ // Adjusted props const adjustedQuadrants = useMemo( - () => adjustQuadrants(quadrants, radius, width, height), - [quadrants, radius, width, height], + () => adjustQuadrants(quadrants, width, legendWidth, height), + [quadrants, width, legendWidth, height], ); + + const rightMostPoint = adjustedQuadrants[3].legendX + legendWidth; + const leftMostPoint = adjustedQuadrants[2].legendX; + + const midpoint = (rightMostPoint + leftMostPoint) / 2; + const widthBetween = + rightMostPoint - legendWidth - (leftMostPoint + legendWidth); + const radius = Math.min(widthBetween, height) / 2 - margin; + const adjustedRings = useMemo( () => adjustRings(rings, radius), [radius, rings], @@ -69,9 +85,10 @@ const Radar = ({ return ( { + const minWidth = 110; + const maxWidth = 130; + const maxContainerWidth = 1600; + + return Math.min( + maxWidth, + Math.max( + minWidth, + minWidth + + ((width - maxContainerWidth / 2) / maxContainerWidth) * + (maxWidth - minWidth), + ), + ); +}; + +export const determineColumnCount = ( + legendWidth: number, + columnWidth: number, +): number => { + const columnWidths = [columnWidth, columnWidth * 2, columnWidth * 3]; + return ( + columnWidths.findIndex(width => legendWidth <= width) + 1 || + columnWidths.length + ); +}; + +export const determineLegendWidth = ( + width: number, + columnWidth: number, +): number => { + const thresholds = [850, 1250]; + const columnWidths = [columnWidth, columnWidth * 2, columnWidth * 3]; + + if (width < thresholds[0]) return columnWidths[0]; + if (width < thresholds[1]) return columnWidths[1]; + return columnWidths[2]; +}; + export const adjustQuadrants = ( quadrants: Quadrant[], - radius: number, width: number, + legendWidth: number, height: number, ) => { /* @@ -43,40 +84,34 @@ export const adjustQuadrants = ( ┼───────────┼─────────────────────────────┼───────────┼─3 */ - const margin = 16; - const xStops = [ - margin, - width / 2 - radius - margin, - width / 2 + radius + margin, - width - margin, - ]; + const xStarts = [margin, width - legendWidth - margin]; const yStops = [margin, height / 2 - margin, height / 2, height - margin]; // The quadrant parameters correspond to Q[0..3] above. They are in this order because of the // original Zalando code; maybe we should refactor them to be in reverse order? const legendParams = [ { - x: xStops[2], + x: xStarts[1], y: yStops[2], - width: xStops[3] - xStops[2], + width: legendWidth, height: yStops[3] - yStops[2], }, { - x: xStops[0], + x: xStarts[0], y: yStops[2], - width: xStops[1] - xStops[0], + width: legendWidth, height: yStops[3] - yStops[2], }, { - x: xStops[0], + x: xStarts[0], y: yStops[0], - width: xStops[1] - xStops[0], + width: legendWidth, height: yStops[1] - yStops[0], }, { - x: xStops[2], + x: xStarts[1], y: yStops[0], - width: xStops[3] - xStops[2], + width: legendWidth, height: yStops[1] - yStops[0], }, ]; diff --git a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarComponent.test.tsx b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarComponent.test.tsx index 383e64bb42..e701090a52 100644 --- a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarComponent.test.tsx +++ b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarComponent.test.tsx @@ -39,7 +39,12 @@ describe('RadarComponent', () => { await new Promise(resolve => setTimeout(resolve, this.delay)); return { entries: [], - quadrants: [], + quadrants: [ + { id: 'infrastructure', name: 'Infrastructure' }, + { id: 'frameworks', name: 'Frameworks' }, + { id: 'languages', name: 'Languages' }, + { id: 'process', name: 'Process' }, + ], rings: [], }; } diff --git a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarLegend/RadarLegend.test.tsx b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarLegend/RadarLegend.test.tsx index 70cd2d0244..e034aeb291 100644 --- a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarLegend/RadarLegend.test.tsx +++ b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarLegend/RadarLegend.test.tsx @@ -22,6 +22,7 @@ import RadarLegend from './RadarLegend'; import { RadarLegendProps } from './types'; const minProps: RadarLegendProps = { + columnCount: 2, quadrants: [{ id: 'languages', name: 'Languages' }], rings: [{ id: 'use', name: 'USE', color: '#93c47d' }], entries: [ diff --git a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx index 0eab1adf75..eab3e0c9f7 100644 --- a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx +++ b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx @@ -35,7 +35,7 @@ const useStyles = makeStyles(theme => ({ fontSize: '18px', }, rings: { - columns: 3, + columns: (props: { columnCount: number }) => props.columnCount, }, ring: { breakInside: 'avoid-column', diff --git a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarLegend/types.ts b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarLegend/types.ts index cdac1bfe9d..a07235c111 100644 --- a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarLegend/types.ts +++ b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarLegend/types.ts @@ -21,6 +21,7 @@ export type Segments = { }; export type RadarLegendProps = { + columnCount: number; quadrants: Quadrant[]; rings: Ring[]; entries: Entry[]; diff --git a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarPage.test.tsx b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarPage.test.tsx index a78a5cc723..425285d319 100644 --- a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarPage.test.tsx +++ b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarPage.test.tsx @@ -40,7 +40,12 @@ describe('RadarPage', () => { async load(): Promise { return { entries: [], - quadrants: [], + quadrants: [ + { id: 'infrastructure', name: 'Infrastructure' }, + { id: 'frameworks', name: 'Frameworks' }, + { id: 'languages', name: 'Languages' }, + { id: 'process', name: 'Process' }, + ], rings: [], }; } @@ -54,7 +59,12 @@ describe('RadarPage', () => { await new Promise(resolve => setTimeout(resolve, 1000)); return { entries: [], - quadrants: [], + quadrants: [ + { id: 'infrastructure', name: 'Infrastructure' }, + { id: 'frameworks', name: 'Frameworks' }, + { id: 'languages', name: 'Languages' }, + { id: 'process', name: 'Process' }, + ], rings: [], }; } diff --git a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarPlot/RadarPlot.test.tsx b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarPlot/RadarPlot.test.tsx index 7557755ed2..b376ffcc0b 100644 --- a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarPlot/RadarPlot.test.tsx +++ b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarPlot/RadarPlot.test.tsx @@ -24,6 +24,7 @@ const minProps: Props = { width: 500, height: 200, radius: 50, + columnCount: 2, quadrants: [{ id: 'languages', name: 'Languages' }], rings: [{ id: 'use', name: 'USE', color: '#93c47d' }], entries: [ diff --git a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx index 56fd029eee..a5da08b5d5 100644 --- a/workspaces/tech-radar/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx +++ b/workspaces/tech-radar/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx @@ -27,6 +27,7 @@ export type Props = { width: number; height: number; radius: number; + columnCount: number; rings: Ring[]; quadrants: Quadrant[]; entries: Entry[]; @@ -41,6 +42,7 @@ const RadarPlot = (props: Props): JSX.Element => { width, height, radius, + columnCount, quadrants, rings, entries, @@ -52,6 +54,7 @@ const RadarPlot = (props: Props): JSX.Element => { return (