Skip to content

Pending Review

Pending Review from User Experience (UX)

Switch Component

Overview

The PWC Switch component provides an interactive toggle control for binary state management with enhanced visual feedback and form integration. Built on the InputBase foundation with integrated icon system and label container architecture, it offers smooth state transitions, accessibility compliance, and seamless PWC form system integration for preference management and feature toggling workflows.

Core Capabilities

  • Visual State Indicators - Integrated checkmark and close icons with color-coded backgrounds for clear on/off state communication
  • Interactive Thumb Animation - Smooth sliding thumb animation with radio-button icon that provides immediate visual feedback during state changes
  • Label Container Integration - Built-in label container system supporting hints, headings, and required field indicators for comprehensive form integration
  • Form Validation Support - Boolean value validation with required field checking and error message display for form integrity
  • Loading State Management - Skeleton rendering during form loading with consistent visual feedback and progressive enhancement
  • Accessibility Compliance - Full keyboard navigation, screen reader support, and semantic HTML structure for inclusive user interaction

When to use Switch:

  • Binary preference toggles requiring immediate visual feedback and clear on/off state representation for user settings
  • Feature enablement controls where users need to activate or deactivate functionality with obvious state indication
  • Settings panels with binary configuration options that benefit from toggle-style interaction over checkbox presentation
  • Dashboard controls where switch styling provides better visual hierarchy and user experience than traditional checkboxes

When not to use Switch:

  • Multi-option selections where radio buttons or select dropdowns provide more appropriate choice mechanisms
  • Agreement or consent scenarios where checkbox semantics provide clearer legal and user experience implications
  • Form fields requiring explicit user acknowledgment where checkbox interaction patterns offer better compliance tracking

Basic Implementation

import { PwcSwitch } from "@progress-i360/pwc-react";
import { useState } from 'react';

function UserPreferences() {
  const [preferences, setPreferences] = useState({
    notifications: true,
    darkMode: false
  });

  const updatePreference = (field: string, value: boolean) => {
    setPreferences((previous) => ({ ...previous, [field]: value }));
  };

  return (
    <>
      <PwcSwitch
        name="notifications"
        label="Enable Notifications"
        hint="Receive updates about your account and activity"
        value={preferences.notifications}
        required
        autoFocus
        onChange={(event) => updatePreference('notifications', event.detail)}
      />

      <PwcSwitch
        name="darkMode"
        label="Dark Mode"
        hint="Use dark theme for better viewing in low light"
        value={preferences.darkMode}
        onChange={(event) => updatePreference('darkMode', event.detail)}
      />
    </>
  );
}
import { useEffect, useMemo, useRef, useState } from 'react';
import '@progress-i360/progress-web-components/button';
import '@progress-i360/progress-web-components/form';
import '@progress-i360/progress-web-components/switch';

type SwitchElement = HTMLElement & {
  checked?: boolean;
  disabled?: boolean;
  required?: boolean;
};

type ButtonElement = HTMLElement & {
  label?: string;
  disabled?: boolean;
  variant?: string;
};

type PwcSubmitDetail = {
  payload: Record<string, unknown>;
  success: () => void;
};

function ApplicationSettingsForm() {
  const formRef = useRef<HTMLElement | null>(null);
  const maintenanceSwitchRef = useRef<SwitchElement | null>(null);
  const cachingSwitchRef = useRef<SwitchElement | null>(null);
  const submitButtonRef = useRef<ButtonElement | null>(null);

  const initialSettings = useMemo(() => ({ maintenance: false, caching: true }), []);

  const [settings, setSettings] = useState(initialSettings);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [hasChanges, setHasChanges] = useState(false);

  useEffect(() => {
    const changed =
      settings.maintenance !== initialSettings.maintenance || settings.caching !== initialSettings.caching;
    setHasChanges(changed);
  }, [initialSettings, settings]);

  useEffect(() => {
    const maintenanceSwitch = maintenanceSwitchRef.current;
    if (!maintenanceSwitch) {
      return;
    }

    maintenanceSwitch.checked = settings.maintenance;
    maintenanceSwitch.disabled = isSubmitting;

    const handleMaintenanceChange = (event: Event) => {
      const detail = (event as CustomEvent<boolean>).detail;
      setSettings((previous) => {
        const next = { ...previous, maintenance: detail };
        return detail ? { ...next, caching: false } : next;
      });
    };

    maintenanceSwitch.addEventListener('pwc-change', handleMaintenanceChange);
    return () => {
      maintenanceSwitch.removeEventListener('pwc-change', handleMaintenanceChange);
    };
  }, [isSubmitting, settings.maintenance]);

  useEffect(() => {
    const cachingSwitch = cachingSwitchRef.current;
    if (!cachingSwitch) {
      return;
    }

    cachingSwitch.checked = settings.caching;
    cachingSwitch.disabled = isSubmitting || settings.maintenance;

    const handleCachingChange = (event: Event) => {
      const detail = (event as CustomEvent<boolean>).detail;
      setSettings((previous) => ({ ...previous, caching: detail }));
    };

    cachingSwitch.addEventListener('pwc-change', handleCachingChange);
    return () => {
      cachingSwitch.removeEventListener('pwc-change', handleCachingChange);
    };
  }, [isSubmitting, settings.caching, settings.maintenance]);

  useEffect(() => {
    const button = submitButtonRef.current;
    if (!button) {
      return;
    }

    button.label = isSubmitting ? 'Applying...' : 'Apply Changes';
    button.disabled = !hasChanges || isSubmitting;
    button.variant = 'primary';
  }, [hasChanges, isSubmitting]);

  useEffect(() => {
    const formElement = formRef.current;
    if (!formElement) {
      return;
    }

    const handleSubmit = (event: Event) => {
      const submitDetail = (event as CustomEvent<PwcSubmitDetail>).detail;
      if (!submitDetail || isSubmitting) {
        return;
      }

      setIsSubmitting(true);
      setTimeout(() => {
        submitDetail.success();
        setIsSubmitting(false);
        setHasChanges(false);
      }, 2000);
    };

    formElement.addEventListener('pwc-submit', handleSubmit as EventListener);
    return () => {
      formElement.removeEventListener('pwc-submit', handleSubmit as EventListener);
    };
  }, [isSubmitting]);

  return (
    <pwc-form ref={formRef}>
      <pwc-switch
        ref={maintenanceSwitchRef}
        name="maintenance"
        label="Maintenance Mode"
        hint="Enable maintenance mode (disables caching automatically)"
      ></pwc-switch>

      <pwc-switch
        ref={cachingSwitchRef}
        name="caching"
        label="Application Caching"
        hint="Enable caching for improved performance"
      ></pwc-switch>

      <pwc-button
        ref={submitButtonRef}
        type="submit"
        style={{ marginTop: '16px' }}
      ></pwc-button>
    </pwc-form>
  );
}
import { Component, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import "@progress-i360/progress-web-components/switch";

@Component({
  selector: 'switch-demo',
  template: `
    <pwc-switch 
      label="Dark Mode" 
      name="darkMode"
      [value]="preferences.darkMode"
      hint="Switch between light and dark themes"
      (pwc-change)="toggleDarkMode($event)">
    </pwc-switch>

    <pwc-switch 
      label="Email Notifications" 
      name="emailNotifications"
      [value]="preferences.emailNotifications"
      required
      hint="Receive important account updates via email"
      (pwc-change)="toggleEmailNotifications($event)">
    </pwc-switch>

    <pwc-switch 
      label="Push Notifications" 
      name="pushNotifications"
      [value]="preferences.pushNotifications"
      [disabled]="!preferences.emailNotifications"
      hint="Browser notifications for real-time updates"
      (pwc-change)="togglePushNotifications($event)">
    </pwc-switch>

    <pwc-switch 
      label="Auto-save" 
      name="autoSave"
      [value]="preferences.autoSave"
      hint="Automatically save changes as you work"
      (pwc-change)="toggleAutoSave($event)">
    </pwc-switch>

    <pwc-switch 
      label="Beta Features" 
      name="betaFeatures"
      [value]="preferences.betaFeatures"
      hint="Access experimental features (may be unstable)"
      (pwc-change)="toggleBetaFeatures($event)">
    </pwc-switch>

    <div class="preferences-summary" *ngIf="hasActivePreferences">
      <p>Active features: {{ getActiveFeatures().join(', ') }}</p>
      <p>Settings saved: {{ settingsSaved ? 'Yes' : 'Pending...' }}</p>
    </div>
  `,
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class SwitchDemo {
  preferences = {
    darkMode: false,
    emailNotifications: true,
    pushNotifications: false,
    autoSave: true,
    betaFeatures: false
  };

  settingsSaved = true;

  get hasActivePreferences() {
    return Object.values(this.preferences).some(value => value);
  }

  toggleDarkMode(event: CustomEvent<boolean>) {
    this.preferences.darkMode = event.detail;
    this.saveSettings();
  }

  toggleEmailNotifications(event: CustomEvent<boolean>) {
    this.preferences.emailNotifications = event.detail;

    // Disable push notifications if email is disabled
    if (!event.detail) {
      this.preferences.pushNotifications = false;
    }

    this.saveSettings();
  }

  togglePushNotifications(event: CustomEvent<boolean>) {
    this.preferences.pushNotifications = event.detail;
    this.saveSettings();
  }

  toggleAutoSave(event: CustomEvent<boolean>) {
    this.preferences.autoSave = event.detail;
    this.saveSettings();
  }

  toggleBetaFeatures(event: CustomEvent<boolean>) {
    this.preferences.betaFeatures = event.detail;
    this.saveSettings();
  }

  getActiveFeatures(): string[] {
    const features = [];
    if (this.preferences.darkMode) features.push('Dark Mode');
    if (this.preferences.emailNotifications) features.push('Email Notifications');
    if (this.preferences.pushNotifications) features.push('Push Notifications');
    if (this.preferences.autoSave) features.push('Auto-save');
    if (this.preferences.betaFeatures) features.push('Beta Features');
    return features;
  }

  private saveSettings() {
    this.settingsSaved = false;

    // Simulate API call
    setTimeout(() => {
      this.settingsSaved = true;
      console.log('Settings saved:', this.preferences);
    }, 500);
  }
}
import '@progress-i360/progress-web-components/switch';

// Create toggle switch for notification preferences
const notificationSwitch = document.createElement('pwc-switch');
notificationSwitch.label = 'Enable Notifications';
notificationSwitch.name = 'notifications';
notificationSwitch.checked = false;

notificationSwitch.addEventListener('pwc-change', (event) => {
  const isEnabled = event.detail;
  console.log('Notifications:', isEnabled ? 'enabled' : 'disabled');
});

document.body.appendChild(notificationSwitch);

Usage Patterns

  • User Preference Toggles - Settings panels with binary preferences for themes, notifications, and feature enablement
  • Feature Control Switches - Dashboard and configuration interfaces where features can be activated or deactivated
  • Privacy and Security Settings - User control panels for privacy preferences, tracking consent, and security features
  • System Configuration Toggles - Administrative interfaces for system-level feature management and operational controls

Best Practices

Content Strategy Guidelines

  • Clear Binary Labels - Use descriptive labels that clearly communicate the on/off state and functionality being controlled
  • Informative Hint Text - Provide context about what the switch controls and the impact of enabling or disabling the feature
  • Consistent State Communication - Ensure visual feedback clearly indicates current state with appropriate color coding and iconography
  • Logical Grouping - Organize related switches into sections with clear headings for better user comprehension and navigation

Performance Optimization

  • Efficient State Management - Optimize boolean state updates and form synchronization for smooth user interactions
  • Icon Loading Strategy - Ensure checkmark, close, and radio-button icons are properly loaded and cached for consistent visual feedback
  • Animation Performance - Implement smooth thumb transitions and state changes without impacting overall application performance
  • Loading State Integration - Properly handle skeleton rendering during form initialization and switch component loading

Integration Architecture

  • Form Context Coordination - Integrate seamlessly with PWC form systems for validation, submission, and error handling workflows
  • Label Container Integration - Leverage label container architecture for consistent styling, hint display, and required field indicators
  • Accessibility Standards - Ensure proper ARIA attributes, keyboard navigation, and screen reader compatibility for switch interactions
  • Design Token Usage - Leverage PWC design tokens for consistent styling, spacing, and color usage across switch components

Common Use Cases

Data Table Headers

  • Column visibility toggles with switches for showing or hiding specific data columns and table features
  • Real-time update switches for enabling live data refresh and automatic table updates
  • Export feature toggles for controlling data export options and formatting preferences

Search Result Sections

  • Filter activation switches for enabling or disabling specific search filters and refinement options
  • View mode toggles for switching between different result presentation styles and layouts
  • Auto-complete switches for controlling search suggestion behavior and user assistance features

Dashboard Widget Headers

  • Widget activation switches for enabling or disabling specific dashboard components and data displays
  • Auto-refresh toggles for controlling automatic data updates and real-time synchronization
  • Notification switches for managing widget-specific alerts and status updates

Troubleshooting

Common Issues

Actions Not Triggering

Symptoms: Switch doesn't toggle states, change events don't fire, or value updates don't register properly

Solutions:

  • Verify that input element is properly connected and change/input event handlers are correctly bound to functional callbacks

  • Check that disabled state doesn't prevent legitimate user interactions or interfere with switch toggling mechanism

  • Ensure form context integration doesn't block switch state updates or prevent proper value synchronization

Actions Not Visible

Symptoms: Switch doesn't render correctly, icons don't appear, or visual states don't update properly

Solutions:

  • Confirm that checkmark, close, and radio-button icons are loaded and available in your icon system configuration

  • Check that switch styling renders correctly with proper background colors, thumb positioning, and state transitions

  • Verify that label container integration displays properly with appropriate spacing, alignment, and visual hierarchy

Layout Issues

Symptoms: Switch appears with incorrect spacing, alignment, or positioning within form layouts and containers

Solutions:

  • Use appropriate container styling to accommodate switch component dimensions and interactive area requirements

  • Check that label container layout works correctly with switch positioning and provides adequate spacing for touch targets

  • Ensure responsive design patterns accommodate switch components across different screen sizes and device orientations

Icon Problems

Symptoms: Switch icons don't display correctly, appear with wrong colors, or don't update based on state changes

Solutions:

  • Verify that icon color theming works correctly for normal, disabled, and checked states with appropriate contrast ratios

  • Check that icon sizing is appropriate for switch dimensions and provides clear visual communication of state

  • Ensure icon transitions and state changes function smoothly during switch interactions and value updates

Implementation Support

  • State Management Patterns - Best practices for handling boolean values, form integration, and switch group coordination
  • Accessibility Compliance - WCAG guidelines implementation, keyboard navigation patterns, and screen reader optimization for switch controls
  • Visual Design Integration - Switch styling strategies, icon usage patterns, and consistent visual feedback for different application contexts

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.