hazo_config 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/general.mdc +51 -0
- package/.storybook/main.ts +33 -0
- package/.storybook/preview.ts +19 -0
- package/CODE_REVIEW.md +81 -0
- package/README.md +92 -0
- package/components.json +21 -0
- package/package.json +64 -0
- package/postcss.config.js +7 -0
- package/src/components/config_editor.stories.tsx +116 -0
- package/src/components/config_editor.tsx +214 -0
- package/src/components/config_viewer.stories.tsx +126 -0
- package/src/components/config_viewer.tsx +170 -0
- package/src/components/example_component.stories.tsx +59 -0
- package/src/components/example_component.tsx +39 -0
- package/src/components/index.ts +9 -0
- package/src/index.ts +7 -0
- package/src/lib/README.md +160 -0
- package/src/lib/config-loader.ts +186 -0
- package/src/lib/index.ts +18 -0
- package/src/lib/mock_config_provider.ts +93 -0
- package/src/lib/types.ts +100 -0
- package/src/lib/utils.ts +15 -0
- package/src/styles/globals.css +61 -0
- package/tailwind.config.js +13 -0
- package/tsconfig.build.json +13 -0
- package/tsconfig.json +30 -0
- package/tsconfig.node.json +15 -0
- package/vite.config.ts +14 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// Config editor component for managing configuration
|
|
2
|
+
// Provides a more advanced interface for editing configuration values
|
|
3
|
+
|
|
4
|
+
import React, { useState, useEffect } from 'react'
|
|
5
|
+
import { cn } from '@/lib/utils'
|
|
6
|
+
import type { ConfigProvider } from '@/lib/types'
|
|
7
|
+
import { RefreshCw, Save } from 'lucide-react'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Config editor component props
|
|
11
|
+
*/
|
|
12
|
+
export interface ConfigEditorProps {
|
|
13
|
+
/**
|
|
14
|
+
* The config provider instance
|
|
15
|
+
*/
|
|
16
|
+
config_provider: ConfigProvider
|
|
17
|
+
/**
|
|
18
|
+
* Additional CSS classes
|
|
19
|
+
*/
|
|
20
|
+
className?: string
|
|
21
|
+
/**
|
|
22
|
+
* Callback when config is updated
|
|
23
|
+
*/
|
|
24
|
+
on_update?: () => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Config editor component
|
|
29
|
+
* Provides interface for viewing and editing configuration with refresh and save capabilities
|
|
30
|
+
* @param props - Component props
|
|
31
|
+
* @returns React component
|
|
32
|
+
*/
|
|
33
|
+
export const ConfigEditor: React.FC<ConfigEditorProps> = ({
|
|
34
|
+
config_provider,
|
|
35
|
+
className,
|
|
36
|
+
on_update,
|
|
37
|
+
}) => {
|
|
38
|
+
const [sections, set_sections] = useState<Record<string, Record<string, string>>>({})
|
|
39
|
+
const [selected_section, set_selected_section] = useState<string | null>(null)
|
|
40
|
+
const [new_section_name, set_new_section_name] = useState<string>('')
|
|
41
|
+
const [new_key, set_new_key] = useState<string>('')
|
|
42
|
+
const [new_value, set_new_value] = useState<string>('')
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Load configuration from provider
|
|
46
|
+
*/
|
|
47
|
+
const load_config = () => {
|
|
48
|
+
const all_sections: Record<string, Record<string, string>> = {}
|
|
49
|
+
|
|
50
|
+
if ('getAllSections' in config_provider && typeof config_provider.getAllSections === 'function') {
|
|
51
|
+
const all = (config_provider as any).getAllSections()
|
|
52
|
+
Object.assign(all_sections, all)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
set_sections(all_sections)
|
|
56
|
+
if (!selected_section && Object.keys(all_sections).length > 0) {
|
|
57
|
+
set_selected_section(Object.keys(all_sections)[0])
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
load_config()
|
|
63
|
+
}, [config_provider])
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Handle refresh button click
|
|
67
|
+
*/
|
|
68
|
+
const handle_refresh = () => {
|
|
69
|
+
config_provider.refresh()
|
|
70
|
+
load_config()
|
|
71
|
+
if (on_update) {
|
|
72
|
+
on_update()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Handle save button click
|
|
78
|
+
*/
|
|
79
|
+
const handle_save = () => {
|
|
80
|
+
config_provider.save()
|
|
81
|
+
load_config()
|
|
82
|
+
if (on_update) {
|
|
83
|
+
on_update()
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Add a new key-value pair to selected section
|
|
89
|
+
*/
|
|
90
|
+
const handle_add_key = () => {
|
|
91
|
+
if (selected_section && new_key && new_value) {
|
|
92
|
+
config_provider.set(selected_section, new_key, new_value)
|
|
93
|
+
set_new_key('')
|
|
94
|
+
set_new_value('')
|
|
95
|
+
load_config()
|
|
96
|
+
if (on_update) {
|
|
97
|
+
on_update()
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Update a config value
|
|
104
|
+
*/
|
|
105
|
+
const handle_update_value = (section: string, key: string, value: string) => {
|
|
106
|
+
config_provider.set(section, key, value)
|
|
107
|
+
load_config()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const current_section_data = selected_section ? sections[selected_section] : null
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div className={cn('cls_config_editor space-y-4', className)}>
|
|
114
|
+
<div className="cls_config_editor_header flex items-center justify-between border-b pb-2">
|
|
115
|
+
<h2 className="text-xl font-semibold">Configuration Editor</h2>
|
|
116
|
+
<div className="flex gap-2">
|
|
117
|
+
<button
|
|
118
|
+
onClick={handle_refresh}
|
|
119
|
+
className="px-3 py-1 border rounded hover:bg-muted flex items-center gap-2"
|
|
120
|
+
aria-label="Refresh"
|
|
121
|
+
>
|
|
122
|
+
<RefreshCw size={16} />
|
|
123
|
+
Refresh
|
|
124
|
+
</button>
|
|
125
|
+
<button
|
|
126
|
+
onClick={handle_save}
|
|
127
|
+
className="px-3 py-1 bg-primary text-primary-foreground rounded hover:bg-primary/90 flex items-center gap-2"
|
|
128
|
+
aria-label="Save"
|
|
129
|
+
>
|
|
130
|
+
<Save size={16} />
|
|
131
|
+
Save
|
|
132
|
+
</button>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<div className="cls_config_editor_content grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
137
|
+
<div className="cls_config_sections_list border rounded p-4">
|
|
138
|
+
<h3 className="font-semibold mb-2">Sections</h3>
|
|
139
|
+
<div className="space-y-1">
|
|
140
|
+
{Object.keys(sections).map((section_name) => (
|
|
141
|
+
<button
|
|
142
|
+
key={section_name}
|
|
143
|
+
onClick={() => set_selected_section(section_name)}
|
|
144
|
+
className={cn(
|
|
145
|
+
'w-full text-left px-2 py-1 rounded text-sm',
|
|
146
|
+
selected_section === section_name
|
|
147
|
+
? 'bg-primary text-primary-foreground'
|
|
148
|
+
: 'hover:bg-muted'
|
|
149
|
+
)}
|
|
150
|
+
>
|
|
151
|
+
{section_name}
|
|
152
|
+
</button>
|
|
153
|
+
))}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div className="cls_config_section_content md:col-span-2 border rounded p-4">
|
|
158
|
+
{selected_section ? (
|
|
159
|
+
<>
|
|
160
|
+
<h3 className="font-semibold mb-4">{selected_section}</h3>
|
|
161
|
+
<div className="space-y-3">
|
|
162
|
+
{current_section_data &&
|
|
163
|
+
Object.entries(current_section_data).map(([key, value]) => (
|
|
164
|
+
<div key={key} className="cls_config_item">
|
|
165
|
+
<label className="block text-sm font-medium mb-1">{key}</label>
|
|
166
|
+
<input
|
|
167
|
+
type="text"
|
|
168
|
+
value={value}
|
|
169
|
+
onChange={(e) =>
|
|
170
|
+
handle_update_value(selected_section, key, e.target.value)
|
|
171
|
+
}
|
|
172
|
+
className="w-full px-2 py-1 border rounded"
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
175
|
+
))}
|
|
176
|
+
|
|
177
|
+
<div className="cls_add_new_key border-t pt-3 mt-3">
|
|
178
|
+
<h4 className="text-sm font-medium mb-2">Add New Key</h4>
|
|
179
|
+
<div className="space-y-2">
|
|
180
|
+
<input
|
|
181
|
+
type="text"
|
|
182
|
+
placeholder="Key name"
|
|
183
|
+
value={new_key}
|
|
184
|
+
onChange={(e) => set_new_key(e.target.value)}
|
|
185
|
+
className="w-full px-2 py-1 border rounded text-sm"
|
|
186
|
+
/>
|
|
187
|
+
<input
|
|
188
|
+
type="text"
|
|
189
|
+
placeholder="Value"
|
|
190
|
+
value={new_value}
|
|
191
|
+
onChange={(e) => set_new_value(e.target.value)}
|
|
192
|
+
className="w-full px-2 py-1 border rounded text-sm"
|
|
193
|
+
/>
|
|
194
|
+
<button
|
|
195
|
+
onClick={handle_add_key}
|
|
196
|
+
className="px-3 py-1 bg-primary text-primary-foreground rounded hover:bg-primary/90 text-sm"
|
|
197
|
+
>
|
|
198
|
+
Add Key
|
|
199
|
+
</button>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
</>
|
|
204
|
+
) : (
|
|
205
|
+
<div className="text-center text-muted-foreground py-8">
|
|
206
|
+
Select a section to view/edit
|
|
207
|
+
</div>
|
|
208
|
+
)}
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Storybook stories for ConfigViewer component
|
|
2
|
+
// Demonstrates the config viewer component with mock config provider
|
|
3
|
+
|
|
4
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
5
|
+
import { ConfigViewer } from './config_viewer'
|
|
6
|
+
import { MockConfigProvider } from '@/lib/mock_config_provider'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Sample configuration data for testing
|
|
10
|
+
*/
|
|
11
|
+
const sample_config = {
|
|
12
|
+
database: {
|
|
13
|
+
host: 'localhost',
|
|
14
|
+
port: '5432',
|
|
15
|
+
name: 'myapp_db',
|
|
16
|
+
user: 'admin',
|
|
17
|
+
},
|
|
18
|
+
logging: {
|
|
19
|
+
logfile: 'logs/app.log',
|
|
20
|
+
level: 'info',
|
|
21
|
+
max_size: '10MB',
|
|
22
|
+
},
|
|
23
|
+
api: {
|
|
24
|
+
base_url: 'https://api.example.com',
|
|
25
|
+
timeout: '5000',
|
|
26
|
+
retry_count: '3',
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Meta configuration for ConfigViewer stories
|
|
32
|
+
*/
|
|
33
|
+
const meta: Meta<typeof ConfigViewer> = {
|
|
34
|
+
title: 'Components/ConfigViewer',
|
|
35
|
+
component: ConfigViewer,
|
|
36
|
+
parameters: {
|
|
37
|
+
layout: 'padded',
|
|
38
|
+
},
|
|
39
|
+
tags: ['autodocs'],
|
|
40
|
+
argTypes: {
|
|
41
|
+
config_provider: {
|
|
42
|
+
control: false,
|
|
43
|
+
description: 'The config provider instance',
|
|
44
|
+
},
|
|
45
|
+
className: {
|
|
46
|
+
control: 'text',
|
|
47
|
+
description: 'Additional CSS classes',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default meta
|
|
53
|
+
type Story = StoryObj<typeof ConfigViewer>
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Default story with sample configuration
|
|
57
|
+
*/
|
|
58
|
+
export const Default: Story = {
|
|
59
|
+
args: {
|
|
60
|
+
config_provider: new MockConfigProvider(sample_config),
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Story with database configuration only
|
|
66
|
+
*/
|
|
67
|
+
export const DatabaseConfig: Story = {
|
|
68
|
+
args: {
|
|
69
|
+
config_provider: new MockConfigProvider({
|
|
70
|
+
database: sample_config.database,
|
|
71
|
+
}),
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Story with logging configuration
|
|
77
|
+
*/
|
|
78
|
+
export const LoggingConfig: Story = {
|
|
79
|
+
args: {
|
|
80
|
+
config_provider: new MockConfigProvider({
|
|
81
|
+
logging: sample_config.logging,
|
|
82
|
+
}),
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Story with API configuration
|
|
88
|
+
*/
|
|
89
|
+
export const ApiConfig: Story = {
|
|
90
|
+
args: {
|
|
91
|
+
config_provider: new MockConfigProvider({
|
|
92
|
+
api: sample_config.api,
|
|
93
|
+
}),
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Story with empty configuration
|
|
99
|
+
*/
|
|
100
|
+
export const EmptyConfig: Story = {
|
|
101
|
+
args: {
|
|
102
|
+
config_provider: new MockConfigProvider(),
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Story with multiple sections
|
|
108
|
+
*/
|
|
109
|
+
export const MultipleSections: Story = {
|
|
110
|
+
args: {
|
|
111
|
+
config_provider: new MockConfigProvider({
|
|
112
|
+
...sample_config,
|
|
113
|
+
server: {
|
|
114
|
+
host: '0.0.0.0',
|
|
115
|
+
port: '3000',
|
|
116
|
+
env: 'production',
|
|
117
|
+
},
|
|
118
|
+
app: {
|
|
119
|
+
name: 'My App',
|
|
120
|
+
version: '1.0.0',
|
|
121
|
+
debug: 'false',
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Config viewer component for displaying configuration data
|
|
2
|
+
// Displays configuration sections and values in a readable format
|
|
3
|
+
|
|
4
|
+
import React, { useState, useEffect } from 'react'
|
|
5
|
+
import { cn } from '@/lib/utils'
|
|
6
|
+
import type { ConfigProvider } from '@/lib/types'
|
|
7
|
+
import { Pencil, CheckCircle2, XCircle } from 'lucide-react'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Config viewer component props
|
|
11
|
+
*/
|
|
12
|
+
export interface ConfigViewerProps {
|
|
13
|
+
/**
|
|
14
|
+
* The config provider instance
|
|
15
|
+
*/
|
|
16
|
+
config_provider: ConfigProvider
|
|
17
|
+
/**
|
|
18
|
+
* Additional CSS classes
|
|
19
|
+
*/
|
|
20
|
+
className?: string
|
|
21
|
+
/**
|
|
22
|
+
* Callback when config is updated
|
|
23
|
+
*/
|
|
24
|
+
on_update?: () => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Config viewer component
|
|
29
|
+
* Displays configuration sections and allows viewing/editing values
|
|
30
|
+
* @param props - Component props
|
|
31
|
+
* @returns React component
|
|
32
|
+
*/
|
|
33
|
+
export const ConfigViewer: React.FC<ConfigViewerProps> = ({
|
|
34
|
+
config_provider,
|
|
35
|
+
className,
|
|
36
|
+
on_update,
|
|
37
|
+
}) => {
|
|
38
|
+
const [sections, set_sections] = useState<Record<string, Record<string, string>>>({})
|
|
39
|
+
const [editing, set_editing] = useState<{ section: string; key: string } | null>(null)
|
|
40
|
+
const [edit_value, set_edit_value] = useState<string>('')
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Load configuration from provider
|
|
44
|
+
*/
|
|
45
|
+
const load_config = () => {
|
|
46
|
+
const all_sections: Record<string, Record<string, string>> = {}
|
|
47
|
+
|
|
48
|
+
// Try to get all sections - if provider has getAllSections, use it
|
|
49
|
+
if ('getAllSections' in config_provider && typeof config_provider.getAllSections === 'function') {
|
|
50
|
+
const all = (config_provider as any).getAllSections()
|
|
51
|
+
Object.assign(all_sections, all)
|
|
52
|
+
} else {
|
|
53
|
+
// Fallback: try common section names
|
|
54
|
+
const common_sections = ['database', 'logging', 'api', 'server', 'app']
|
|
55
|
+
for (const section of common_sections) {
|
|
56
|
+
const section_data = config_provider.getSection(section)
|
|
57
|
+
if (section_data) {
|
|
58
|
+
all_sections[section] = section_data
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
set_sections(all_sections)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
load_config()
|
|
68
|
+
}, [config_provider])
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Start editing a config value
|
|
72
|
+
*/
|
|
73
|
+
const start_edit = (section: string, key: string) => {
|
|
74
|
+
const value = config_provider.get(section, key) || ''
|
|
75
|
+
set_editing({ section, key })
|
|
76
|
+
set_edit_value(value)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Cancel editing
|
|
81
|
+
*/
|
|
82
|
+
const cancel_edit = () => {
|
|
83
|
+
set_editing(null)
|
|
84
|
+
set_edit_value('')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Save edited value
|
|
89
|
+
*/
|
|
90
|
+
const save_edit = () => {
|
|
91
|
+
if (editing) {
|
|
92
|
+
config_provider.set(editing.section, editing.key, edit_value)
|
|
93
|
+
config_provider.save()
|
|
94
|
+
load_config()
|
|
95
|
+
set_editing(null)
|
|
96
|
+
set_edit_value('')
|
|
97
|
+
if (on_update) {
|
|
98
|
+
on_update()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<div className={cn('cls_config_viewer space-y-4', className)}>
|
|
105
|
+
{Object.keys(sections).length === 0 ? (
|
|
106
|
+
<div className="p-4 text-center text-muted-foreground">
|
|
107
|
+
No configuration sections found
|
|
108
|
+
</div>
|
|
109
|
+
) : (
|
|
110
|
+
Object.entries(sections).map(([section_name, section_data]) => (
|
|
111
|
+
<div key={section_name} className="cls_config_section border rounded-lg p-4">
|
|
112
|
+
<h3 className="text-lg font-semibold mb-3">{section_name}</h3>
|
|
113
|
+
<div className="space-y-2">
|
|
114
|
+
{Object.entries(section_data).map(([key, value]) => {
|
|
115
|
+
const is_editing = editing?.section === section_name && editing?.key === key
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<div key={key} className="cls_config_item flex items-center gap-2">
|
|
119
|
+
<div className="flex-1">
|
|
120
|
+
<div className="text-sm font-medium text-muted-foreground">{key}</div>
|
|
121
|
+
{is_editing ? (
|
|
122
|
+
<div className="flex items-center gap-2 mt-1">
|
|
123
|
+
<input
|
|
124
|
+
type="text"
|
|
125
|
+
value={edit_value}
|
|
126
|
+
onChange={(e) => set_edit_value(e.target.value)}
|
|
127
|
+
className="flex-1 px-2 py-1 border rounded text-sm"
|
|
128
|
+
autoFocus
|
|
129
|
+
/>
|
|
130
|
+
<button
|
|
131
|
+
onClick={save_edit}
|
|
132
|
+
className="text-green-600 hover:text-green-700"
|
|
133
|
+
aria-label="Save"
|
|
134
|
+
>
|
|
135
|
+
<CheckCircle2 size={20} />
|
|
136
|
+
</button>
|
|
137
|
+
<button
|
|
138
|
+
onClick={cancel_edit}
|
|
139
|
+
className="text-red-600 hover:text-red-700"
|
|
140
|
+
aria-label="Cancel"
|
|
141
|
+
>
|
|
142
|
+
<XCircle size={20} />
|
|
143
|
+
</button>
|
|
144
|
+
</div>
|
|
145
|
+
) : (
|
|
146
|
+
<div className="flex items-center gap-2 mt-1">
|
|
147
|
+
<div className="flex-1 px-2 py-1 bg-muted rounded text-sm">
|
|
148
|
+
{value || '(empty)'}
|
|
149
|
+
</div>
|
|
150
|
+
<button
|
|
151
|
+
onClick={() => start_edit(section_name, key)}
|
|
152
|
+
className="text-blue-600 hover:text-blue-700"
|
|
153
|
+
aria-label="Edit"
|
|
154
|
+
>
|
|
155
|
+
<Pencil size={18} />
|
|
156
|
+
</button>
|
|
157
|
+
</div>
|
|
158
|
+
)}
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
)
|
|
162
|
+
})}
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
))
|
|
166
|
+
)}
|
|
167
|
+
</div>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Storybook stories for the ExampleComponent
|
|
2
|
+
// Demonstrates how to create stories for components
|
|
3
|
+
|
|
4
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
5
|
+
import { ExampleComponent } from './example_component'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Meta configuration for ExampleComponent stories
|
|
9
|
+
*/
|
|
10
|
+
const meta: Meta<typeof ExampleComponent> = {
|
|
11
|
+
title: 'Components/ExampleComponent',
|
|
12
|
+
component: ExampleComponent,
|
|
13
|
+
parameters: {
|
|
14
|
+
layout: 'centered',
|
|
15
|
+
},
|
|
16
|
+
tags: ['autodocs'],
|
|
17
|
+
argTypes: {
|
|
18
|
+
title: {
|
|
19
|
+
control: 'text',
|
|
20
|
+
description: 'The title to display',
|
|
21
|
+
},
|
|
22
|
+
className: {
|
|
23
|
+
control: 'text',
|
|
24
|
+
description: 'Additional CSS classes',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default meta
|
|
30
|
+
type Story = StoryObj<typeof ExampleComponent>
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Default story for ExampleComponent
|
|
34
|
+
*/
|
|
35
|
+
export const Default: Story = {
|
|
36
|
+
args: {
|
|
37
|
+
title: 'Example Component',
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Story with custom title
|
|
43
|
+
*/
|
|
44
|
+
export const CustomTitle: Story = {
|
|
45
|
+
args: {
|
|
46
|
+
title: 'Custom Title Example',
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Story with custom styling
|
|
52
|
+
*/
|
|
53
|
+
export const CustomStyling: Story = {
|
|
54
|
+
args: {
|
|
55
|
+
title: 'Styled Component',
|
|
56
|
+
className: 'bg-blue-100 border-blue-500',
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Example component for the config management library
|
|
2
|
+
// This is a placeholder component to demonstrate the setup
|
|
3
|
+
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import { cn } from '@/lib/utils'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Example component props interface
|
|
9
|
+
*/
|
|
10
|
+
export interface ExampleComponentProps {
|
|
11
|
+
/**
|
|
12
|
+
* The title to display
|
|
13
|
+
*/
|
|
14
|
+
title?: string
|
|
15
|
+
/**
|
|
16
|
+
* Additional CSS classes
|
|
17
|
+
*/
|
|
18
|
+
className?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Example component for demonstrating the component library setup
|
|
23
|
+
* @param props - Component props
|
|
24
|
+
* @returns React component
|
|
25
|
+
*/
|
|
26
|
+
export const ExampleComponent: React.FC<ExampleComponentProps> = ({
|
|
27
|
+
title = 'Example Component',
|
|
28
|
+
className,
|
|
29
|
+
}) => {
|
|
30
|
+
return (
|
|
31
|
+
<div className={cn('cls_example_component p-4 border rounded-lg', className)}>
|
|
32
|
+
<h2 className="text-xl font-semibold">{title}</h2>
|
|
33
|
+
<p className="text-muted-foreground mt-2">
|
|
34
|
+
This is an example component for the config management library.
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Component exports
|
|
2
|
+
// Export all components from this file for easy importing
|
|
3
|
+
|
|
4
|
+
export { ExampleComponent } from './example_component'
|
|
5
|
+
export { ConfigViewer } from './config_viewer'
|
|
6
|
+
export { ConfigEditor } from './config_editor'
|
|
7
|
+
export type { ConfigViewerProps } from './config_viewer'
|
|
8
|
+
export type { ConfigEditorProps } from './config_editor'
|
|
9
|
+
|