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.