Skip to content

Commit 7bd455f

Browse files
authored
[Documentation] Add documentation for batch 3 components (#338)
* Add docs generator and button documentation Introduce a new generator (`ruby_ui:install:docs`) that copies component documentation files from the gem to Rails applications. Documentation files follow the naming convention `{component}_docs.rb` and are copied to `app/views/docs/` with the `_docs` suffix removed. Usage: bin/rails g ruby_ui:install:docs bin/rails g ruby_ui:install:docs --force Include stub classes for docs dependencies (Views::Base, Docs::Header, Docs::VisualCodeExample, etc.) so docs files can be loaded without errors. Rails apps can override these with full implementations. Includes button component documentation as the first example. * Add documentation for batch 1 components Add docs files for: - accordion - alert - alert_dialog - aspect_ratio - avatar - badge - breadcrumb - calendar - card * Add documentation for batch 2 components Add documentation files for: carousel, chart, checkbox, clipboard, codeblock, collapsible, combobox, command, context_menu, dialog * Add documentation for batch 3 components Migrate documentation files for dropdown_menu, form, hover_card, input, link, masked_input, and pagination components.
1 parent 271d07c commit 7bd455f

File tree

7 files changed

+809
-0
lines changed

7 files changed

+809
-0
lines changed
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# frozen_string_literal: true
2+
3+
class Views::Docs::DropdownMenu < Views::Base
4+
def view_template
5+
component = "DropdownMenu"
6+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
7+
render Docs::Header.new(title: "Dropdown Menu", description: "Displays a menu to the user — such as a set of actions or functions — triggered by a button.")
8+
9+
Heading(level: 2) { "Usage" }
10+
11+
render Docs::VisualCodeExample.new(title: "Example", context: self) do
12+
<<~RUBY
13+
DropdownMenu do
14+
DropdownMenuTrigger(class: 'w-full') do
15+
Button(variant: :outline) { "Open" }
16+
end
17+
DropdownMenuContent do
18+
DropdownMenuLabel { "My Account" }
19+
DropdownMenuSeparator
20+
DropdownMenuItem(href: '#') { "Profile" }
21+
DropdownMenuItem(href: '#') { "Billing" }
22+
DropdownMenuItem(href: '#') { "Team" }
23+
DropdownMenuItem(href: '#') { "Subscription" }
24+
end
25+
end
26+
RUBY
27+
end
28+
29+
render Docs::VisualCodeExample.new(title: "Placement", description: "If the DropdownMenu conflicts with edge, it will auto-adjust it's placement", context: self) do
30+
<<~RUBY
31+
div(class: 'grid grid-cols-1 sm:grid-cols-3 gap-4') do
32+
# -- TOP --
33+
DropdownMenu(options: { placement: 'top' }) do
34+
DropdownMenuTrigger(class: 'w-full') do
35+
Button(variant: :outline, class: 'w-full justify-center') { 'top' }
36+
end
37+
DropdownMenuContent do
38+
DropdownMenuLabel { "My Account" }
39+
DropdownMenuSeparator
40+
DropdownMenuItem(href: '#') { "Profile" }
41+
DropdownMenuItem(href: '#') { "Billing" }
42+
DropdownMenuItem(href: '#') { "Team" }
43+
DropdownMenuItem(href: '#') { "Subscription" }
44+
end
45+
end
46+
47+
DropdownMenu(options: { placement: 'top-start' }) do
48+
DropdownMenuTrigger(class: 'w-full') do
49+
Button(variant: :outline, class: 'w-full justify-center') { 'top-start' }
50+
end
51+
DropdownMenuContent do
52+
DropdownMenuLabel { "My Account" }
53+
DropdownMenuSeparator
54+
DropdownMenuItem(href: '#') { "Profile" }
55+
DropdownMenuItem(href: '#') { "Billing" }
56+
DropdownMenuItem(href: '#') { "Team" }
57+
DropdownMenuItem(href: '#') { "Subscription" }
58+
end
59+
end
60+
61+
DropdownMenu(options: { placement: 'top-end' }) do
62+
DropdownMenuTrigger(class: 'w-full') do
63+
Button(variant: :outline, class: 'w-full justify-center') { 'top-end' }
64+
end
65+
DropdownMenuContent do
66+
DropdownMenuLabel { "My Account" }
67+
DropdownMenuSeparator
68+
DropdownMenuItem(href: '#') { "Profile" }
69+
DropdownMenuItem(href: '#') { "Billing" }
70+
DropdownMenuItem(href: '#') { "Team" }
71+
DropdownMenuItem(href: '#') { "Subscription" }
72+
end
73+
end
74+
75+
# -- BOTTOM --
76+
DropdownMenu(options: { placement: 'bottom' }) do
77+
DropdownMenuTrigger(class: 'w-full') do
78+
Button(variant: :outline, class: 'w-full justify-center') { 'bottom' }
79+
end
80+
DropdownMenuContent do
81+
DropdownMenuLabel { "My Account" }
82+
DropdownMenuSeparator
83+
DropdownMenuItem(href: '#') { "Profile" }
84+
DropdownMenuItem(href: '#') { "Billing" }
85+
DropdownMenuItem(href: '#') { "Team" }
86+
DropdownMenuItem(href: '#') { "Subscription" }
87+
end
88+
end
89+
90+
DropdownMenu(options: { placement: 'bottom-start' }) do
91+
DropdownMenuTrigger(class: 'w-full') do
92+
Button(variant: :outline, class: 'w-full justify-center') { 'bottom-start' }
93+
end
94+
DropdownMenuContent do
95+
DropdownMenuLabel { "My Account" }
96+
DropdownMenuSeparator
97+
DropdownMenuItem(href: '#') { "Profile" }
98+
DropdownMenuItem(href: '#') { "Billing" }
99+
DropdownMenuItem(href: '#') { "Team" }
100+
DropdownMenuItem(href: '#') { "Subscription" }
101+
end
102+
end
103+
104+
DropdownMenu(options: { placement: 'bottom-end' }) do
105+
DropdownMenuTrigger(class: 'w-full') do
106+
Button(variant: :outline, class: 'w-full justify-center') { 'bottom-end' }
107+
end
108+
DropdownMenuContent do
109+
DropdownMenuLabel { "My Account" }
110+
DropdownMenuSeparator
111+
DropdownMenuItem(href: '#') { "Profile" }
112+
DropdownMenuItem(href: '#') { "Billing" }
113+
DropdownMenuItem(href: '#') { "Team" }
114+
DropdownMenuItem(href: '#') { "Subscription" }
115+
end
116+
end
117+
118+
# -- LEFT --
119+
DropdownMenu(options: { placement: 'left' }) do
120+
DropdownMenuTrigger(class: 'w-full') do
121+
Button(variant: :outline, class: 'w-full justify-center') { 'left' }
122+
end
123+
DropdownMenuContent do
124+
DropdownMenuLabel { "My Account" }
125+
DropdownMenuSeparator
126+
DropdownMenuItem(href: '#') { "Profile" }
127+
DropdownMenuItem(href: '#') { "Billing" }
128+
DropdownMenuItem(href: '#') { "Team" }
129+
DropdownMenuItem(href: '#') { "Subscription" }
130+
end
131+
end
132+
133+
DropdownMenu(options: { placement: 'left-start' }) do
134+
DropdownMenuTrigger(class: 'w-full') do
135+
Button(variant: :outline, class: 'w-full justify-center') { 'left-start' }
136+
end
137+
DropdownMenuContent do
138+
DropdownMenuLabel { "My Account" }
139+
DropdownMenuSeparator
140+
DropdownMenuItem(href: '#') { "Profile" }
141+
DropdownMenuItem(href: '#') { "Billing" }
142+
DropdownMenuItem(href: '#') { "Team" }
143+
DropdownMenuItem(href: '#') { "Subscription" }
144+
end
145+
end
146+
147+
DropdownMenu(options: { placement: 'left-end' }) do
148+
DropdownMenuTrigger(class: 'w-full') do
149+
Button(variant: :outline, class: 'w-full justify-center') { 'left-end' }
150+
end
151+
DropdownMenuContent do
152+
DropdownMenuLabel { "My Account" }
153+
DropdownMenuSeparator
154+
DropdownMenuItem(href: '#') { "Profile" }
155+
DropdownMenuItem(href: '#') { "Billing" }
156+
DropdownMenuItem(href: '#') { "Team" }
157+
DropdownMenuItem(href: '#') { "Subscription" }
158+
end
159+
end
160+
161+
# -- RIGHT --
162+
DropdownMenu(options: { placement: 'right' }) do
163+
DropdownMenuTrigger(class: 'w-full') do
164+
Button(variant: :outline, class: 'w-full justify-center') { 'right' }
165+
end
166+
DropdownMenuContent do
167+
DropdownMenuLabel { "My Account" }
168+
DropdownMenuSeparator
169+
DropdownMenuItem(href: '#') { "Profile" }
170+
DropdownMenuItem(href: '#') { "Billing" }
171+
DropdownMenuItem(href: '#') { "Team" }
172+
DropdownMenuItem(href: '#') { "Subscription" }
173+
end
174+
end
175+
176+
DropdownMenu(options: { placement: 'right-start' }) do
177+
DropdownMenuTrigger(class: 'w-full') do
178+
Button(variant: :outline, class: 'w-full justify-center') { 'right-start' }
179+
end
180+
DropdownMenuContent do
181+
DropdownMenuLabel { "My Account" }
182+
DropdownMenuSeparator
183+
DropdownMenuItem(href: '#') { "Profile" }
184+
DropdownMenuItem(href: '#') { "Billing" }
185+
DropdownMenuItem(href: '#') { "Team" }
186+
DropdownMenuItem(href: '#') { "Subscription" }
187+
end
188+
end
189+
190+
DropdownMenu(options: { placement: 'right-end' }) do
191+
DropdownMenuTrigger(class: 'w-full') do
192+
Button(variant: :outline, class: 'w-full justify-center') { 'right-end' }
193+
end
194+
DropdownMenuContent do
195+
DropdownMenuLabel { "My Account" }
196+
DropdownMenuSeparator
197+
DropdownMenuItem(href: '#') { "Profile" }
198+
DropdownMenuItem(href: '#') { "Billing" }
199+
DropdownMenuItem(href: '#') { "Team" }
200+
DropdownMenuItem(href: '#') { "Subscription" }
201+
end
202+
end
203+
end
204+
RUBY
205+
end
206+
207+
render Components::ComponentSetup::Tabs.new(component_name: component)
208+
209+
render Docs::ComponentsTable.new(component_files(component))
210+
end
211+
end
212+
end

lib/ruby_ui/form/form_docs.rb

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# frozen_string_literal: true
2+
3+
class Views::Docs::Form < Views::Base
4+
def view_template
5+
component = "Form"
6+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
7+
render Docs::Header.new(title: "Form", description: "Building forms with built-in client-side validations.")
8+
9+
Heading(level: 2) { "Usage" }
10+
11+
render Docs::VisualCodeExample.new(title: "Example", context: self) do
12+
<<~RUBY
13+
Form(class: "w-2/3 space-y-6") do
14+
FormField do
15+
FormFieldLabel { "Default error" }
16+
Input(placeholder: "Joel Drapper", required: true, minlength: "3") { "Joel Drapper" }
17+
FormFieldHint()
18+
FormFieldError()
19+
end
20+
Button(type: "submit") { "Save" }
21+
end
22+
RUBY
23+
end
24+
25+
render Docs::VisualCodeExample.new(title: "Disabled", context: self) do
26+
<<~RUBY
27+
FormField do
28+
FormFieldLabel { "Disabled" }
29+
Input(disabled: true, placeholder: "Joel Drapper", required: true, minlength: "3") { "Joel Drapper" }
30+
end
31+
RUBY
32+
end
33+
34+
render Docs::VisualCodeExample.new(title: "Aria Disabled", context: self) do
35+
<<~RUBY
36+
FormField do
37+
FormFieldLabel { "Aria Disabled" }
38+
Input(aria: {disabled: "true"}, placeholder: "Joel Drapper", required: true, minlength: "3") { "Joel Drapper" }
39+
end
40+
RUBY
41+
end
42+
43+
render Docs::VisualCodeExample.new(title: "Custom error message", context: self) do
44+
<<~RUBY
45+
Form(class: "w-2/3 space-y-6") do
46+
FormField do
47+
FormFieldLabel { "Custom error message" }
48+
Input(placeholder: "[email protected]", required: true, data_value_missing: "Custom error message")
49+
FormFieldError()
50+
end
51+
Button(type: "submit") { "Save" }
52+
end
53+
RUBY
54+
end
55+
56+
render Docs::VisualCodeExample.new(title: "Backend error", context: self) do
57+
<<~RUBY
58+
Form(class: "w-2/3 space-y-6") do
59+
FormField do
60+
FormFieldLabel { "Backend error" }
61+
Input(placeholder: "Joel Drapper", required: true)
62+
FormFieldError { "Error from backend" }
63+
end
64+
Button(type: "submit") { "Save" }
65+
end
66+
RUBY
67+
end
68+
69+
render Docs::VisualCodeExample.new(title: "Checkbox", context: self) do
70+
<<~RUBY
71+
Form(class: "w-2/3 space-y-6") do
72+
FormField do
73+
Checkbox(required: true)
74+
label(
75+
class:
76+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
77+
) { " Accept terms and conditions " }
78+
FormFieldError()
79+
end
80+
Button(type: "submit") { "Save" }
81+
end
82+
RUBY
83+
end
84+
85+
render Docs::VisualCodeExample.new(title: "Select", context: self) do
86+
<<~RUBY
87+
Form(class: "w-2/3 space-y-6") do
88+
FormField do
89+
FormFieldLabel { "Select" }
90+
Select do
91+
SelectInput(required: true)
92+
SelectTrigger do
93+
SelectValue(placeholder: "Select a fruit")
94+
end
95+
SelectContent() do
96+
SelectGroup do
97+
SelectLabel { "Fruits" }
98+
SelectItem(value: "apple") { "Apple" }
99+
SelectItem(value: "orange") { "Orange" }
100+
SelectItem(value: "banana") { "Banana" }
101+
SelectItem(value: "watermelon") { "Watermelon" }
102+
end
103+
end
104+
end
105+
FormFieldError()
106+
end
107+
Button(type: "submit") { "Save" }
108+
end
109+
RUBY
110+
end
111+
112+
render Docs::VisualCodeExample.new(title: "Combobox", context: self) do
113+
<<~RUBY
114+
Form(class: "w-2/3 space-y-6") do
115+
FormField do
116+
FormFieldLabel { "Combobox" }
117+
118+
Combobox do
119+
ComboboxTrigger placeholder: "Pick value"
120+
121+
ComboboxPopover do
122+
ComboboxSearchInput(placeholder: "Pick value or type anything")
123+
124+
ComboboxList do
125+
ComboboxEmptyState { "No result" }
126+
127+
ComboboxListGroup label: "Fruits" do
128+
ComboboxItem do
129+
ComboboxRadio(name: "food", value: "apple", required: true)
130+
span { "Apple" }
131+
end
132+
133+
ComboboxItem do
134+
ComboboxRadio(name: "food", value: "banana", required: true)
135+
span { "Banana" }
136+
end
137+
end
138+
139+
ComboboxListGroup label: "Vegetable" do
140+
ComboboxItem do
141+
ComboboxRadio(name: "food", value: "brocoli", required: true)
142+
span { "Broccoli" }
143+
end
144+
145+
ComboboxItem do
146+
ComboboxRadio(name: "food", value: "carrot", required: true)
147+
span { "Carrot" }
148+
end
149+
end
150+
151+
ComboboxListGroup label: "Others" do
152+
ComboboxItem do
153+
ComboboxRadio(name: "food", value: "chocolate", required: true)
154+
span { "Chocolate" }
155+
end
156+
157+
ComboboxItem do
158+
ComboboxRadio(name: "food", value: "milk", required: true)
159+
span { "Milk" }
160+
end
161+
end
162+
end
163+
end
164+
end
165+
166+
FormFieldError()
167+
end
168+
Button(type: "submit") { "Save" }
169+
end
170+
RUBY
171+
end
172+
173+
render Components::ComponentSetup::Tabs.new(component_name: component)
174+
175+
render Docs::ComponentsTable.new(component_files(component))
176+
end
177+
end
178+
end

0 commit comments

Comments
 (0)