Pending Review
Pending Review from User Experience (UX)
Link Component
Overview
The PWC Link component provides a consistent and accessible way to create navigational and interactive links throughout the application. Built on Lit web components, it supports internal navigation, external links, and custom actions with integrated icon support and comprehensive accessibility features.
Core Capabilities
- Flexible Navigation - Support for internal routing, external URLs, and custom click actions
- Icon Integration - Seamless icon display with proper color coordination and spacing
- Accessibility Standards - Built-in ARIA attributes, keyboard navigation, and screen reader support
- Interactive States - Comprehensive hover, focus, and active states with visual feedback
- Target Control - Configurable link targets for new window/tab opening behavior
- Event Handling - Custom click event handling with prevention for placeholder links
When to use Link:
- Navigation Elements - Create connections between different pages or sections of the application
- External Resources - Link to external websites, documentation, or downloadable content
- Action Triggers - Implement clickable elements that perform specific actions or operations
- Reference Content - Provide access to related information, help documentation, or support resources
When not to use Link:
- Primary Actions - Use button components for main call-to-action elements instead of links
- Form Submissions - Utilize proper form submission buttons rather than link-based form handling
- Complex Interactions - Choose specialized components for multi-step processes or complex user interactions
Basic Implementation
import React, { useState, useCallback } from 'react';
import { PwcLink, PwcFlex, PwcButton } from '@progress-i360/pwc-react';
function SmartNavigation() {
const [activeLink, setActiveLink] = useState('dashboard');
const [visitCount, setVisitCount] = useState({});
// React 18: useCallback for optimization
const handleLinkClick = useCallback((linkName) => {
setVisitCount(prev => ({
...prev,
[linkName]: (prev[linkName] || 0) + 1
}));
setActiveLink(linkName);
}, []);
const links = [
{ name: 'dashboard', label: 'Dashboard', href: '/dashboard' },
{ name: 'profile', label: 'Profile', href: '/profile' },
{ name: 'settings', label: 'Settings', href: '/settings' }
];
return (
<PwcFlex direction="column" gap="s">
<PwcFlex gap="m">
{links.map(link => (
<PwcLink
key={link.name}
content={`${link.label} (${visitCount[link.name] || 0})`}
href={link.href}
onClick={(e) => {
e.preventDefault();
handleLinkClick(link.name);
}}
/>
))}
</PwcFlex>
<PwcButton
label="Reset Counts"
variant="outline"
onPwcClick={() => setVisitCount({})}
/>
</PwcFlex>
);
}
import { useCallback, useOptimistic, useState, startTransition } from 'react';
import '@progress-i360/progress-web-components/link';
import '@progress-i360/progress-web-components/flex';
import '@progress-i360/progress-web-components/button';
type Bookmark = {
id: number;
title: string;
href: string;
visited: boolean;
};
function BookmarkTracker() {
const [links, setLinks] = useState<Bookmark[]>([
{ id: 1, title: 'Progress Docs', href: 'https://progress.com/docs', visited: false },
{ id: 2, title: 'OpenEdge Guide', href: 'https://progress.com/openedge', visited: false }
]);
// React 19+: Optimistic visit tracking
const [optimisticLinks, markVisited] = useOptimistic(
links,
(state, linkId: number) => state.map(link =>
link.id === linkId ? { ...link, visited: true } : link
)
);
const handleLinkClick = useCallback((linkId: number) => {
markVisited(linkId);
startTransition(() => {
setLinks(prev => prev.map(link =>
link.id === linkId ? { ...link, visited: true } : link
));
});
}, [markVisited, setLinks]);
const addLink = useCallback(() => {
const newLink: Bookmark = {
id: Date.now(),
title: `Resource ${optimisticLinks.length + 1}`,
href: `https://progress.com/resources/${optimisticLinks.length + 1}`,
visited: false
};
startTransition(() => {
setLinks(prev => [...prev, newLink]);
});
}, [optimisticLinks.length, setLinks]);
return (
<pwc-flex direction="column" gap="s">
{optimisticLinks.map(link => (
<pwc-link
key={link.id}
content={`${link.title} ${link.visited ? '✓' : ''}`}
href={link.href}
target="_blank"
onPwcClick={() => handleLinkClick(link.id)}
></pwc-link>
))}
<pwc-button
label="Add Link"
variant="primary"
onPwcClick={addLink}
></pwc-button>
</pwc-flex>
);
}
import { Component, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import "@progress-i360/progress-web-components/link";
import "@progress-i360/progress-web-components/flex";
import "@progress-i360/progress-web-components/button";
import "@progress-i360/progress-web-components/body";
import "@progress-i360/progress-web-components/heading";
@Component({
selector: 'link-demo',
template: `
<pwc-flex direction="column" gap="m" padding="m">
<pwc-heading content="Navigation Links Demo" size="l"></pwc-heading>
<pwc-flex direction="column" gap="s">
<pwc-link [content]="linkText" [href]="linkUrl" [target]="linkTarget" (pwc-click)="handleLinkClick()"></pwc-link>
<pwc-link content="External Documentation" href="https://docs.example.com" target="_blank" [icon]="externalIcon"></pwc-link>
<pwc-link content="Custom Action Link" href="#" (pwc-click)="performCustomAction()"></pwc-link>
</pwc-flex>
<pwc-flex gap="s" wrap="wrap">
<pwc-button label="Change Link" variant="outline" (pwc-click)="changeLink()"></pwc-button>
<pwc-button label="Toggle Target" variant="outline" (pwc-click)="toggleTarget()"></pwc-button>
<pwc-button label="Toggle Icon" variant="primary" (pwc-click)="toggleIcon()"></pwc-button>
</pwc-flex>
<pwc-body [content]="'Clicks: ' + clickCount + ', Target: ' + (linkTarget || 'Same window')" color="subtle" size="xs"></pwc-body>
</pwc-flex>
`,
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class LinkDemo {
linkText = 'Go to Dashboard';
linkUrl = '/dashboard';
linkTarget = '';
externalIcon = 'external-link';
clickCount = 0;
links = [
{ text: 'Go to Dashboard', url: '/dashboard' },
{ text: 'View Profile', url: '/profile' },
{ text: 'Settings Page', url: '/settings' },
{ text: 'Help Center', url: '/help' }
];
currentLinkIndex = 0;
handleLinkClick() {
this.clickCount++;
}
changeLink() {
this.currentLinkIndex = (this.currentLinkIndex + 1) % this.links.length;
const link = this.links[this.currentLinkIndex];
this.linkText = link.text;
this.linkUrl = link.url;
}
toggleTarget() {
this.linkTarget = this.linkTarget === '_blank' ? '' : '_blank';
}
toggleIcon() {
this.externalIcon = this.externalIcon === 'external-link' ? 'arrow-right' : 'external-link';
}
performCustomAction() {
this.clickCount++;
alert(`Custom action performed! Total clicks: ${this.clickCount}`);
}
}
import '@progress-i360/progress-web-components/link';
const navLink = document.createElement('pwc-link');
navLink.setAttribute('content', 'Go to Settings');
navLink.setAttribute('href', '/settings');
const externalLink = document.createElement('pwc-link');
externalLink.setAttribute('content', 'View Documentation');
externalLink.setAttribute('href', 'https://docs.example.com');
externalLink.setAttribute('target', '_blank');
document.body.appendChild(navLink);
document.body.appendChild(externalLink);
Usage Patterns
- Internal Navigation - Use relative URLs and standard href attributes for single-page application routing and page transitions
- External Resources - Implement target="_blank" for external links with proper security attributes for user safety
- Custom Actions - Utilize href="#" with click handlers for links that trigger application-specific functionality
- Icon Enhancement - Integrate icons to provide visual context and improve link recognition and usability
Best Practices
Content Strategy Guidelines
- Clear Link Text - Write descriptive link content that clearly indicates the destination or action
- Contextual Relevance - Ensure link text makes sense when read out of context by screen readers
- Consistent Language - Use similar phrasing patterns for links that perform similar actions
- Action-Oriented Wording - Use active voice and specific verbs that describe what will happen when clicked
Performance Optimization
- Efficient Rendering - Lightweight component structure that minimizes DOM manipulation and reflow
- Icon Loading - Optimize icon assets for fast loading when using integrated icon functionality
- Event Delegation - Implement efficient click event handling with proper event propagation management
- Link Prefetching - Consider implementing link prefetching for frequently accessed internal routes
Integration Architecture
- Router Compatibility - Seamless integration with application routing systems and navigation frameworks
- Design Token Usage - Consistent styling through PWC design tokens for colors, typography, and spacing
- Accessibility Compliance - Built-in ARIA attributes and keyboard navigation support for inclusive design
- State Management - Proper handling of active, visited, and focus states for enhanced user experience
Common Use Cases
Data Table Headers
- Column Links - Navigation links in table headers for detailed views or sorting functionality
- Export Actions - Download links for data export with appropriate file type indicators
- Filter Navigation - Links to predefined filter states or saved search configurations
Search Result Sections
- Result Links - Direct navigation to search result details with descriptive link text
- Category Filters - Links to refine search results by category, type, or other criteria
- Related Content - Cross-references to related searches or similar content items
Dashboard Widget Headers
- Widget Details - Links to expanded views or detailed analysis of widget data
- Configuration Links - Access to widget settings and customization options
- Help Resources - Links to documentation, tutorials, or support information for specific widgets
Troubleshooting
Common Issues
Links Not Navigating
Problem: Links don't navigate to the specified URL or route
Solution:
- Verify href attribute is correctly set and check for JavaScript event prevention
Target Attribute Not Working
Problem: Links don't open in new windows or tabs as expected
Solution:
- Ensure target="_blank" is properly set and consider adding rel="noopener noreferrer" for security
Styling Issues
Problem: Links don't display proper hover or focus states
Solution:
- Check CSS custom properties are loaded and verify design token implementation
Icon Display Problems
Problem: Icons don't appear or are misaligned within links
Solution:
- Verify icon property is set correctly and ensure PWC Icon component is properly imported
Implementation Support
For detailed implementation guidance:
- Router Integration - Compatible with React Router, Angular Router, and other navigation frameworks
- Accessibility Features - Built-in ARIA attributes, keyboard navigation, and screen reader optimization
- Design System Alignment - Consistent styling through PWC design tokens and component architecture
Resources
Storybook Documentation
For comprehensive API documentation, interactive examples, and testing tools: 📖 View Complete API Documentation in Storybook →
This guide provides high-level implementation guidance. For detailed API specifications and interactive examples, visit our Storybook documentation.