Skip to content

Commit fab9df3

Browse files
twjefferychrisolsen
authored andcommitted
feat(#3713): add related component cards to component pages
Surface relatedComponents MDX data as mini cards below tabs on each component page. Cards show thumbnail, name, description, and category badge. Extract shared thumbnail utility from ComponentsGrid. Fix relatedComponents data: typo in input, add missing bidirectional relationships for pagination, drawer, form-item, side-menu, button.
1 parent de0ed34 commit fab9df3

9 files changed

Lines changed: 163 additions & 22 deletions

File tree

docs/src/components/ComponentsGrid.tsx

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*/
1212

1313
import React, { useState, useMemo, useCallback, useEffect, useRef } from "react";
14+
import { getThumbnailPath } from "../lib/component-thumbnails";
1415
import { createPortal } from "react-dom";
1516
import {
1617
GoabButton,
@@ -114,27 +115,6 @@ function formatStatus(status: string): string {
114115
return status.charAt(0).toUpperCase() + status.slice(1);
115116
}
116117

117-
// Thumbnail filename mapping for slugs that don't match the filename
118-
const THUMBNAIL_MAP: Record<string, string> = {
119-
"app-header": "header",
120-
icon: "icons",
121-
input: "text-input",
122-
"checkbox-list": "checkbox-group",
123-
"circular-progress": "circular-progress-indicator",
124-
"linear-progress": "linear-progress-indicator",
125-
notification: "notification-banner",
126-
skeleton: "skeleton-loader",
127-
"radio-group": "radio",
128-
"file-upload-input": "file-uploader",
129-
"link-button": "link",
130-
"page-block": "block",
131-
};
132-
133-
function getThumbnailPath(slug: string): string {
134-
const filename = THUMBNAIL_MAP[slug] || slug;
135-
return `/images/component-thumbnails/${filename}.svg`;
136-
}
137-
138118
export function ComponentsGrid({ components }: ComponentsGridProps) {
139119
// State
140120
const [searchValue, setSearchValue] = useState("");

docs/src/content/components/button.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ tags:
1010
relatedComponents:
1111
- button-group
1212
- icon-button
13+
- link-button
1314
figmaUrl: https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=420-6810
1415
githubUrl: https://github.com/GovAlta/ui-components/issues?q=is%3Aissue+is%3Aopen+label%3AButton
1516
webComponentTag: goa-button

docs/src/content/components/drawer.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ tags:
1111
- sidepanel
1212
relatedComponents:
1313
- modal
14+
- push-drawer
1415
githubUrl: https://github.com/GovAlta/ui-components/labels/Drawer
1516
webComponentTag: goa-drawer
1617
reactClassName: GoabDrawer

docs/src/content/components/form-item.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ relatedComponents:
1111
- input
1212
- dropdown
1313
- checkbox
14+
- checkbox-list
15+
- date-picker
16+
- file-upload-input
17+
- radio-group
18+
- text-area
1419
figmaUrl: https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=27284-300347
1520
githubUrl: https://github.com/GovAlta/ui-components/labels/Form%20item
1621
webComponentTag: goa-form-item

docs/src/content/components/input.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ tags:
1111
- text input
1212
relatedComponents:
1313
- form-item
14-
- textarea
14+
- text-area
1515
- dropdown
1616
figmaUrl: https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=27301-303447
1717
githubUrl: https://github.com/GovAlta/ui-components/labels/Input
@@ -25,6 +25,7 @@ Capture text input from users in forms.
2525
## When to use
2626

2727
Use Input when you need users to enter:
28+
2829
- Short text answers (names, email addresses)
2930
- Numbers (phone numbers, quantities)
3031
- Dates and times
@@ -33,6 +34,7 @@ Use Input when you need users to enter:
3334
## When not to use
3435

3536
Don't use Input when:
37+
3638
- Users need to enter multiple lines of text (use Textarea)
3739
- Users need to select from predefined options (use Dropdown or Radio)
3840
- The input requires complex formatting (consider specialized components)

docs/src/content/components/pagination.mdx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ tags:
77
- pager
88
- pagination controls
99
- structure and navigation
10+
relatedComponents:
11+
- data-grid
12+
- table
1013
figmaUrl: https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=622-13964
1114
githubUrl: https://github.com/GovAlta/ui-components/labels/Pagination
1215
webComponentTag: goa-pagination

docs/src/content/components/side-menu.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ tags:
1111
relatedComponents:
1212
- side-menu-group
1313
- side-menu-heading
14+
- work-side-menu
1415
figmaUrl: https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=24089-474089
1516
githubUrl: https://github.com/GovAlta/ui-components/labels/Side%20menu
1617
webComponentTag: goa-side-menu
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Slug-to-filename mapping for component thumbnails that don't match their slug
2+
const THUMBNAIL_MAP: Record<string, string> = {
3+
"app-header": "header",
4+
icon: "icons",
5+
input: "text-input",
6+
"checkbox-list": "checkbox-group",
7+
"circular-progress": "circular-progress-indicator",
8+
"linear-progress": "linear-progress-indicator",
9+
notification: "notification-banner",
10+
skeleton: "skeleton-loader",
11+
"radio-group": "radio",
12+
"file-upload-input": "file-uploader",
13+
"link-button": "link",
14+
"page-block": "block",
15+
};
16+
17+
export function getThumbnailPath(slug: string): string {
18+
const filename = THUMBNAIL_MAP[slug] || slug;
19+
return `/images/component-thumbnails/${filename}.svg`;
20+
}

docs/src/pages/components/[slug].astro

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ import {
1010
getComponentApi,
1111
getGuidanceForComponent,
1212
getExamplesForComponent,
13+
getRelatedComponents,
1314
getSubcomponents,
1415
categorizeGuidance,
1516
} from '../../lib/content-queries';
1617
import { getComponentConfigurations } from '../../data/configurations';
18+
import { getThumbnailPath } from '../../lib/component-thumbnails';
1719
1820
// Generate static paths for all components
1921
export async function getStaticPaths() {
@@ -47,6 +49,9 @@ const componentExamples = await getExamplesForComponent(slug);
4749
// 5. Get component configurations (if available)
4850
const configurations = getComponentConfigurations(slug);
4951
52+
// 6. Get visible related components (peers, not subcomponents)
53+
const relatedComponents = await getRelatedComponents(component.data.relatedComponents ?? []);
54+
5055
// GitHub issues URL for this component
5156
const githubIssuesUrl = `https://github.com/GovAlta/ui-components/issues?q=is%3Aissue+is%3Aopen+label%3A%22${encodeURIComponent(component.data.name)}%22`;
5257
@@ -174,6 +179,39 @@ const v1DocsUrl = `https://v1.design.alberta.ca/components/${slug}`;
174179
</TabContentWrapper>
175180
</goa-tab>
176181
</goa-tabs>
182+
183+
{relatedComponents.length > 0 && (
184+
<section class="related-components">
185+
<h2 class="related-heading">Related components</h2>
186+
<div class="related-cards">
187+
{relatedComponents.map((comp) => {
188+
const compSlug = comp.data.slug || comp.slug;
189+
return (
190+
<a href={`/components/${compSlug}`} class="related-card">
191+
<div class="related-card-thumbnail" aria-hidden="true">
192+
<img src={getThumbnailPath(compSlug)} alt="" loading="lazy"
193+
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
194+
<span class="related-card-thumbnail-fallback">{comp.data.name}</span>
195+
</div>
196+
<div class="related-card-info">
197+
<span class="related-card-name">{comp.data.name}</span>
198+
{comp.data.description && (
199+
<p class="related-card-description">{comp.data.description}</p>
200+
)}
201+
<div class="related-card-badge">
202+
<goa-badge version="2" type="default" emphasis="subtle" icon="false" content={comp.data.category.replace(/-/g, ' ')} />
203+
</div>
204+
</div>
205+
</a>
206+
);
207+
})}
208+
</div>
209+
</section>
210+
)}
211+
212+
<goa-spacer version="2" vspacing="xl"></goa-spacer>
213+
<goa-divider version="2"></goa-divider>
214+
<goa-spacer version="2" vspacing="m"></goa-spacer>
177215
</div>
178216
<goa-link size="small" trailingicon="open">
179217
<a href={v1DocsUrl} target="_blank" rel="noopener noreferrer"
@@ -296,5 +334,95 @@ const v1DocsUrl = `https://v1.design.alberta.ca/components/${slug}`;
296334
padding-left: var(--goa-space-l);
297335
max-width: 65ch;
298336
}
337+
338+
/* Related Components */
339+
.related-components {
340+
margin-top: var(--goa-space-2xl);
341+
padding-top: var(--goa-space-xl);
342+
border-top: 1px solid var(--goa-color-greyscale-200);
343+
}
344+
345+
.related-heading {
346+
font: var(--goa-typography-heading-s);
347+
color: var(--goa-color-text-default);
348+
margin: 0 0 var(--goa-space-m);
349+
}
350+
351+
.related-cards {
352+
display: grid;
353+
grid-template-columns: repeat(auto-fill, minmax(12rem, 15rem));
354+
gap: var(--goa-space-m);
355+
}
356+
357+
.related-card {
358+
display: flex;
359+
flex-direction: column;
360+
text-decoration: none;
361+
color: inherit;
362+
}
363+
364+
.related-card:focus-visible {
365+
outline: 2px solid var(--goa-color-interactive-focus);
366+
outline-offset: 2px;
367+
border-radius: var(--goa-border-radius-m);
368+
}
369+
370+
.related-card-thumbnail {
371+
aspect-ratio: 386 / 256;
372+
background: var(--goa-color-greyscale-200);
373+
border-radius: var(--goa-border-radius-m);
374+
overflow: hidden;
375+
}
376+
377+
.related-card-thumbnail img {
378+
width: 100%;
379+
height: 100%;
380+
object-fit: cover;
381+
display: block;
382+
}
383+
384+
.related-card-thumbnail-fallback {
385+
display: none;
386+
width: 100%;
387+
height: 100%;
388+
align-items: center;
389+
justify-content: center;
390+
font: var(--goa-typography-heading-xs);
391+
color: var(--goa-color-text-secondary);
392+
text-align: center;
393+
padding: var(--goa-space-s);
394+
}
395+
396+
.related-card-info {
397+
display: flex;
398+
flex-direction: column;
399+
gap: var(--goa-space-2xs);
400+
padding-top: var(--goa-space-xs);
401+
}
402+
403+
.related-card-name {
404+
font: var(--goa-typography-heading-xs);
405+
color: var(--goa-color-interactive-default);
406+
text-decoration: none;
407+
}
408+
409+
.related-card:hover .related-card-name {
410+
text-decoration: underline;
411+
}
412+
413+
.related-card-description {
414+
margin: 0;
415+
font: var(--goa-typography-body-s);
416+
color: var(--goa-color-text-secondary);
417+
line-height: 1.5;
418+
display: -webkit-box;
419+
-webkit-line-clamp: 2;
420+
-webkit-box-orient: vertical;
421+
overflow: hidden;
422+
}
423+
424+
.related-card-badge {
425+
margin-top: var(--goa-space-3xs);
426+
}
299427
</style>
300428

0 commit comments

Comments
 (0)