now-design-organisms
Version:
Organism components for the Now Design System
803 lines (784 loc) • 24.8 kB
JavaScript
import React, { useState } from 'react';
import LeftPanel from './LeftPanel';
import { MetalcloudMeltingFurnaceLine, SystemAddFill, ArrowsArrowDownSLine, WeatherSunLine, UserLine, SettingsLine, FileTextLine, HomeLine, ChartLine, NotificationLine, ShieldLine, DatabaseLine, CloudLine, ToolsLine, AlertLine, CheckLine } from 'now-design-icons';
export default {
title: 'Organisms/LeftPanel',
component: LeftPanel,
parameters: {
layout: 'fullscreen',
docs: {
description: {
component: `
# LeftPanel Organism Component
A comprehensive left panel navigation organism that provides a complete layout for filtering and navigation using the AccordionSelectableListContainer.
## Features
- **Organism-Level Component**: Combines multiple molecules for complex UI patterns
- **Clean API**: Minimal props focused on functionality
- **Flexible Styling**: All layout and visual properties via \`style\` prop
- **Responsive Design**: Mobile-first approach with breakpoints
- **Theme Integration**: Uses design tokens for consistent theming
- **Accessibility**: Proper focus states and semantic markup
- **Developer Friendly**: Helpful warnings for missing data
## Usage
\`\`\`jsx
import { LeftPanel } from 'now-design-organisms';
<LeftPanel
items={items}
onItemSelect={(itemId, parentAccordionId, selectedItemLabel) => {
console.log('Selected:', itemId, parentAccordionId, selectedItemLabel);
}}
selectedItem="melting-furnace"
style={{ width: '280px', backgroundColor: '#ffffff' }}
/>
\`\`\`
`
}
}
},
argTypes: {
items: {
control: {
type: 'object'
},
description: 'Array of accordion configurations (required)',
table: {
type: {
summary: 'Array<AccordionConfig>'
},
defaultValue: {
summary: '[]'
},
category: 'Data'
}
},
onItemSelect: {
action: 'itemSelected',
description: 'Enhanced callback when any item is selected. Receives: (itemId, parentAccordionId, selectedItemLabel)',
table: {
type: {
summary: 'function'
},
category: 'Events'
}
},
selectedItem: {
control: {
type: 'text'
},
description: 'Selected item ID to highlight on initial render',
table: {
type: {
summary: 'string'
},
category: 'Data'
}
},
logo: {
control: {
type: 'object'
},
description: 'Custom logo component or element to replace LogoMetalCloud',
table: {
type: {
summary: 'ReactElement|Component'
},
category: 'Customization'
}
},
logoSize: {
control: {
type: 'object'
},
description: 'Logo dimensions {width, height}',
table: {
type: {
summary: '{width: number, height: number}'
},
defaultValue: {
summary: '{width: 64, height: 32}'
},
category: 'Customization'
}
},
onLogoClick: {
action: 'logoClicked',
description: 'Callback when logo is clicked',
table: {
type: {
summary: 'function'
},
category: 'Events'
}
},
contentClassName: {
control: {
type: 'text'
},
description: 'Additional CSS classes for content container',
table: {
type: {
summary: 'string'
},
defaultValue: {
summary: "''"
},
category: 'Styling'
}
},
contentStyle: {
control: {
type: 'object'
},
description: 'Additional inline styles for content container',
table: {
type: {
summary: 'object'
},
defaultValue: {
summary: '{}'
},
category: 'Styling'
}
},
className: {
control: {
type: 'text'
},
description: 'Additional CSS classes for the main container',
table: {
type: {
summary: 'string'
},
defaultValue: {
summary: "''"
},
category: 'Styling'
}
},
style: {
control: {
type: 'object'
},
description: 'Additional inline styles (includes layout, visual, and positioning styles)',
table: {
type: {
summary: 'object'
},
defaultValue: {
summary: '{}'
},
category: 'Styling'
}
}
}
};
// Sample accordion data for stories
const sampleAccordionData = [{
id: 'production',
triggerLabel: 'Production',
triggerIcon: MetalcloudMeltingFurnaceLine,
items: [{
id: 'melting-furnace',
label: 'Melting Furnace',
icon: MetalcloudMeltingFurnaceLine
}, {
id: 'casting-line',
label: 'Casting Line',
icon: SystemAddFill
}, {
id: 'quality-check',
label: 'Quality Check',
icon: WeatherSunLine
}]
}, {
id: 'management',
triggerLabel: 'Management',
triggerIcon: UserLine,
items: [{
id: 'user-management',
label: 'User Management',
icon: UserLine
}, {
id: 'settings',
label: 'Settings',
icon: SettingsLine
}, {
id: 'reports',
label: 'Reports',
icon: FileTextLine
}]
}, {
id: 'system',
triggerLabel: 'System',
triggerIcon: SystemAddFill,
items: [{
id: 'configuration',
label: 'Configuration',
icon: SystemAddFill
}, {
id: 'maintenance',
label: 'Maintenance',
icon: MetalcloudMeltingFurnaceLine
}, {
id: 'backup',
label: 'Backup',
icon: WeatherSunLine
}]
}];
const Template = args => /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
height: '100vh'
}
}, /*#__PURE__*/React.createElement(LeftPanel, args), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
padding: '20px',
backgroundColor: '#f5f5f5'
}
}, /*#__PURE__*/React.createElement("h2", null, "Main Content Area"), /*#__PURE__*/React.createElement("p", null, "This is the main content area that would be displayed when items are selected from the left panel.")));
// Default story with basic configuration
export const Default = Template.bind({});
Default.args = {
items: sampleAccordionData,
style: {
width: '280px',
backgroundColor: '#ffffff',
borderRight: '1px solid #e0e0e0'
}
};
// Compact version
export const Compact = Template.bind({});
Compact.args = {
items: sampleAccordionData,
logoSize: {
width: 48,
height: 24
},
style: {
width: '220px',
backgroundColor: '#f8f9fa',
borderRight: '1px solid #dee2e6'
}
};
// Wide version
export const Wide = Template.bind({});
Wide.args = {
items: sampleAccordionData,
logoSize: {
width: 80,
height: 40
},
style: {
width: '320px',
backgroundColor: '#ffffff',
borderRight: '1px solid #e0e0e0'
}
};
// With default selection
export const WithDefaultSelection = Template.bind({});
WithDefaultSelection.args = {
items: sampleAccordionData,
selectedItem: 'melting-furnace',
style: {
width: '280px',
backgroundColor: '#ffffff',
borderRight: '1px solid #e0e0e0'
}
};
// Interactive with state management
export const Interactive = () => {
const [selectedItemId, setSelectedItemId] = useState(null);
const [selectedAccordionId, setSelectedAccordionId] = useState(null);
const [selectedItemLabel, setSelectedItemLabel] = useState(null);
const handleItemSelect = (itemId, parentAccordionId, selectedItemLabel) => {
setSelectedItemId(itemId);
setSelectedAccordionId(parentAccordionId);
setSelectedItemLabel(selectedItemLabel);
};
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
height: '100vh'
}
}, /*#__PURE__*/React.createElement(LeftPanel, {
items: sampleAccordionData,
onItemSelect: handleItemSelect,
selectedItem: "melting-furnace",
style: {
width: '280px',
backgroundColor: '#ffffff',
borderRight: '1px solid #e0e0e0'
}
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
padding: '20px',
backgroundColor: '#f5f5f5'
}
}, /*#__PURE__*/React.createElement("h2", null, "Interactive Example"), /*#__PURE__*/React.createElement("p", null, "This example shows the left panel with interactive state management."), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: '20px',
padding: '16px',
backgroundColor: '#ffffff',
borderRadius: '8px'
}
}, /*#__PURE__*/React.createElement("h3", null, "Selection Details:"), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("strong", null, "Selected Item ID:"), " ", selectedItemId || 'None'), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("strong", null, "Parent Accordion ID:"), " ", selectedAccordionId || 'None'), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("strong", null, "Selected Item Label:"), " ", selectedItemLabel || 'None'))));
};
// Complex data structure
export const ComplexData = () => {
const complexAccordionData = [{
id: 'manufacturing',
triggerLabel: 'Manufacturing',
triggerIcon: MetalcloudMeltingFurnaceLine,
items: [{
id: 'furnace-1',
label: 'Furnace Line 1',
icon: MetalcloudMeltingFurnaceLine
}, {
id: 'furnace-2',
label: 'Furnace Line 2',
icon: MetalcloudMeltingFurnaceLine
}, {
id: 'casting-1',
label: 'Casting Unit 1',
icon: SystemAddFill
}, {
id: 'casting-2',
label: 'Casting Unit 2',
icon: SystemAddFill
}, {
id: 'quality-1',
label: 'Quality Station 1',
icon: WeatherSunLine
}, {
id: 'quality-2',
label: 'Quality Station 2',
icon: WeatherSunLine
}]
}, {
id: 'operations',
triggerLabel: 'Operations',
triggerIcon: UserLine,
items: [{
id: 'users',
label: 'User Management',
icon: UserLine
}, {
id: 'roles',
label: 'Role Management',
icon: UserLine
}, {
id: 'permissions',
label: 'Permissions',
icon: SettingsLine
}, {
id: 'audit',
label: 'Audit Logs',
icon: FileTextLine
}]
}, {
id: 'analytics',
triggerLabel: 'Analytics',
triggerIcon: ChartLine,
items: [{
id: 'reports',
label: 'Reports',
icon: FileTextLine
}, {
id: 'dashboards',
label: 'Dashboards',
icon: ChartLine
}, {
id: 'metrics',
label: 'Metrics',
icon: WeatherSunLine
}, {
id: 'alerts',
label: 'Alerts',
icon: AlertLine
}]
}, {
id: 'system',
triggerLabel: 'System',
triggerIcon: SystemAddFill,
items: [{
id: 'config',
label: 'Configuration',
icon: SystemAddFill
}, {
id: 'maintenance',
label: 'Maintenance',
icon: ToolsLine
}, {
id: 'backup',
label: 'Backup & Restore',
icon: DatabaseLine
}, {
id: 'updates',
label: 'System Updates',
icon: CloudLine
}, {
id: 'logs',
label: 'System Logs',
icon: FileTextLine
}, {
id: 'security',
label: 'Security',
icon: ShieldLine
}]
}];
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
height: '100vh'
}
}, /*#__PURE__*/React.createElement(LeftPanel, {
items: complexAccordionData,
selectedItem: "furnace-1",
style: {
width: '300px',
backgroundColor: '#ffffff',
borderRight: '1px solid #e0e0e0'
}
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
padding: '20px',
backgroundColor: '#f5f5f5'
}
}, /*#__PURE__*/React.createElement("h2", null, "Complex Navigation Example"), /*#__PURE__*/React.createElement("p", null, "This example shows a more complex navigation structure with multiple accordions and items."), /*#__PURE__*/React.createElement("p", null, "The data structure includes 4 accordions with 6 items each, demonstrating the component's ability to handle complex navigation hierarchies.")));
};
// Custom styling examples
export const CustomStyling = () => {
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
height: '100vh'
}
}, /*#__PURE__*/React.createElement(LeftPanel, {
items: sampleAccordionData,
style: {
width: '280px',
backgroundColor: '#2c3e50',
borderRight: '2px solid #34495e',
borderRadius: '0 8px 8px 0',
boxShadow: '2px 0 10px rgba(0,0,0,0.1)',
padding: '16px'
},
contentStyle: {
backgroundColor: 'transparent'
}
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
padding: '20px',
backgroundColor: '#ecf0f1'
}
}, /*#__PURE__*/React.createElement("h2", null, "Custom Styled Panel"), /*#__PURE__*/React.createElement("p", null, "This example shows the left panel with custom styling including:"), /*#__PURE__*/React.createElement("ul", null, /*#__PURE__*/React.createElement("li", null, "Dark background color (#2c3e50)"), /*#__PURE__*/React.createElement("li", null, "Custom border radius"), /*#__PURE__*/React.createElement("li", null, "Box shadow for depth"), /*#__PURE__*/React.createElement("li", null, "Custom padding"))));
};
// With disabled items
export const WithDisabledItems = () => {
const accordionDataWithDisabled = [{
id: 'production',
triggerLabel: 'Production',
triggerIcon: MetalcloudMeltingFurnaceLine,
items: [{
id: 'melting-furnace',
label: 'Melting Furnace',
icon: MetalcloudMeltingFurnaceLine
}, {
id: 'casting-line',
label: 'Casting Line (Disabled)',
icon: SystemAddFill,
disabled: true
}, {
id: 'quality-check',
label: 'Quality Check',
icon: WeatherSunLine
}]
}, {
id: 'management',
triggerLabel: 'Management',
triggerIcon: UserLine,
items: [{
id: 'user-management',
label: 'User Management',
icon: UserLine
}, {
id: 'settings',
label: 'Settings (Disabled)',
icon: SettingsLine,
disabled: true
}, {
id: 'reports',
label: 'Reports',
icon: FileTextLine
}]
}];
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
height: '100vh'
}
}, /*#__PURE__*/React.createElement(LeftPanel, {
items: accordionDataWithDisabled,
style: {
width: '280px',
backgroundColor: '#ffffff',
borderRight: '1px solid #e0e0e0'
}
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
padding: '20px',
backgroundColor: '#f5f5f5'
}
}, /*#__PURE__*/React.createElement("h2", null, "Panel with Disabled Items"), /*#__PURE__*/React.createElement("p", null, "This example shows the left panel with some disabled items that cannot be selected."), /*#__PURE__*/React.createElement("p", null, "Disabled items are visually distinct and non-interactive.")));
};
// With custom logo component (FIXED)
export const WithCustomLogo = () => {
const CustomLogo = ({
width,
height
}) => /*#__PURE__*/React.createElement("div", {
style: {
width: width,
height: height,
backgroundColor: '#3498db',
borderRadius: '8px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
fontSize: '12px',
fontWeight: 'bold',
fontFamily: 'Arial, sans-serif'
}
}, "CUSTOM");
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
height: '100vh'
}
}, /*#__PURE__*/React.createElement(LeftPanel, {
items: sampleAccordionData,
logo: CustomLogo,
logoSize: {
width: 80,
height: 40
},
onLogoClick: () => alert('Custom logo clicked!'),
style: {
width: '280px',
backgroundColor: '#ffffff',
borderRight: '1px solid #e0e0e0'
}
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
padding: '20px',
backgroundColor: '#f5f5f5'
}
}, /*#__PURE__*/React.createElement("h2", null, "Custom Logo Example"), /*#__PURE__*/React.createElement("p", null, "This example shows the left panel with a custom logo component instead of the default LogoMetalCloud."), /*#__PURE__*/React.createElement("p", null, "The custom logo is clickable and demonstrates the flexibility of the logo prop.")));
};
// With logo click handler
export const WithLogoClick = () => {
const [logoClickCount, setLogoClickCount] = useState(0);
const handleLogoClick = () => {
setLogoClickCount(prev => prev + 1);
};
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
height: '100vh'
}
}, /*#__PURE__*/React.createElement(LeftPanel, {
items: sampleAccordionData,
onLogoClick: handleLogoClick,
style: {
width: '280px',
backgroundColor: '#ffffff',
borderRight: '1px solid #e0e0e0'
}
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
padding: '20px',
backgroundColor: '#f5f5f5'
}
}, /*#__PURE__*/React.createElement("h2", null, "Logo Click Handler"), /*#__PURE__*/React.createElement("p", null, "This example demonstrates the logo click functionality."), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: '20px',
padding: '16px',
backgroundColor: '#ffffff',
borderRadius: '8px'
}
}, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("strong", null, "Logo Click Count:"), " ", logoClickCount), /*#__PURE__*/React.createElement("p", null, "Click the logo in the left panel to increment the counter."))));
};
// Empty state (no accordion data)
export const EmptyState = () => {
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
height: '100vh'
}
}, /*#__PURE__*/React.createElement(LeftPanel, {
items: [],
style: {
width: '280px',
backgroundColor: '#ffffff',
borderRight: '1px solid #e0e0e0'
}
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
padding: '20px',
backgroundColor: '#f5f5f5'
}
}, /*#__PURE__*/React.createElement("h2", null, "Empty State"), /*#__PURE__*/React.createElement("p", null, "This example shows the left panel with no accordion data."), /*#__PURE__*/React.createElement("p", null, "Check the browser console for the warning message about empty items.")));
};
// Responsive example with font family
export const Responsive = () => {
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
height: '100vh'
}
}, /*#__PURE__*/React.createElement(LeftPanel, {
items: sampleAccordionData,
style: {
width: '280px',
minWidth: '200px',
maxWidth: '320px',
backgroundColor: '#ffffff',
borderRight: '1px solid #e0e0e0',
fontFamily: 'Oxanium, Arial, sans-serif'
}
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
padding: '20px',
backgroundColor: '#f5f5f5',
fontFamily: 'Oxanium, Arial, sans-serif'
}
}, /*#__PURE__*/React.createElement("h2", null, "Responsive Panel"), /*#__PURE__*/React.createElement("p", null, "This example shows a responsive left panel with minimum and maximum width constraints."), /*#__PURE__*/React.createElement("p", null, "Try resizing the browser window to see how the panel adapts."), /*#__PURE__*/React.createElement("p", null, "Font family is set to Oxanium for consistent theming.")));
};
// With content styling
export const WithContentStyling = () => {
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
height: '100vh'
}
}, /*#__PURE__*/React.createElement(LeftPanel, {
items: sampleAccordionData,
contentClassName: "custom-content",
contentStyle: {
backgroundColor: '#f8f9fa',
padding: '8px',
borderRadius: '4px'
},
style: {
width: '280px',
backgroundColor: '#ffffff',
borderRight: '1px solid #e0e0e0',
padding: '16px'
}
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
padding: '20px',
backgroundColor: '#f5f5f5'
}
}, /*#__PURE__*/React.createElement("h2", null, "Content Styling Example"), /*#__PURE__*/React.createElement("p", null, "This example demonstrates custom styling for the content container."), /*#__PURE__*/React.createElement("p", null, "The content area has a light background, custom padding, and border radius.")));
};
// Interactive testing with actions
export const InteractiveTesting = () => {
const [selectedItemId, setSelectedItemId] = useState(null);
const [selectedAccordionId, setSelectedAccordionId] = useState(null);
const [selectedItemLabel, setSelectedItemLabel] = useState(null);
const [logoClickCount, setLogoClickCount] = useState(0);
const [currentData, setCurrentData] = useState(sampleAccordionData);
const handleItemSelect = (itemId, parentAccordionId, selectedItemLabel) => {
setSelectedItemId(itemId);
setSelectedAccordionId(parentAccordionId);
setSelectedItemLabel(selectedItemLabel);
};
const handleLogoClick = () => {
setLogoClickCount(prev => prev + 1);
};
const toggleData = () => {
setCurrentData(currentData === sampleAccordionData ? [] : sampleAccordionData);
};
const resetSelection = () => {
setSelectedItemId(null);
setSelectedAccordionId(null);
setSelectedItemLabel(null);
};
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
height: '100vh'
}
}, /*#__PURE__*/React.createElement(LeftPanel, {
items: currentData,
onItemSelect: handleItemSelect,
onLogoClick: handleLogoClick,
style: {
width: '280px',
backgroundColor: '#ffffff',
borderRight: '1px solid #e0e0e0',
fontFamily: 'Oxanium, Arial, sans-serif'
}
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
padding: '20px',
backgroundColor: '#f5f5f5',
fontFamily: 'Oxanium, Arial, sans-serif'
}
}, /*#__PURE__*/React.createElement("h2", null, "Interactive Testing Panel"), /*#__PURE__*/React.createElement("p", null, "Test all interactions and responsive behavior here."), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: '20px',
padding: '16px',
backgroundColor: '#ffffff',
borderRadius: '8px'
}
}, /*#__PURE__*/React.createElement("h3", null, "Selection Details:"), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("strong", null, "Selected Item ID:"), " ", selectedItemId || 'None'), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("strong", null, "Parent Accordion ID:"), " ", selectedAccordionId || 'None'), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("strong", null, "Selected Item Label:"), " ", selectedItemLabel || 'None'), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("strong", null, "Logo Click Count:"), " ", logoClickCount)), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: '20px',
padding: '16px',
backgroundColor: '#e8f5e8',
borderRadius: '8px'
}
}, /*#__PURE__*/React.createElement("h3", null, "Test Controls:"), /*#__PURE__*/React.createElement("button", {
onClick: toggleData,
style: {
marginRight: '10px',
padding: '8px 16px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}
}, currentData.length > 0 ? 'Clear Data' : 'Load Data'), /*#__PURE__*/React.createElement("button", {
onClick: resetSelection,
style: {
padding: '8px 16px',
backgroundColor: '#6c757d',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}
}, "Reset Selection")), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: '20px',
padding: '16px',
backgroundColor: '#fff3cd',
borderRadius: '8px'
}
}, /*#__PURE__*/React.createElement("h3", null, "Responsive Testing:"), /*#__PURE__*/React.createElement("p", null, "\u2022 Try resizing the browser window"), /*#__PURE__*/React.createElement("p", null, "\u2022 Test on different screen sizes"), /*#__PURE__*/React.createElement("p", null, "\u2022 Check font rendering with Oxanium"), /*#__PURE__*/React.createElement("p", null, "\u2022 Verify all interactions work properly"))));
};