okonomi_ui_kit 0.1.9 → 0.1.10
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.
- checksums.yaml +4 -4
- data/README.md +46 -4
- data/app/assets/builds/okonomi_ui_kit/application.tailwind.css +140 -79
- data/app/helpers/okonomi_ui_kit/CLAUDE.md +619 -0
- data/app/helpers/okonomi_ui_kit/component.rb +4 -0
- data/app/helpers/okonomi_ui_kit/components/badge.rb +4 -4
- data/app/helpers/okonomi_ui_kit/components/button_base.rb +88 -16
- data/app/helpers/okonomi_ui_kit/components/button_tag.rb +11 -5
- data/app/helpers/okonomi_ui_kit/components/button_to.rb +5 -4
- data/app/helpers/okonomi_ui_kit/components/dropdown_button.rb +147 -0
- data/app/helpers/okonomi_ui_kit/components/link_to.rb +5 -4
- data/app/helpers/okonomi_ui_kit/components/page.rb +16 -201
- data/app/helpers/okonomi_ui_kit/components/page_header.rb +111 -0
- data/app/helpers/okonomi_ui_kit/components/page_section.rb +145 -0
- data/app/javascript/okonomi_ui_kit/controllers/dropdown_controller.js +6 -0
- data/app/views/okonomi/components/dropdown_button/_dropdown_button.html.erb +282 -0
- data/app/views/okonomi/components/forms/field/_field.html.erb +34 -3
- data/app/views/okonomi/components/page/_page.html.erb +1 -1
- data/app/views/okonomi/components/page_header/_page_header.html.erb +4 -0
- data/app/views/okonomi/components/page_section/_page_section.html.erb +4 -0
- data/lib/okonomi_ui_kit/version.rb +1 -1
- metadata +13 -8
- data/app/views/okonomi/forms/tailwind/_field.html.erb +0 -34
- data/app/views/okonomi/page_builder/_page.html.erb +0 -3
@@ -0,0 +1,619 @@
|
|
1
|
+
# OkonomiUiKit Component Implementation Guide
|
2
|
+
|
3
|
+
This guide explains how to implement new components in OkonomiUiKit using the plugin-based architecture.
|
4
|
+
|
5
|
+
## Component Architecture Overview
|
6
|
+
|
7
|
+
OkonomiUiKit uses a plugin-based system where components are dynamically loaded through the `method_missing` mechanism in the `UiBuilder` class. This provides a clean, extensible way to add new components without modifying the core helper.
|
8
|
+
|
9
|
+
## Steps to Create a New Component
|
10
|
+
|
11
|
+
### 1. Create the Component Class
|
12
|
+
|
13
|
+
Create a new file in `app/helpers/okonomi_ui_kit/components/[component_name].rb`:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
module OkonomiUiKit
|
17
|
+
module Components
|
18
|
+
class YourComponent < OkonomiUiKit::Component
|
19
|
+
def render(*args, &block)
|
20
|
+
# Extract options and process arguments
|
21
|
+
# Call view.render with the template path and variables
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
### 2. Create the Template Structure
|
29
|
+
|
30
|
+
Create the template directory and file:
|
31
|
+
```
|
32
|
+
app/views/okonomi/components/[component_name]/_[component_name].html.erb
|
33
|
+
```
|
34
|
+
|
35
|
+
The template path follows the convention: `okonomi/components/[name]/[name]`
|
36
|
+
|
37
|
+
### 3. Base Component Class
|
38
|
+
|
39
|
+
All components inherit from `OkonomiUiKit::Component` which provides:
|
40
|
+
|
41
|
+
- `view`: Reference to the template/view context
|
42
|
+
- `style`: Access to registered component styles
|
43
|
+
- `template_path`: Automatically generates the path to the component's template
|
44
|
+
- `name`: Returns the underscored component name
|
45
|
+
|
46
|
+
## Example: Alert Component
|
47
|
+
|
48
|
+
Here's how the Alert component is implemented:
|
49
|
+
|
50
|
+
### Component Class (`app/helpers/okonomi_ui_kit/components/alert.rb`):
|
51
|
+
```ruby
|
52
|
+
module OkonomiUiKit
|
53
|
+
module Components
|
54
|
+
class Alert < OkonomiUiKit::Component
|
55
|
+
def render(title, options = {}, &block)
|
56
|
+
view.render(template_path, title:, options: options.with_indifferent_access, &block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
### Template (`app/views/okonomi/components/alert/_alert.html.erb`):
|
64
|
+
```erb
|
65
|
+
<div class="hover:bg-blue-700">
|
66
|
+
<%= title %>
|
67
|
+
</div>
|
68
|
+
```
|
69
|
+
|
70
|
+
## Example: Typography Component
|
71
|
+
|
72
|
+
A more complex example showing variant handling and theme integration:
|
73
|
+
|
74
|
+
### Component Class (`app/helpers/okonomi_ui_kit/components/typography.rb`):
|
75
|
+
```ruby
|
76
|
+
module OkonomiUiKit
|
77
|
+
module Components
|
78
|
+
class Typography < OkonomiUiKit::Component
|
79
|
+
TYPOGRAPHY_COMPONENTS = {
|
80
|
+
body1: 'p',
|
81
|
+
body2: 'p',
|
82
|
+
h1: 'h1',
|
83
|
+
h2: 'h2',
|
84
|
+
h3: 'h3',
|
85
|
+
h4: 'h4',
|
86
|
+
h5: 'h5',
|
87
|
+
h6: 'h6',
|
88
|
+
}.freeze
|
89
|
+
|
90
|
+
def render(text = nil, options = {}, &block)
|
91
|
+
options, text = text, nil if block_given?
|
92
|
+
options ||= {}
|
93
|
+
|
94
|
+
variant = (options.delete(:variant) || 'body1').to_sym
|
95
|
+
component = (TYPOGRAPHY_COMPONENTS[variant] || 'span').to_s
|
96
|
+
color = (options.delete(:color) || 'default').to_sym
|
97
|
+
|
98
|
+
classes = [
|
99
|
+
style(:variants, variant) || '',
|
100
|
+
style(:colors, color) || '',
|
101
|
+
options.delete(:class) || ''
|
102
|
+
].reject(&:blank?).join(' ')
|
103
|
+
|
104
|
+
view.render(
|
105
|
+
template_path,
|
106
|
+
text: text,
|
107
|
+
options: options,
|
108
|
+
variant: variant,
|
109
|
+
component: component,
|
110
|
+
classes: classes,
|
111
|
+
&block
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
### Template (`app/views/okonomi/components/typography/_typography.html.erb`):
|
120
|
+
```erb
|
121
|
+
<%= content_tag component, class: classes do %>
|
122
|
+
<% if defined?(text)%>
|
123
|
+
<%= text %>
|
124
|
+
<% else %>
|
125
|
+
<%= yield %>
|
126
|
+
<% end %>
|
127
|
+
<% end %>
|
128
|
+
```
|
129
|
+
|
130
|
+
## How the Plugin System Works
|
131
|
+
|
132
|
+
1. When `ui.your_component(...)` is called, the `method_missing` in `UiBuilder` intercepts it
|
133
|
+
2. It converts `your_component` to `YourComponent` and checks if `OkonomiUiKit::Components::YourComponent` exists
|
134
|
+
3. If found, it instantiates the component with the template and theme, then calls `render`
|
135
|
+
4. If not found, it calls `super` to raise the standard NoMethodError
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
def method_missing(method_name, *args, &block)
|
139
|
+
component_name = "OkonomiUiKit::Components::#{method_name.to_s.camelize}"
|
140
|
+
if Object.const_defined?(component_name)
|
141
|
+
return component_name.constantize.new(@template, get_theme).render(*args, &block)
|
142
|
+
else
|
143
|
+
super
|
144
|
+
end
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
## Best Practices
|
149
|
+
|
150
|
+
1. **Style Integration**: Always use the `style` method to get styling classes
|
151
|
+
2. **Flexible Arguments**: Support both text/content as first argument and block form
|
152
|
+
3. **Options Processing**: Use `with_indifferent_access` for options hashes
|
153
|
+
4. **Class Composition**: Build classes arrays and join them, filtering out blanks
|
154
|
+
5. **Template Variables**: Pass all necessary variables to the template explicitly
|
155
|
+
|
156
|
+
## Defining and Using Styles in Components
|
157
|
+
|
158
|
+
### Style Registration
|
159
|
+
|
160
|
+
Components can define their default styles using the `register_styles` class method. This approach provides:
|
161
|
+
- Clean separation of styling from logic
|
162
|
+
- Easy style overrides via theme system
|
163
|
+
- Consistent style access patterns
|
164
|
+
|
165
|
+
#### Basic Style Registration
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
module OkonomiUiKit
|
169
|
+
module Components
|
170
|
+
class Button < OkonomiUiKit::Component
|
171
|
+
register_styles :default do
|
172
|
+
{
|
173
|
+
base: "inline-flex items-center justify-center rounded-md font-medium",
|
174
|
+
sizes: {
|
175
|
+
sm: "px-3 py-1.5 text-sm",
|
176
|
+
md: "px-4 py-2 text-base",
|
177
|
+
lg: "px-6 py-3 text-lg"
|
178
|
+
},
|
179
|
+
variants: {
|
180
|
+
primary: "bg-primary-600 text-white hover:bg-primary-700",
|
181
|
+
secondary: "bg-secondary-600 text-white hover:bg-secondary-700",
|
182
|
+
outlined: "border border-gray-300 bg-white text-gray-700 hover:bg-gray-50"
|
183
|
+
}
|
184
|
+
}
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
### Accessing Styles in Components
|
192
|
+
|
193
|
+
The `Component` base class provides a `style` method to access registered styles:
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
def render(text, options = {})
|
197
|
+
size = (options.delete(:size) || :md).to_sym
|
198
|
+
variant = (options.delete(:variant) || :primary).to_sym
|
199
|
+
|
200
|
+
classes = [
|
201
|
+
style(:base), # Access base styles
|
202
|
+
style(:sizes, size), # Access nested styles
|
203
|
+
style(:variants, variant), # Access variant styles
|
204
|
+
options.delete(:class) # Include custom classes
|
205
|
+
].compact.join(' ')
|
206
|
+
|
207
|
+
view.tag.button(text, class: classes, **options)
|
208
|
+
end
|
209
|
+
```
|
210
|
+
|
211
|
+
### Using Config Classes for Customization
|
212
|
+
|
213
|
+
Users can customize component styles by creating config classes instead of modifying theme configuration:
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
# app/helpers/okonomi_ui_kit/configs/button.rb
|
217
|
+
module OkonomiUiKit
|
218
|
+
module Configs
|
219
|
+
class Button < OkonomiUiKit::Config
|
220
|
+
register_styles :default do
|
221
|
+
{
|
222
|
+
base: "custom-button-base-classes",
|
223
|
+
variants: {
|
224
|
+
primary: "bg-brand-500 hover:bg-brand-600"
|
225
|
+
}
|
226
|
+
}
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
```
|
232
|
+
|
233
|
+
For detailed information, see the [Style Override Guide](../guides/style-overrides-guide.md).
|
234
|
+
|
235
|
+
### Style Registration Patterns
|
236
|
+
|
237
|
+
#### 1. Simple Components
|
238
|
+
For components with basic styling needs:
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
register_styles :default do
|
242
|
+
{
|
243
|
+
base: "inline-block rounded px-2 py-1 text-sm",
|
244
|
+
colors: {
|
245
|
+
default: "bg-gray-100 text-gray-800",
|
246
|
+
primary: "bg-blue-100 text-blue-800",
|
247
|
+
success: "bg-green-100 text-green-800"
|
248
|
+
}
|
249
|
+
}
|
250
|
+
end
|
251
|
+
```
|
252
|
+
|
253
|
+
#### 2. Complex Components
|
254
|
+
For components with multiple style dimensions:
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
register_styles :default do
|
258
|
+
{
|
259
|
+
base: "relative inline-flex items-center",
|
260
|
+
variants: {
|
261
|
+
solid: "shadow-sm",
|
262
|
+
ghost: "shadow-none",
|
263
|
+
raised: "shadow-lg"
|
264
|
+
},
|
265
|
+
sizes: {
|
266
|
+
sm: "h-8 text-sm",
|
267
|
+
md: "h-10 text-base",
|
268
|
+
lg: "h-12 text-lg"
|
269
|
+
},
|
270
|
+
states: {
|
271
|
+
disabled: "opacity-50 cursor-not-allowed",
|
272
|
+
loading: "cursor-wait",
|
273
|
+
active: "ring-2 ring-offset-2"
|
274
|
+
}
|
275
|
+
}
|
276
|
+
end
|
277
|
+
```
|
278
|
+
|
279
|
+
#### 3. Conditional Styles
|
280
|
+
When styles depend on multiple conditions:
|
281
|
+
|
282
|
+
```ruby
|
283
|
+
def render(content, options = {})
|
284
|
+
variant = options.delete(:variant) || :solid
|
285
|
+
size = options.delete(:size) || :md
|
286
|
+
disabled = options.delete(:disabled)
|
287
|
+
loading = options.delete(:loading)
|
288
|
+
|
289
|
+
classes = [
|
290
|
+
style(:base),
|
291
|
+
style(:variants, variant),
|
292
|
+
style(:sizes, size),
|
293
|
+
disabled ? style(:states, :disabled) : nil,
|
294
|
+
loading ? style(:states, :loading) : nil
|
295
|
+
].compact.join(' ')
|
296
|
+
|
297
|
+
# ...
|
298
|
+
end
|
299
|
+
```
|
300
|
+
|
301
|
+
### Style Override System
|
302
|
+
|
303
|
+
Components use a two-layer style system:
|
304
|
+
1. **Internal Styles**: Default styles defined with `register_styles` in the component
|
305
|
+
2. **Config Styles**: Override styles defined in config classes
|
306
|
+
|
307
|
+
Styles are merged intelligently using TWMerge, with config styles taking precedence over internal styles.
|
308
|
+
|
309
|
+
### Style Method Reference
|
310
|
+
|
311
|
+
The `style` method supports multiple access patterns:
|
312
|
+
|
313
|
+
```ruby
|
314
|
+
# Access base styles
|
315
|
+
style(:base) # => "inline-flex items-center..."
|
316
|
+
|
317
|
+
# Access nested styles
|
318
|
+
style(:variants, :primary) # => "bg-primary-600 text-white..."
|
319
|
+
|
320
|
+
# Access deeply nested styles
|
321
|
+
style(:states, :hover, :primary) # => "hover:bg-primary-700"
|
322
|
+
|
323
|
+
# Returns nil for non-existent keys (safe access)
|
324
|
+
style(:variants, :unknown) # => nil
|
325
|
+
```
|
326
|
+
|
327
|
+
### Best Practices for Component Styles
|
328
|
+
|
329
|
+
1. **Use Semantic Keys**: Name style groups based on their purpose (variants, sizes, states)
|
330
|
+
2. **Provide Defaults**: Always include sensible defaults for optional style parameters
|
331
|
+
3. **Keep Base Minimal**: Base styles should only include essential, always-applied classes
|
332
|
+
4. **Avoid Conflicts**: Design style groups to be composable without conflicts
|
333
|
+
5. **Use Tailwind Utilities**: Leverage Tailwind's utility classes for consistency
|
334
|
+
6. **Document Style Options**: Include comments describing available style options
|
335
|
+
|
336
|
+
### Complete Example: Badge Component
|
337
|
+
|
338
|
+
```ruby
|
339
|
+
module OkonomiUiKit
|
340
|
+
module Components
|
341
|
+
class Badge < OkonomiUiKit::Component
|
342
|
+
def render(text, options = {})
|
343
|
+
options = options.with_indifferent_access
|
344
|
+
severity = (options.delete(:severity) || :default).to_sym
|
345
|
+
|
346
|
+
classes = [
|
347
|
+
style(:base),
|
348
|
+
style(:severities, severity) || '',
|
349
|
+
options.delete(:class) || ''
|
350
|
+
].reject(&:blank?).join(' ')
|
351
|
+
|
352
|
+
view.tag.span(text, class: classes, **options)
|
353
|
+
end
|
354
|
+
|
355
|
+
register_styles :default do
|
356
|
+
{
|
357
|
+
base: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium",
|
358
|
+
severities: {
|
359
|
+
default: "bg-gray-100 text-gray-800",
|
360
|
+
success: "bg-green-100 text-green-800",
|
361
|
+
danger: "bg-red-100 text-red-800",
|
362
|
+
info: "bg-blue-100 text-blue-800",
|
363
|
+
warning: "bg-yellow-100 text-yellow-800"
|
364
|
+
}
|
365
|
+
}
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
```
|
371
|
+
|
372
|
+
## Component Style System
|
373
|
+
|
374
|
+
All components use the `register_styles` method to define their default styles and access them via the `style` helper:
|
375
|
+
|
376
|
+
```ruby
|
377
|
+
class MyComponent < OkonomiUiKit::Component
|
378
|
+
register_styles :default do
|
379
|
+
{
|
380
|
+
base: "...",
|
381
|
+
variants: { ... }
|
382
|
+
}
|
383
|
+
end
|
384
|
+
|
385
|
+
def render(...)
|
386
|
+
style(:base) # Access registered styles
|
387
|
+
end
|
388
|
+
end
|
389
|
+
```
|
390
|
+
|
391
|
+
The `style` method provides:
|
392
|
+
- Clean syntax for accessing styles
|
393
|
+
- Automatic merging with config class overrides
|
394
|
+
- Safe access (returns nil for missing keys)
|
395
|
+
- Deep nesting support
|
396
|
+
|
397
|
+
## Testing Your Component
|
398
|
+
|
399
|
+
After creating a component, test it in your views:
|
400
|
+
|
401
|
+
```erb
|
402
|
+
<%= ui.your_component("Content", variant: :primary, color: :success) %>
|
403
|
+
|
404
|
+
<%= ui.your_component(variant: :secondary) do %>
|
405
|
+
<span>Block content</span>
|
406
|
+
<% end %>
|
407
|
+
```
|
408
|
+
|
409
|
+
## Component Naming Conventions
|
410
|
+
|
411
|
+
- Component class: `PascalCase` (e.g., `ButtonGroup`)
|
412
|
+
- Method name: `snake_case` (e.g., `button_group`)
|
413
|
+
- Template directory: `snake_case` (e.g., `button_group`)
|
414
|
+
- Template file: `_snake_case.html.erb` (e.g., `_button_group.html.erb`)
|
415
|
+
|
416
|
+
This architecture ensures components are:
|
417
|
+
- Easy to add without modifying core code
|
418
|
+
- Automatically available through the `ui` helper
|
419
|
+
- Consistent in structure and behavior
|
420
|
+
- Fully integrated with the theme system
|
421
|
+
|
422
|
+
## Testing Components
|
423
|
+
|
424
|
+
Components should be thoroughly tested to ensure they work correctly and integrate properly with the theme system. Here's how to test components:
|
425
|
+
|
426
|
+
### Test File Location
|
427
|
+
|
428
|
+
Create test files in `test/helpers/okonomi_ui_kit/components/[component_name]_test.rb`
|
429
|
+
|
430
|
+
### Basic Test Structure
|
431
|
+
|
432
|
+
```ruby
|
433
|
+
require "test_helper"
|
434
|
+
|
435
|
+
module OkonomiUiKit
|
436
|
+
module Components
|
437
|
+
class YourComponentTest < ActionView::TestCase
|
438
|
+
include OkonomiUiKit::UiHelper
|
439
|
+
|
440
|
+
test "component renders with default options" do
|
441
|
+
html = ui.your_component("Content")
|
442
|
+
|
443
|
+
assert_includes html, "expected content"
|
444
|
+
assert_includes html, "<expected_tag"
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|
449
|
+
```
|
450
|
+
|
451
|
+
### Key Testing Areas
|
452
|
+
|
453
|
+
1. **Default Rendering**
|
454
|
+
```ruby
|
455
|
+
test "renders with minimal arguments" do
|
456
|
+
html = ui.typography("Hello World")
|
457
|
+
|
458
|
+
assert_includes html, "<p"
|
459
|
+
assert_includes html, "Hello World"
|
460
|
+
assert_includes html, "</p>"
|
461
|
+
end
|
462
|
+
```
|
463
|
+
|
464
|
+
2. **Variant Support**
|
465
|
+
```ruby
|
466
|
+
test "renders all variants correctly" do
|
467
|
+
%i[primary secondary outlined].each do |variant|
|
468
|
+
html = ui.button("Click", variant: variant)
|
469
|
+
|
470
|
+
# Assert variant-specific rendering
|
471
|
+
end
|
472
|
+
end
|
473
|
+
```
|
474
|
+
|
475
|
+
3. **Block Content**
|
476
|
+
```ruby
|
477
|
+
test "accepts block content" do
|
478
|
+
html = ui.card do
|
479
|
+
"<strong>Custom content</strong>".html_safe
|
480
|
+
end
|
481
|
+
|
482
|
+
assert_includes html, "<strong>Custom content</strong>"
|
483
|
+
end
|
484
|
+
```
|
485
|
+
|
486
|
+
4. **Theme Integration**
|
487
|
+
```ruby
|
488
|
+
test "applies registered styles" do
|
489
|
+
html = ui.alert("Message", variant: :success)
|
490
|
+
|
491
|
+
# Check that registered styles are applied
|
492
|
+
assert_match /class="[^"]*"/, html
|
493
|
+
end
|
494
|
+
```
|
495
|
+
|
496
|
+
5. **HTML Options**
|
497
|
+
```ruby
|
498
|
+
test "accepts html options" do
|
499
|
+
html = ui.badge("New", id: "my-badge", data: { value: "1" })
|
500
|
+
|
501
|
+
assert_includes html, 'id="my-badge"'
|
502
|
+
assert_includes html, 'data-value="1"'
|
503
|
+
end
|
504
|
+
```
|
505
|
+
|
506
|
+
6. **Style Registration**
|
507
|
+
```ruby
|
508
|
+
test "applies registered styles correctly" do
|
509
|
+
html = ui.button("Test", variant: :primary)
|
510
|
+
|
511
|
+
# Verify the component uses its registered styles
|
512
|
+
assert_match /bg-primary/, html
|
513
|
+
end
|
514
|
+
```
|
515
|
+
|
516
|
+
7. **Edge Cases**
|
517
|
+
```ruby
|
518
|
+
test "handles nil content gracefully" do
|
519
|
+
html = ui.typography(nil)
|
520
|
+
|
521
|
+
assert_includes html, "<p"
|
522
|
+
refute_includes html, "nil"
|
523
|
+
end
|
524
|
+
```
|
525
|
+
|
526
|
+
8. **Plugin System**
|
527
|
+
```ruby
|
528
|
+
test "component loads via plugin system" do
|
529
|
+
assert_nothing_raised do
|
530
|
+
ui.your_component("Test")
|
531
|
+
end
|
532
|
+
end
|
533
|
+
```
|
534
|
+
|
535
|
+
### Complete Typography Test Example
|
536
|
+
|
537
|
+
Here's the complete test suite for the Typography component as a reference:
|
538
|
+
|
539
|
+
```ruby
|
540
|
+
require "test_helper"
|
541
|
+
|
542
|
+
module OkonomiUiKit
|
543
|
+
module Components
|
544
|
+
class TypographyTest < ActionView::TestCase
|
545
|
+
include OkonomiUiKit::UiHelper
|
546
|
+
|
547
|
+
test "typography renders with default variant and text" do
|
548
|
+
html = ui.typography("Hello World")
|
549
|
+
|
550
|
+
assert_includes html, "<p"
|
551
|
+
assert_includes html, "Hello World"
|
552
|
+
assert_includes html, "</p>"
|
553
|
+
end
|
554
|
+
|
555
|
+
test "typography renders all heading variants correctly" do
|
556
|
+
%i[h1 h2 h3 h4 h5 h6].each do |variant|
|
557
|
+
html = ui.typography("Heading #{variant}", variant: variant)
|
558
|
+
|
559
|
+
assert_includes html, "<#{variant}"
|
560
|
+
assert_includes html, "Heading #{variant}"
|
561
|
+
assert_includes html, "</#{variant}>"
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
test "typography accepts block content" do
|
566
|
+
html = ui.typography(variant: :h2) do
|
567
|
+
"<strong>Bold content</strong>".html_safe
|
568
|
+
end
|
569
|
+
|
570
|
+
assert_includes html, "<h2"
|
571
|
+
assert_includes html, "<strong>Bold content</strong>"
|
572
|
+
assert_includes html, "</h2>"
|
573
|
+
end
|
574
|
+
|
575
|
+
test "typography applies color classes" do
|
576
|
+
html = ui.typography("Colored text", color: :primary)
|
577
|
+
|
578
|
+
assert_match /class="[^"]*"/, html
|
579
|
+
end
|
580
|
+
|
581
|
+
test "typography merges custom classes" do
|
582
|
+
html = ui.typography("Custom styled", class: "custom-class")
|
583
|
+
|
584
|
+
assert_includes html, "custom-class"
|
585
|
+
end
|
586
|
+
|
587
|
+
test "typography accepts html options" do
|
588
|
+
html = ui.typography("Text with ID", id: "my-typography", data: { testid: "typography-element" })
|
589
|
+
|
590
|
+
assert_includes html, 'id="my-typography"'
|
591
|
+
assert_includes html, 'data-testid="typography-element"'
|
592
|
+
end
|
593
|
+
end
|
594
|
+
end
|
595
|
+
end
|
596
|
+
```
|
597
|
+
|
598
|
+
### Running Component Tests
|
599
|
+
|
600
|
+
Run tests for a specific component:
|
601
|
+
```bash
|
602
|
+
bin/rails test test/helpers/okonomi_ui_kit/components/typography_test.rb
|
603
|
+
```
|
604
|
+
|
605
|
+
Run all component tests:
|
606
|
+
```bash
|
607
|
+
bin/rails test test/helpers/okonomi_ui_kit/components/
|
608
|
+
```
|
609
|
+
|
610
|
+
### Testing Best Practices
|
611
|
+
|
612
|
+
1. **Test Public Interface** - Focus on testing what users of the component will use
|
613
|
+
2. **Avoid Implementation Details** - Don't test internal methods or exact HTML structure unless critical
|
614
|
+
3. **Test Edge Cases** - Include tests for nil values, empty strings, missing options
|
615
|
+
4. **Test Integration** - Ensure components work with the style system and view helpers
|
616
|
+
5. **Keep Tests Fast** - Use ActionView::TestCase for unit tests rather than integration tests
|
617
|
+
6. **Use Descriptive Names** - Test names should clearly indicate what behavior they verify
|
618
|
+
|
619
|
+
This testing approach ensures your components are reliable, maintainable, and properly integrated with the OkonomiUiKit system.
|
@@ -5,11 +5,11 @@ module OkonomiUiKit
|
|
5
5
|
options = options.with_indifferent_access
|
6
6
|
severity = (options.delete(:severity) || options.delete(:variant) || :default).to_sym
|
7
7
|
|
8
|
-
classes =
|
8
|
+
classes = tw_merge(
|
9
9
|
style(:base),
|
10
|
-
style(:severities, severity)
|
11
|
-
options.delete(:class)
|
12
|
-
|
10
|
+
style(:severities, severity),
|
11
|
+
options.delete(:class)
|
12
|
+
)
|
13
13
|
|
14
14
|
view.tag.span(text, class: classes, **options)
|
15
15
|
end
|