Skip to content

Pending Review

Pending Review from User Experience (UX)

Radio Section Component

Overview

The PWC Radio Section component provides conditional form section rendering based on radio button selections, enabling dynamic form structures that adapt to user choices. Built on the InputBase foundation with integrated Radio component functionality, it offers slot-based content organization, automatic child component state management, and seamless form integration for complex multi-step workflows.

Core Capabilities

  • Conditional Content Rendering - Dynamic slot-based rendering that shows/hides form sections based on radio selection state
  • Integrated Radio Controls - Built-in radio button functionality with flexible orientation and option management for seamless user interaction
  • Automatic State Management - Intelligent child component enabling/disabling based on parent selection and form context
  • Slot-Based Architecture - Named slot system allowing flexible content organization and dynamic section composition
  • Form Integration - Seamless connection with PWC form systems for validation, submission, and state synchronization
  • Progressive Disclosure - Reveal additional form fields and options based on user selections for improved user experience

When to use Radio Section:

  • Multi-step forms requiring conditional field display based on initial selection choices and user workflow paths
  • Configuration interfaces where specific options reveal additional settings and customization parameters
  • Survey and questionnaire forms with branching logic that adapts to user responses and preferences
  • Settings panels with dependent options that only appear when parent selections are made and validated

When not to use Radio Section:

  • Simple radio selections that don't require additional form fields or conditional content rendering
  • Complex branching logic requiring multiple levels of nested conditions beyond single radio selection
  • Static forms where all fields should remain visible regardless of user selections and input state

Basic Implementation

import { PwcCheckbox, PwcInput, PwcRadioSection } from "@progress-i360/pwc-react";
import { useMemo, useState } from 'react';

function ContactPreferencesForm() {
  const [contactMethod, setContactMethod] = useState('email');

  const contactOptions = useMemo(
    () => [
      { label: 'Email', value: 'email' },
      { label: 'Phone', value: 'phone' }
    ],
    []
  );

  return (
    <PwcRadioSection
      name="contactMethod"
      label="Contact Method"
      hint="How would you like to be contacted?"
      options={contactOptions}
      orientation="horizontal"
      value={contactMethod}
      required
      onChange={(event) => setContactMethod(event.detail)}
    >
      <PwcInput
        name="email"
        label="Email Address"
        type="email"
        placeholder="you@example.com"
        required
        slot="email"
      />
      <PwcCheckbox
        name="marketingEmails"
        label="Receive marketing emails"
        slot="email"
      />
      <PwcInput
        name="phone"
        label="Phone Number"
        type="tel"
        placeholder="+1 (555) 123-4567"
        required
        slot="phone"
      />
    </PwcRadioSection>
  );
}
import { useEffect, useMemo, useRef, useState } from 'react';
import '@progress-i360/progress-web-components/button';
import '@progress-i360/progress-web-components/checkbox';
import '@progress-i360/progress-web-components/form';
import '@progress-i360/progress-web-components/radio-section';
import '@progress-i360/progress-web-components/select';

type RadioOption = { label: string; value: string };

type RadioSectionElement = HTMLElement & {
  options?: RadioOption[];
  value?: string;
  disabled?: boolean;
  orientation?: 'horizontal' | 'vertical';
};

type SelectElement = HTMLElement & {
  options?: RadioOption[];
  value?: string;
  disabled?: boolean;
  required?: boolean;
};

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

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

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

function ProjectConfigurationWizard() {
  const formRef = useRef<HTMLElement | null>(null);
  const radioSectionRef = useRef<RadioSectionElement | null>(null);
  const frameworkRef = useRef<SelectElement | null>(null);
  const pwaCheckboxRef = useRef<CheckboxElement | null>(null);
  const pushCheckboxRef = useRef<CheckboxElement | null>(null);
  const submitButtonRef = useRef<ButtonElement | null>(null);

  const projectTypeOptions = useMemo<RadioOption[]>(
    () => [
      { label: 'Web Application', value: 'web' },
      { label: 'Mobile App', value: 'mobile' }
    ],
    []
  );

  const frameworkOptions = useMemo<RadioOption[]>(
    () => [
      { label: 'React', value: 'react' },
      { label: 'Vue.js', value: 'vue' }
    ],
    []
  );

  const [config, setConfig] = useState({
    projectType: 'web',
    framework: 'react',
    enablePwa: false,
    enablePush: false
  });

  const [isSubmitting, setIsSubmitting] = useState(false);

  useEffect(() => {
    const section = radioSectionRef.current;
    if (!section) {
      return;
    }

    section.options = projectTypeOptions;
    section.value = config.projectType;
    section.disabled = isSubmitting;
    section.orientation = 'horizontal';

    const handleChange = (event: Event) => {
      const detail = (event as CustomEvent<string>).detail;
      setConfig((previous) => ({ ...previous, projectType: detail }));
    };

    section.addEventListener('pwc-change', handleChange);
    return () => {
      section.removeEventListener('pwc-change', handleChange);
    };
  }, [config.projectType, isSubmitting, projectTypeOptions]);

  useEffect(() => {
    const selectElement = frameworkRef.current;
    if (!selectElement) {
      return;
    }

    selectElement.options = frameworkOptions;
    selectElement.value = config.framework;
    selectElement.disabled = isSubmitting || config.projectType !== 'web';
    selectElement.required = true;

    const handleFrameworkChange = (event: Event) => {
      const detail = (event as CustomEvent<string>).detail;
      setConfig((previous) => ({ ...previous, framework: detail }));
    };

    selectElement.addEventListener('pwc-change', handleFrameworkChange);
    return () => {
      selectElement.removeEventListener('pwc-change', handleFrameworkChange);
    };
  }, [config.framework, config.projectType, frameworkOptions, isSubmitting]);

  useEffect(() => {
    const pwaCheckbox = pwaCheckboxRef.current;
    if (!pwaCheckbox) {
      return;
    }

    pwaCheckbox.checked = config.enablePwa;
    pwaCheckbox.disabled = isSubmitting || config.projectType !== 'web';

    const handlePwaChange = (event: Event) => {
      const detail = (event as CustomEvent<boolean>).detail;
      setConfig((previous) => ({ ...previous, enablePwa: detail }));
    };

    pwaCheckbox.addEventListener('pwc-change', handlePwaChange);
    return () => {
      pwaCheckbox.removeEventListener('pwc-change', handlePwaChange);
    };
  }, [config.enablePwa, config.projectType, isSubmitting]);

  useEffect(() => {
    const pushCheckbox = pushCheckboxRef.current;
    if (!pushCheckbox) {
      return;
    }

    pushCheckbox.checked = config.enablePush;
    pushCheckbox.disabled = isSubmitting || config.projectType !== 'mobile';

    const handlePushChange = (event: Event) => {
      const detail = (event as CustomEvent<boolean>).detail;
      setConfig((previous) => ({ ...previous, enablePush: detail }));
    };

    pushCheckbox.addEventListener('pwc-change', handlePushChange);
    return () => {
      pushCheckbox.removeEventListener('pwc-change', handlePushChange);
    };
  }, [config.enablePush, config.projectType, isSubmitting]);

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

    button.label = isSubmitting ? 'Creating...' : 'Create Project';
    button.disabled = isSubmitting;
    button.variant = 'primary';
  }, [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);
      }, 2000);
    };

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

  return (
    <pwc-form ref={formRef}>
      <pwc-radio-section
        ref={radioSectionRef}
        name="projectType"
        label="Project Type"
        hint="Select the type of project you're creating"
        required
      >
        <pwc-select
          ref={frameworkRef}
          name="webFramework"
          label="Frontend Framework"
          slot="web"
        ></pwc-select>
        <pwc-checkbox
          ref={pwaCheckboxRef}
          name="pwa"
          label="Enable Progressive Web App features"
          slot="web"
        ></pwc-checkbox>
        <pwc-checkbox
          ref={pushCheckboxRef}
          name="pushNotifications"
          label="Include push notifications"
          slot="mobile"
        ></pwc-checkbox>
      </pwc-radio-section>

      <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/checkbox";
import "@progress-i360/progress-web-components/form-section";
import "@progress-i360/progress-web-components/input";
import "@progress-i360/progress-web-components/radio-section";

@Component({
  selector: 'radio-section-demo',
  template: `
    <pwc-form-section heading="Contact Information" hint="How would you like us to reach you?">
      <pwc-radio-section
        label="Preferred Contact Method"
        name="contactMethod"
        [options]="contactOptions"
        [value]="selectedContact"
        orientation="horizontal"
        required
        (pwc-change)="updateContact($event)">

        <pwc-input
          slot="email"
          name="emailAddress"
          label="Email Address"
          type="email"
          required
          hint="We'll send updates to this address"
          (pwc-change)="updateEmail($event)">
        </pwc-input>

        <pwc-checkbox
          slot="email"
          name="emailNewsletter"
          label="Subscribe to newsletter"
          (pwc-change)="updateEmailPrefs($event)">
        </pwc-checkbox>

        <pwc-input
          slot="phone"
          name="phoneNumber"
          label="Phone Number"
          type="tel"
          required
          hint="Include country code for international numbers"
          (pwc-change)="updatePhone($event)">
        </pwc-input>

        <pwc-checkbox
          slot="phone"
          name="smsUpdates"
          label="Receive SMS updates"
          (pwc-change)="updateSmsPrefs($event)">
        </pwc-checkbox>

        <pwc-input
          slot="mail"
          name="streetAddress"
          label="Street Address"
          required
          (pwc-change)="updateAddress($event)">
        </pwc-input>

        <pwc-input
          slot="mail"
          name="city"
          label="City"
          required
          (pwc-change)="updateCity($event)">
        </pwc-input>

      </pwc-radio-section>

      <div class="contact-summary" *ngIf="hasContactInfo">
        <p>Contact method: {{ selectedContact }}</p>
        <p>Form completion: {{ getCompletionStatus() }}%</p>
      </div>
    </pwc-form-section>
  `,
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class RadioSectionDemo {
  selectedContact = 'email';
  contactData = {
    email: '',
    phone: '',
    address: '',
    city: ''
  };
  preferences = {
    newsletter: false,
    sms: false
  };

  contactOptions = [
    { label: 'Email', value: 'email' },
    { label: 'Phone', value: 'phone' },
    { label: 'Mail', value: 'mail' }
  ];

  get hasContactInfo() {
    return this.selectedContact !== '';
  }

  updateContact(event: CustomEvent<string>) {
    this.selectedContact = event.detail;
  }

  updateEmail(event: CustomEvent<string>) {
    this.contactData.email = event.detail;
  }

  updatePhone(event: CustomEvent<string>) {
    this.contactData.phone = event.detail;
  }

  updateAddress(event: CustomEvent<string>) {
    this.contactData.address = event.detail;
  }

  updateCity(event: CustomEvent<string>) {
    this.contactData.city = event.detail;
  }

  updateEmailPrefs(event: CustomEvent<boolean>) {
    this.preferences.newsletter = event.detail;
  }

  updateSmsPrefs(event: CustomEvent<boolean>) {
    this.preferences.sms = event.detail;
  }

  getCompletionStatus(): number {
    const requiredFields = this.getRequiredFieldsForMethod();
    const completedFields = requiredFields.filter(field => this.contactData[field as keyof typeof this.contactData]);
    return Math.round((completedFields.length / requiredFields.length) * 100);
  }

  private getRequiredFieldsForMethod(): string[] {
    switch (this.selectedContact) {
      case 'email':
        return ['email'];
      case 'phone':
        return ['phone'];
      case 'mail':
        return ['address', 'city'];
      default:
        return [];
    }
  }
}
import '@progress-i360/progress-web-components/checkbox';
import '@progress-i360/progress-web-components/input';
import '@progress-i360/progress-web-components/radio-section';

// Create radio section with conditional content
const radioSection = document.createElement('pwc-radio-section');
radioSection.label = 'Contact Method';
radioSection.name = 'contactMethod';
radioSection.orientation = 'horizontal';
radioSection.required = true;

// Define radio options
const options = [
  { label: 'Email', value: 'email' },
  { label: 'Phone', value: 'phone' }
];
radioSection.options = options;

// Create conditional content for each option
const emailInput = document.createElement('pwc-input');
emailInput.label = 'Email Address';
emailInput.slot = 'email';

const phoneInput = document.createElement('pwc-input');
phoneInput.label = 'Phone Number';
phoneInput.slot = 'phone';

radioSection.append(emailInput, phoneInput);

radioSection.addEventListener('pwc-change', (event) => {
  console.log('Contact method changed:', event.detail);
});

document.body.appendChild(radioSection);

Usage Patterns

  • Progressive Form Disclosure - Multi-step configuration where initial selections determine which additional fields become available and required
  • Conditional Settings Panels - User preference interfaces where specific choices reveal related customization options and advanced settings
  • Survey Branching Logic - Questionnaire forms that adapt based on responses to display relevant follow-up questions and data collection fields
  • Wizard-Style Workflows - Setup processes where each selection guides users through appropriate next steps and configuration options

Best Practices

Content Strategy Guidelines

  • Logical Option Grouping - Organize radio options in intuitive sequences that match user mental models and decision-making processes
  • Clear Conditional Relationships - Ensure users understand which selections will reveal additional fields and configuration requirements
  • Consistent Slot Naming - Use descriptive, consistent slot names that clearly map to their corresponding radio option values
  • Progressive Complexity - Start with simple choices and gradually reveal more detailed options to avoid overwhelming users initially

Performance Optimization

  • Efficient Child Management - Optimize slot visibility toggling and child component state management for smooth user interactions
  • Lazy Field Rendering - Consider rendering conditional fields only when their parent options are selected to improve initial load performance
  • State Synchronization - Ensure form state updates efficiently when radio selections change and child fields are shown or hidden
  • Memory Management - Properly clean up event listeners and component references when sections are dynamically shown or hidden

Integration Architecture

  • Form Context Coordination - Integrate seamlessly with PWC form systems for validation, submission, and error handling across all conditional sections
  • Slot Architecture Planning - Design slot-based content organization that scales well with complex branching logic and nested conditions
  • Accessibility Standards - Ensure proper ARIA attributes, screen reader announcements, and keyboard navigation for dynamic content changes
  • Design Token Usage - Leverage PWC design tokens for consistent styling, spacing, and visual hierarchy across radio sections and conditional content

Common Use Cases

Data Table Headers

  • Column configuration sections with radio selections for sort order that reveal additional sorting and filtering options
  • View mode selection with conditional display settings and customization fields based on chosen presentation style
  • Export configuration with format selection that shows relevant options for file types, compression, and data inclusion settings

Search Result Sections

  • Search scope selection with conditional filters and refinement options specific to chosen content types and data sources
  • Result sorting with additional configuration fields for custom sort criteria and advanced ranking parameters
  • Time range filtering with conditional date picker fields and preset options based on selected temporal scope

Dashboard Widget Headers

  • Widget type selection with conditional configuration fields for chart types, data sources, and visualization parameters
  • Data aggregation options with specific calculation fields and grouping criteria based on selected aggregation methods
  • Refresh interval configuration with conditional scheduling options and notification preferences for different update frequencies

Troubleshooting

Common Issues

Actions Not Triggering

Symptoms: Radio selection doesn't reveal conditional content or child components don't update properly when sections change

Solutions:

  • Verify that slot names in child components exactly match the radio option values for proper content mapping

  • Check that radio change events are properly handled and trigger the slot visibility toggling mechanism

  • Ensure child component state management updates correctly when parent radio selections change and sections are shown or hidden

Actions Not Visible

Symptoms: Conditional form sections don't appear or disappear correctly when radio selections are made

Solutions:

  • Confirm that child components are properly assigned to named slots that correspond to radio option values

  • Check that slot rendering logic correctly shows and hides content based on current radio selection state

  • Verify that disabled states are properly managed for both visible and hidden child components

Layout Issues

Symptoms: Conditional sections appear with incorrect spacing, alignment, or layout disruption when content changes

Solutions:

  • Use appropriate gap and padding values from design system tokens for consistent spacing between radio section elements

  • Check that flex layout properties accommodate dynamic content changes without causing layout shifts or alignment problems

  • Ensure responsive design patterns work correctly when conditional content is shown or hidden across different screen sizes

Icon Problems

Symptoms: Radio buttons don't display correctly or conditional content indicators don't appear with proper visual feedback

Solutions:

  • Verify that radio button styling and selection indicators work correctly within the radio section component context

  • Check that any icons or visual indicators for conditional content display properly and update based on selection state

  • Ensure focus indicators and selection states work correctly for both radio controls and revealed conditional content

Implementation Support

  • Conditional Logic Design - Best practices for organizing complex branching logic, slot management, and progressive disclosure patterns
  • Accessibility Compliance - WCAG guidelines implementation for dynamic content, screen reader announcements, and keyboard navigation in conditional forms
  • Form Integration Patterns - Strategies for integrating radio sections with validation systems, form submission, and error handling across conditional content

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.