Skip to content

Commit 581f9bc

Browse files
committed
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.
1 parent 1171416 commit 581f9bc

File tree

9 files changed

+273
-1
lines changed

9 files changed

+273
-1
lines changed

CONTRIBUTING.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,22 @@ While we don't have specific test coverage requirements, all contributions shoul
4141

4242
If your changes include new components, modify how components should be used, or add new behaviors, it is highly recommended to also open a PR on the [ruby-ui/web](https://github.com/ruby-ui/web) repository. This ensures the documentation website stays up-to-date with the latest component changes.
4343

44+
### Installing Documentation Files
45+
46+
RubyUI includes documentation files for each component that can be installed into your Rails application. These files are located at `lib/ruby_ui/{component}/{component}_docs.rb` and provide usage examples for each component.
47+
48+
To install the documentation files:
49+
50+
```bash
51+
bin/rails g ruby_ui:install:docs
52+
```
53+
54+
To overwrite existing documentation files:
55+
56+
```bash
57+
bin/rails g ruby_ui:install:docs --force
58+
```
59+
60+
This will copy the documentation files to `app/views/docs/` in your Rails application.
61+
4462
Thank you for contributing to make RubyUI better!
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# frozen_string_literal: true
2+
3+
require "rails/generators"
4+
5+
module RubyUI
6+
module Generators
7+
module Install
8+
class DocsGenerator < Rails::Generators::Base
9+
namespace "ruby_ui:install:docs"
10+
source_root File.expand_path("../../../ruby_ui", __dir__)
11+
class_option :force, type: :boolean, default: false
12+
13+
def copy_docs_files
14+
say "Installing RubyUI documentation files..."
15+
16+
docs_file_paths.each do |source_path|
17+
dest_filename = File.basename(source_path).sub("_docs", "")
18+
copy_file source_path, Rails.root.join("app/views/docs", dest_filename), force: options["force"]
19+
end
20+
21+
say ""
22+
say "Documentation installed to app/views/docs/", :green
23+
end
24+
25+
private
26+
27+
def docs_file_paths
28+
Dir.glob(File.join(self.class.source_root, "*", "*_docs.rb"))
29+
end
30+
end
31+
end
32+
end
33+
end

lib/ruby_ui/button/button_docs.rb

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# frozen_string_literal: true
2+
3+
class Views::Docs::Button < Views::Base
4+
def view_template
5+
component = "Button"
6+
7+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
8+
render Docs::Header.new(title: "Button", description: "Displays a button or a component that looks like a button.")
9+
10+
Heading(level: 2) { "Usage" }
11+
12+
render Docs::VisualCodeExample.new(title: "Example", context: self) do
13+
<<~RUBY
14+
Button { "Button" }
15+
RUBY
16+
end
17+
18+
render Docs::VisualCodeExample.new(title: "Primary", context: self) do
19+
<<~RUBY
20+
Button(variant: :primary) { "Primary" }
21+
RUBY
22+
end
23+
24+
render Docs::VisualCodeExample.new(title: "Secondary", context: self) do
25+
<<~RUBY
26+
Button(variant: :secondary) { "Secondary" }
27+
RUBY
28+
end
29+
30+
render Docs::VisualCodeExample.new(title: "Destructive", context: self) do
31+
<<~RUBY
32+
Button(variant: :destructive) { "Destructive" }
33+
RUBY
34+
end
35+
36+
render Docs::VisualCodeExample.new(title: "Outline", context: self) do
37+
<<~RUBY
38+
Button(variant: :outline) { "Outline" }
39+
RUBY
40+
end
41+
42+
render Docs::VisualCodeExample.new(title: "Ghost", context: self) do
43+
<<~RUBY
44+
Button(variant: :ghost) { "Ghost" }
45+
RUBY
46+
end
47+
48+
render Docs::VisualCodeExample.new(title: "Link", context: self) do
49+
<<~RUBY
50+
Button(variant: :link) { "Link" }
51+
RUBY
52+
end
53+
54+
render Docs::VisualCodeExample.new(title: "Disabled", context: self) do
55+
<<~RUBY
56+
Button(disabled: true) { "Disabled" }
57+
RUBY
58+
end
59+
60+
render Docs::VisualCodeExample.new(title: "Aria Disabled", context: self) do
61+
<<~RUBY
62+
Button(aria: {disabled: "true"}) { "Aria Disabled" }
63+
RUBY
64+
end
65+
66+
render Docs::VisualCodeExample.new(title: "Icon", context: self) do
67+
<<~RUBY
68+
Button(variant: :outline, icon: true) do
69+
svg(
70+
xmlns: "http://www.w3.org/2000/svg",
71+
viewbox: "0 0 20 20",
72+
fill: "currentColor",
73+
class: "w-5 h-5"
74+
) do |s|
75+
s.path(
76+
fill_rule: "evenodd",
77+
d:
78+
"M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z",
79+
clip_rule: "evenodd"
80+
)
81+
end
82+
end
83+
RUBY
84+
end
85+
86+
render Docs::VisualCodeExample.new(title: "With Icon", context: self) do
87+
<<~RUBY
88+
Button(variant: :primary) do
89+
svg(
90+
xmlns: "http://www.w3.org/2000/svg",
91+
fill: "none",
92+
viewbox: "0 0 24 24",
93+
stroke_width: "1.5",
94+
stroke: "currentColor",
95+
class: "w-4 h-4 mr-2"
96+
) do |s|
97+
s.path(
98+
stroke_linecap: "round",
99+
stroke_linejoin: "round",
100+
d:
101+
"M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75"
102+
)
103+
end
104+
span { "Login with Email" }
105+
end
106+
RUBY
107+
end
108+
109+
render Docs::VisualCodeExample.new(title: "With Icon", context: self) do
110+
<<~RUBY
111+
Button(variant: :primary, disabled: true) do
112+
svg(
113+
xmlns: "http://www.w3.org/2000/svg",
114+
viewbox: "0 0 20 20",
115+
fill: "currentColor",
116+
class: "w-4 h-4 mr-2 animate-spin"
117+
) do |s|
118+
s.path(
119+
fill_rule: "evenodd",
120+
d:
121+
"M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z",
122+
clip_rule: "evenodd"
123+
)
124+
end
125+
span { "Please wait" }
126+
end
127+
RUBY
128+
end
129+
130+
render Docs::VisualCodeExample.new(title: "Submit", context: self) do
131+
<<~RUBY
132+
Button(variant: :primary, type: :submit) do
133+
span { "Submit application" }
134+
end
135+
RUBY
136+
end
137+
138+
render Components::ComponentSetup::Tabs.new(component_name: component)
139+
140+
render Docs::ComponentsTable.new(component_files(component))
141+
end
142+
end
143+
end

lib/ruby_ui/docs/base.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# frozen_string_literal: true
2+
3+
module Views
4+
class Base < Phlex::HTML
5+
def Heading(level:, &)
6+
tag = :"h#{level}"
7+
send(tag, &)
8+
end
9+
10+
def component_files(component_name)
11+
[]
12+
end
13+
end
14+
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
module Components
4+
module ComponentSetup
5+
class Tabs < Phlex::HTML
6+
def initialize(component_name:)
7+
@component_name = component_name
8+
end
9+
10+
def view_template
11+
# Minimal stub - empty by default
12+
end
13+
end
14+
end
15+
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# frozen_string_literal: true
2+
3+
module Docs
4+
class ComponentsTable < Phlex::HTML
5+
def initialize(files)
6+
@files = files
7+
end
8+
9+
def view_template
10+
# Minimal stub - empty by default
11+
end
12+
end
13+
end

lib/ruby_ui/docs/header.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# frozen_string_literal: true
2+
3+
module Docs
4+
class Header < Phlex::HTML
5+
def initialize(title:, description: nil)
6+
@title = title
7+
@description = description
8+
end
9+
10+
def view_template
11+
div do
12+
h1 { @title }
13+
p { @description } if @description
14+
end
15+
end
16+
end
17+
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# frozen_string_literal: true
2+
3+
module Docs
4+
class VisualCodeExample < Phlex::HTML
5+
def initialize(title:, context:)
6+
@title = title
7+
@context = context
8+
end
9+
10+
def view_template(&block)
11+
code = block.call
12+
div do
13+
h3 { @title }
14+
pre { code }
15+
@context.instance_eval(code)
16+
end
17+
end
18+
end
19+
end

test/test_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
module RubyUI
1111
extend Phlex::Kit
1212

13-
Dir.glob("lib/ruby_ui/**/*.rb").map do |path|
13+
Dir.glob("lib/ruby_ui/**/*.rb").reject { |f| f.include?("/docs/") || f.end_with?("_docs.rb") }.map do |path|
1414
class_name = path.split("/").last.delete_suffix(".rb").split("_").map(&:capitalize).join.to_sym
1515

1616
autoload class_name, path

0 commit comments

Comments
 (0)