Skip to content

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

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.