showcase-rails 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +76 -7
  3. data/app/assets/builds/showcase.css +78 -78
  4. data/app/controllers/showcase/engine_controller.rb +12 -0
  5. data/app/controllers/showcase/pages_controller.rb +1 -4
  6. data/app/controllers/showcase/previews_controller.rb +5 -0
  7. data/app/models/showcase/{page/options.rb → options.rb} +1 -1
  8. data/app/models/showcase/path.rb +49 -12
  9. data/app/models/showcase/preview.rb +94 -0
  10. data/app/models/showcase/{page/sample.rb → sample.rb} +10 -6
  11. data/app/views/layouts/showcase.html.erb +3 -3
  12. data/app/views/showcase/engine/_javascripts.html.erb +1 -0
  13. data/app/views/showcase/engine/_options.html.erb +29 -0
  14. data/app/views/showcase/engine/_preview.html.erb +26 -0
  15. data/app/views/showcase/engine/_root.html.erb +13 -0
  16. data/app/views/showcase/engine/_sample.html.erb +37 -0
  17. data/app/views/showcase/engine/_stylesheets.html.erb +1 -0
  18. data/app/views/showcase/engine/index.html.erb +33 -0
  19. data/app/views/showcase/engine/path/_path.html.erb +3 -0
  20. data/app/views/showcase/engine/path/_tree.html.erb +4 -0
  21. data/app/views/showcase/engine/show.html.erb +1 -0
  22. data/config/routes.rb +2 -2
  23. data/config/tailwind.config.js +1 -0
  24. data/lib/showcase/integration_test.rb +23 -0
  25. data/lib/showcase/route_helper.rb +8 -0
  26. data/lib/showcase/version.rb +1 -1
  27. data/lib/showcase.rb +14 -10
  28. data/lib/tasks/showcase_tasks.rake +30 -4
  29. metadata +20 -14
  30. data/app/controllers/showcase/application_controller.rb +0 -3
  31. data/app/models/showcase/page.rb +0 -45
  32. data/app/views/showcase/_root.html.erb +0 -13
  33. data/app/views/showcase/pages/_options.html.erb +0 -27
  34. data/app/views/showcase/pages/_page.html.erb +0 -26
  35. data/app/views/showcase/pages/_sample.html.erb +0 -37
  36. data/app/views/showcase/pages/index.html.erb +0 -33
  37. data/app/views/showcase/pages/show.html.erb +0 -1
  38. data/app/views/showcase/path/_tree.html.erb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e84e94d8b861787bdbbcd87577beab2496d60c231b8803e1ba83d0a0e488708
4
- data.tar.gz: 7e95b0377b27c9f17df42f35a1491fa6d4aafb821bfc1df1db4c496b42fb86b4
3
+ metadata.gz: 38aa85b8e5c0a98ee2dbb45f0a184981de8a86371efbb2237e2a06f0d2f56eef
4
+ data.tar.gz: b4e44e25c0b76afcd33a2dc155cae5bdebe1a5a9ca3cd670d4cc2bae94d7f38b
5
5
  SHA512:
6
- metadata.gz: a1666dce260d55ba265e654dfd39bde2304bdb130b04b389f38352d16bdbe8771031fa79264ff2e13d479018aa33fb069dcc4e1cf835021e0003b9030c54f213
7
- data.tar.gz: f19f23e1545e075bd2f78e1750b3da6838ab78850368a866f2351ac0dacf6aad9b64ea680fefc0618721baf70c025625e29a2fdee7ceb616a4ca74dfaf9309ab
6
+ metadata.gz: 995f0b2b6f765c93936adb968c501218fccc592befe12df06e0aa19b9b092a0ea385cab086763f0b855e3e86eb784edf8365143b49e69dbda934bd8ce608016d
7
+ data.tar.gz: 123c59c68801489dec09d40c144c64fbc09dc6c65925833cbeade7a4c8f479595b6d59274d3ab7e74cdd18495dd8276ef60fa714b3f87662c786d6c190d55c39
data/README.md CHANGED
@@ -1,13 +1,14 @@
1
1
  # Showcase
2
2
 
3
- Showcase lets you build previews for partials, components, view helpers and Stimulus controllers.
3
+ Showcase lets you build previews for your partials, components, view helpers, Stimulus controllers and more.
4
4
 
5
- Add a view template to `app/views/showcases` and it'll show up in Showcase's menu.
5
+ Add a template to `app/views/showcase/previews` and it'll show up in Showcase's menu.
6
6
 
7
7
  Here's how to showcase a standard button component:
8
8
 
9
9
  ```erb
10
- <%# app/views/showcases/components/button.html.erb %>
10
+ <%# app/views/showcase/previews/_button.html.erb %>
11
+ <% showcase.title "Button" %> <%# `title` is optional and inferred from the filename, by default. %>
11
12
  <% showcase.description "This button component handles what we click on" %>
12
13
 
13
14
  <% showcase.sample "Basic" do %>
@@ -19,17 +20,85 @@ Here's how to showcase a standard button component:
19
20
  <% end %>
20
21
 
21
22
  <% showcase.options do |o| %>
22
- <% o.required :content, String, "The content to output as the button text" %>
23
- <% o.optional :mode, default: :small, values: %i[ small medium large ], description: "We support three modes" %>
23
+ <% o.required :content, "The content to output as the button text" %>
24
+ <% o.optional :mode, "We support three modes", default: :small, options: %i[ small medium large ] %>
24
25
  <% end %>
25
26
  ```
26
27
 
28
+ Which will then render the following:
29
+
30
+ ![](/readme/example.png?raw=true "Showcase showing a button component")
31
+
32
+ ## Automatic smokescreen testing
33
+
34
+ Run `bin/rails showcase:install:integration_test` to automatic testing installed in `test/integration/showcase_test.rb`.
35
+
36
+ This will render every Showcase you've defined and assert they respond with `200 OK`. You can add custom assertions by overriding `assert_showcase_preview`.
37
+
38
+ ## View examples
39
+
40
+ Clone the repository, run `bundle install`, then run `bin/rails server`, visit localhost:3000 in your browser.
41
+
42
+ ## Overriding Showcase's default rendering
43
+
44
+ Showcase's rendering happens through two controllers:
45
+
46
+ 1. [`Showcase::EngineController`](app/controllers/showcase/engine_controller.rb)
47
+ 1. [`Showcase::PreviewsController`](app/controllers/showcase/previews_controller.rb)
48
+
49
+ All paths shown here are assumed to be in `app/views`.
50
+
51
+ The actions all use a `layout "showcase"`, which renders like this:
52
+
53
+ - [layouts/showcase.html.erb](app/views/layouts/showcase.html.erb)
54
+ - [showcase/engine/_root.html.erb](app/views/showcase/engine/_root.html.erb)
55
+ - [showcase/engine/path/_tree.html.erb](app/views/showcase/engine/path/_tree.html.erb)
56
+
57
+ So for `Showcase::EngineController#index` we render:
58
+
59
+ - [showcase/engine/index.html.erb](app/views/showcase/engine/index.html.erb)
60
+
61
+ And for `Showcase::PreviewsController#show` we render:
62
+
63
+ - [showcase/engine/show.html.erb](app/views/showcase/engine/show.html.erb)
64
+ - [showcase/engine/_preview.html.erb](app/views/showcase/engine/_preview.html.erb)
65
+ - [showcase/engine/_sample.html.erb](app/views/showcase/engine/_sample.html.erb)
66
+ - [showcase/engine/_options.html.erb](app/views/showcase/engine/_options.html.erb)
67
+
68
+ If you want to override any specific rendering, e.g. how a `Showcase::Preview` is rendered,
69
+ copy the file from our repo `app/views` directory into your `app/views` directory.
70
+
71
+ ### Loading your own assets
72
+
73
+ Showcase bundles its own `showcase.js` and `showcase.css` asset files through
74
+ Action View's [javascript_include_tag][] and [stylesheet_link_tag][].
75
+
76
+ If your assets require more sophisticated loading techniques, declare your own
77
+ versions of the [showcase/engine/_javascripts.html.erb][] and
78
+ [showcase/engine/_stylesheets.html.erb][] partials. When customizing those
79
+ partials, make sure to include `"showcase"` in your list of assets.
80
+
81
+
82
+ [javascript_include_tag]: https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html#method-i-javascript_include_tag
83
+ [stylesheet_link_tag]: https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html#method-i-stylesheet_link_tag
84
+ [showcase/engine/_javascripts.html.erb]: ./showcase/engine/_javascripts.html.erb
85
+ [showcase/engine/_stylesheets.html.erb]: ./showcase/engine/_stylesheets.html.erb
86
+
27
87
  ## Installation
28
88
 
29
- Add this line to your application's Gemfile:
89
+ Add this line to your application's Gemfile. If you're utilizing the
90
+ [Showcase::IntegrationTest](lib/showcase/integration_test.rb) class, make sure
91
+ that the `showcase-rails` gems is available to your test environment:
92
+
30
93
 
31
94
  ```ruby
32
- gem "showcase"
95
+ # nested in the default group
96
+ gem "showcase-rails"
97
+
98
+ # or nested in the :development and :test groups
99
+ group :development, :test do
100
+ gem "showcase-rails"
101
+ end
33
102
  ```
34
103
 
35
104
  And then execute:
@@ -679,280 +679,280 @@ select {
679
679
  --tw-backdrop-sepia: ;
680
680
  }
681
681
 
682
- .relative {
682
+ .sc-relative {
683
683
  position: relative;
684
684
  }
685
685
 
686
- .col-span-3 {
686
+ .sc-col-span-3 {
687
687
  grid-column: span 3 / span 3;
688
688
  }
689
689
 
690
- .col-span-9 {
690
+ .sc-col-span-9 {
691
691
  grid-column: span 9 / span 9;
692
692
  }
693
693
 
694
- .mb-2 {
694
+ .sc-mb-2 {
695
695
  margin-bottom: 0.5rem;
696
696
  }
697
697
 
698
- .mb-4 {
698
+ .sc-mb-4 {
699
699
  margin-bottom: 1rem;
700
700
  }
701
701
 
702
- .inline-block {
702
+ .sc-inline-block {
703
703
  display: inline-block;
704
704
  }
705
705
 
706
- .flex {
706
+ .sc-flex {
707
707
  display: flex;
708
708
  }
709
709
 
710
- .table {
710
+ .sc-table {
711
711
  display: table;
712
712
  }
713
713
 
714
- .grid {
714
+ .sc-grid {
715
715
  display: grid;
716
716
  }
717
717
 
718
- .h-full {
718
+ .sc-h-full {
719
719
  height: 100%;
720
720
  }
721
721
 
722
- .max-h-20 {
722
+ .sc-max-h-20 {
723
723
  max-height: 5rem;
724
724
  }
725
725
 
726
- .min-h-screen {
726
+ .sc-min-h-screen {
727
727
  min-height: 100vh;
728
728
  }
729
729
 
730
- .w-full {
730
+ .sc-w-full {
731
731
  width: 100%;
732
732
  }
733
733
 
734
- .border-collapse {
734
+ .sc-border-collapse {
735
735
  border-collapse: collapse;
736
736
  }
737
737
 
738
- .cursor-pointer {
738
+ .sc-cursor-pointer {
739
739
  cursor: pointer;
740
740
  }
741
741
 
742
- .select-none {
742
+ .sc-select-none {
743
743
  -webkit-user-select: none;
744
744
  -moz-user-select: none;
745
745
  user-select: none;
746
746
  }
747
747
 
748
- .list-none {
748
+ .sc-list-none {
749
749
  list-style-type: none;
750
750
  }
751
751
 
752
- .grid-cols-12 {
752
+ .sc-grid-cols-12 {
753
753
  grid-template-columns: repeat(12, minmax(0, 1fr));
754
754
  }
755
755
 
756
- .flex-col {
756
+ .sc-flex-col {
757
757
  flex-direction: column;
758
758
  }
759
759
 
760
- .flex-wrap {
760
+ .sc-flex-wrap {
761
761
  flex-wrap: wrap;
762
762
  }
763
763
 
764
- .items-center {
764
+ .sc-items-center {
765
765
  align-items: center;
766
766
  }
767
767
 
768
- .space-y-8 > :not([hidden]) ~ :not([hidden]) {
768
+ .sc-space-y-8 > :not([hidden]) ~ :not([hidden]) {
769
769
  --tw-space-y-reverse: 0;
770
770
  margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
771
771
  margin-bottom: calc(2rem * var(--tw-space-y-reverse));
772
772
  }
773
773
 
774
- .space-x-2 > :not([hidden]) ~ :not([hidden]) {
774
+ .sc-space-x-2 > :not([hidden]) ~ :not([hidden]) {
775
775
  --tw-space-x-reverse: 0;
776
776
  margin-right: calc(0.5rem * var(--tw-space-x-reverse));
777
777
  margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
778
778
  }
779
779
 
780
- .space-y-4 > :not([hidden]) ~ :not([hidden]) {
780
+ .sc-space-y-4 > :not([hidden]) ~ :not([hidden]) {
781
781
  --tw-space-y-reverse: 0;
782
782
  margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
783
783
  margin-bottom: calc(1rem * var(--tw-space-y-reverse));
784
784
  }
785
785
 
786
- .overflow-hidden {
787
- overflow: hidden;
788
- }
789
-
790
- .overflow-scroll {
786
+ .sc-overflow-scroll {
791
787
  overflow: scroll;
792
788
  }
793
789
 
794
- .overflow-x-auto {
790
+ .sc-overflow-x-auto {
795
791
  overflow-x: auto;
796
792
  }
797
793
 
798
- .truncate {
794
+ .sc-overflow-y-auto {
795
+ overflow-y: auto;
796
+ }
797
+
798
+ .sc-truncate {
799
799
  overflow: hidden;
800
800
  text-overflow: ellipsis;
801
801
  white-space: nowrap;
802
802
  }
803
803
 
804
- .rounded-md {
804
+ .sc-rounded-md {
805
805
  border-radius: 0.375rem;
806
806
  }
807
807
 
808
- .border {
808
+ .sc-border {
809
809
  border-width: 1px;
810
810
  }
811
811
 
812
- .border-0 {
812
+ .sc-border-0 {
813
813
  border-width: 0px;
814
814
  }
815
815
 
816
- .border-r {
817
- border-right-width: 1px;
816
+ .sc-border-t {
817
+ border-top-width: 1px;
818
818
  }
819
819
 
820
- .border-t {
821
- border-top-width: 1px;
820
+ .sc-border-r {
821
+ border-right-width: 1px;
822
822
  }
823
823
 
824
- .border-b {
824
+ .sc-border-b {
825
825
  border-bottom-width: 1px;
826
826
  }
827
827
 
828
- .border-gray-200 {
828
+ .sc-border-gray-200 {
829
829
  --tw-border-opacity: 1;
830
830
  border-color: rgb(229 231 235 / var(--tw-border-opacity));
831
831
  }
832
832
 
833
- .bg-slate-50 {
833
+ .sc-bg-slate-50 {
834
834
  --tw-bg-opacity: 1;
835
835
  background-color: rgb(248 250 252 / var(--tw-bg-opacity));
836
836
  }
837
837
 
838
- .bg-slate-100\/50 {
838
+ .sc-bg-slate-100\/50 {
839
839
  background-color: rgb(241 245 249 / 0.5);
840
840
  }
841
841
 
842
- .bg-indigo-50 {
842
+ .sc-bg-indigo-50 {
843
843
  --tw-bg-opacity: 1;
844
844
  background-color: rgb(238 242 255 / var(--tw-bg-opacity));
845
845
  }
846
846
 
847
- .p-12 {
848
- padding: 3rem;
847
+ .sc-p-4 {
848
+ padding: 1rem;
849
849
  }
850
850
 
851
- .p-4 {
852
- padding: 1rem;
851
+ .sc-p-12 {
852
+ padding: 3rem;
853
853
  }
854
854
 
855
- .py-5 {
856
- padding-top: 1.25rem;
857
- padding-bottom: 1.25rem;
855
+ .sc-px-4 {
856
+ padding-left: 1rem;
857
+ padding-right: 1rem;
858
858
  }
859
859
 
860
- .py-2 {
860
+ .sc-py-2 {
861
861
  padding-top: 0.5rem;
862
862
  padding-bottom: 0.5rem;
863
863
  }
864
864
 
865
- .px-4 {
866
- padding-left: 1rem;
867
- padding-right: 1rem;
865
+ .sc-py-5 {
866
+ padding-top: 1.25rem;
867
+ padding-bottom: 1.25rem;
868
868
  }
869
869
 
870
- .px-8 {
870
+ .sc-px-8 {
871
871
  padding-left: 2rem;
872
872
  padding-right: 2rem;
873
873
  }
874
874
 
875
- .pl-4 {
875
+ .sc-pl-4 {
876
876
  padding-left: 1rem;
877
877
  }
878
878
 
879
- .pt-7 {
879
+ .sc-pt-7 {
880
880
  padding-top: 1.75rem;
881
881
  }
882
882
 
883
- .text-2xl {
884
- font-size: 1.5rem;
885
- line-height: 2rem;
886
- }
887
-
888
- .text-xl {
883
+ .sc-text-xl {
889
884
  font-size: 1.25rem;
890
885
  line-height: 1.75rem;
891
886
  }
892
887
 
893
- .text-3xl {
888
+ .sc-text-3xl {
894
889
  font-size: 1.875rem;
895
890
  line-height: 2.25rem;
896
891
  }
897
892
 
898
- .text-base {
893
+ .sc-text-base {
899
894
  font-size: 1rem;
900
895
  line-height: 1.5rem;
901
896
  }
902
897
 
903
- .font-black {
904
- font-weight: 900;
898
+ .sc-text-2xl {
899
+ font-size: 1.5rem;
900
+ line-height: 2rem;
905
901
  }
906
902
 
907
- .font-semibold {
903
+ .sc-font-semibold {
908
904
  font-weight: 600;
909
905
  }
910
906
 
911
- .font-normal {
907
+ .sc-font-normal {
912
908
  font-weight: 400;
913
909
  }
914
910
 
915
- .font-medium {
911
+ .sc-font-black {
912
+ font-weight: 900;
913
+ }
914
+
915
+ .sc-font-medium {
916
916
  font-weight: 500;
917
917
  }
918
918
 
919
- .italic {
919
+ .sc-italic {
920
920
  font-style: italic;
921
921
  }
922
922
 
923
- .leading-snug {
923
+ .sc-leading-snug {
924
924
  line-height: 1.375;
925
925
  }
926
926
 
927
- .text-black {
927
+ .sc-text-black {
928
928
  --tw-text-opacity: 1;
929
929
  color: rgb(0 0 0 / var(--tw-text-opacity));
930
930
  }
931
931
 
932
- .hover\:select-all:hover {
932
+ .hover\:sc-select-all:hover {
933
933
  -webkit-user-select: all;
934
934
  -moz-user-select: all;
935
935
  user-select: all;
936
936
  }
937
937
 
938
- .hover\:bg-indigo-50:hover {
938
+ .hover\:sc-bg-indigo-50:hover {
939
939
  --tw-bg-opacity: 1;
940
940
  background-color: rgb(238 242 255 / var(--tw-bg-opacity));
941
941
  }
942
942
 
943
943
  @media (min-width: 768px) {
944
- .md\:text-lg {
944
+ .md\:sc-text-lg {
945
945
  font-size: 1.125rem;
946
946
  line-height: 1.75rem;
947
947
  }
948
948
  }
949
949
 
950
950
  @media (min-width: 1280px) {
951
- .xl\:col-span-2 {
951
+ .xl\:sc-col-span-2 {
952
952
  grid-column: span 2 / span 2;
953
953
  }
954
954
 
955
- .xl\:col-span-10 {
955
+ .xl\:sc-col-span-10 {
956
956
  grid-column: span 10 / span 10;
957
957
  }
958
958
  }
@@ -0,0 +1,12 @@
1
+ class Showcase::EngineController < ActionController::Base
2
+ layout "showcase"
3
+
4
+ helper Showcase::RouteHelper
5
+
6
+ if defined?(::ApplicationController)
7
+ helper all_helpers_from_path ::ApplicationController.helpers_path
8
+ end
9
+
10
+ def index
11
+ end
12
+ end
@@ -1,7 +1,4 @@
1
- class Showcase::PagesController < Showcase::ApplicationController
2
- def index
3
- end
4
-
1
+ class Showcase::PagesController < Showcase::EngineController
5
2
  def show
6
3
  @page = Showcase::Path.new(params[:id]).page_for view_context
7
4
  end
@@ -0,0 +1,5 @@
1
+ class Showcase::PreviewsController < Showcase::EngineController
2
+ def show
3
+ @preview = Showcase::Path.new(params[:id]).preview_for view_context
4
+ end
5
+ end
@@ -1,4 +1,4 @@
1
- class Showcase::Page::Options
1
+ class Showcase::Options
2
2
  include Enumerable
3
3
 
4
4
  def initialize(view_context)
@@ -1,30 +1,67 @@
1
1
  class Showcase::Path
2
- class Tree < Struct.new(:id, :paths)
2
+ class Tree < Struct.new(:id, :children)
3
+ def initialize(id, children = [])
4
+ super
5
+ end
6
+ delegate :<<, to: :children
7
+
8
+ cached_partial_path = "showcase/engine/path/tree"
9
+ define_method(:to_partial_path) { cached_partial_path }
10
+
3
11
  def name
4
- root? ? "Pages" : id
12
+ root? ? "Previews" : id
5
13
  end
6
14
 
7
15
  def root?
8
16
  id == "."
9
17
  end
10
- end
11
18
 
12
- def self.tree
13
- all.group_by(&:dirname).map { Tree.new _1, _2 }
19
+ def ordered_children
20
+ children.partition { !_1.is_a?(Tree) }.flatten
21
+ end
22
+
23
+ def ordered_paths
24
+ children.flat_map { _1.is_a?(Tree) ? _1.ordered_paths : _1 }
25
+ end
26
+
27
+ def self.index(...)
28
+ new(:discardable_root).tap { _1.index(...) }.ordered_children
29
+ end
30
+
31
+ def index(paths)
32
+ paths.each do |path|
33
+ ids = yield path
34
+ ids.inject(self, :edge_for) << path
35
+ end
36
+ end
37
+
38
+ def edge_for(id)
39
+ find(id) || insert(id)
40
+ end
41
+
42
+ private
43
+
44
+ def find(id) = children.find { _1.id == id }
45
+ def insert(id) = self.class.new(id).tap { self << _1 }
14
46
  end
15
47
 
16
- def self.all
17
- Showcase.filenames.map { new _1 }.sort_by!(&:id)
48
+ def self.tree
49
+ paths = Showcase.previews.map { new _1 }.sort_by!(&:id)
50
+ Tree.index(paths, &:segments)
18
51
  end
19
52
 
20
- attr_reader :id, :dirname, :basename
53
+ attr_reader :id, :segments, :basename
21
54
 
22
55
  def initialize(path)
23
- @id = path.split(".").first
24
- @dirname, @basename = File.split(@id)
56
+ @id = path.split(".").first.delete_prefix("_").sub(/\/_/, "/")
57
+ @basename = File.basename(@id)
58
+ @segments = File.dirname(@id).split("/")
25
59
  end
26
60
 
27
- def page_for(view_context)
28
- Showcase::Page.new(view_context, id: id, title: basename.titleize).tap(&:render_template)
61
+ cached_partial_path = "showcase/engine/path/path"
62
+ define_method(:to_partial_path) { cached_partial_path }
63
+
64
+ def preview_for(view_context)
65
+ Showcase::Preview.new(view_context, id: id, title: basename.titleize).tap(&:render_associated_partial)
29
66
  end
30
67
  end
@@ -0,0 +1,94 @@
1
+ class Showcase::Preview
2
+ attr_reader :id, :badges, :samples
3
+
4
+ def initialize(view_context, id:, title: nil)
5
+ @view_context, @id = view_context, id
6
+ @badges, @samples = [], []
7
+ title title
8
+ end
9
+
10
+ # Set a custom title for the Preview. By default, it's automatically inferred from the sidebar title,
11
+ # e.g. showcase/previews/_button.html.erb will have Button as the title.
12
+ def title(content = nil)
13
+ @title = content if content
14
+ @title
15
+ end
16
+
17
+ # Describe the Preview in more detail to help guide other developers on what the inner partial/component etc.'s purpose is.
18
+ #
19
+ # <% showcase.description "Our button element" %>
20
+ # <% showcase.description do %>
21
+ # <h3>Our button element</h3> — <span>but with custom description HTML</span>
22
+ # <% end %>
23
+ def description(content = nil, &block)
24
+ @description = content || @view_context.capture(&block) if content || block_given?
25
+ @description
26
+ end
27
+
28
+ # Optional badges you can give to a preview:
29
+ #
30
+ # <% showcase.badge :partial, :view_helper %>
31
+ def badge(*badges)
32
+ @badges.concat badges
33
+ end
34
+
35
+ # Adds a named sample to demonstrate with the Showcase can do.
36
+ #
37
+ # By default, sample takes a block that'll automatically have its source extracted, like this:
38
+ #
39
+ # <% showcase.sample "Basic" do %>
40
+ # <%= render "components/button", content: "Button Content", mode: :small %>
41
+ # <% end %>
42
+ #
43
+ # This outputs a `<showcase-sample>` custom HTML element.
44
+ # The sample name is used to generate the `id` via `name.parameterize` by default, pass `id:` to override.
45
+ #
46
+ # If more advanced rendering is needed, the sample is available as a block argument:
47
+ #
48
+ # <% showcase.sample "Advanced" do |sample| %>
49
+ # <% sample.preview do %>
50
+ # <%= render "components/button", content: "Button Content", mode: :small %>
51
+ # <% end %>
52
+ #
53
+ # <% sample.extract do %>
54
+ # This will be in the source output.
55
+ # <% end %>
56
+ #
57
+ # The sample also supports several extra options:
58
+ #
59
+ # <% showcase.sample "Basic", id: "custom-id", description: "Please use this", events: "toggle:toggled", custom: "option" do %>
60
+ # <%# … %>
61
+ # <% end %>
62
+ #
63
+ # Here we set:
64
+ # - the `sample.id` with the HTML element `id` is overriden
65
+ # - the `sample.description`
66
+ # - the `sample.events` what JavaScript `events` to listen for on the element
67
+ # - any other custom options are available in `sample.details`.
68
+ def sample(name, **options, &block)
69
+ @samples << Showcase::Sample.new(@view_context, name, **options).tap { _1.collect(&block) }
70
+ end
71
+
72
+ # Yields an Options object to help define the configuration table for a Preview.
73
+ #
74
+ # <% showcase.options do |o| %>
75
+ # <% o.required :content, "Pass the inner content text that the button should display" %>
76
+ # <% o.optional :mode, "Pass an optional mode override", default: :small, options: %i[ small medium large ] %>
77
+ # <% o.optional :method, "What HTTP method to use", type: "String | Symbol", default: :post %>
78
+ # <% o.optional :reversed, "Whether the inner text should be reversed", default: false %> # type: "Boolean" is inferred from the default here.
79
+ # <% o.optional "**options", "Every other option is passed on as options to the inner `button_tag`", type: Hash %>
80
+ # <% end %>
81
+ #
82
+ # The `type:` is derived if a `default:` is passed, otherwise it's assumed to be a String.
83
+ #
84
+ # Showcase outputs the columns with this order [:name, :required, :type, :default, :description], any other passed column is
85
+ # automatically rendered after those.
86
+ def options
87
+ @options ||= Showcase::Options.new(@view_context).tap { yield _1 if block_given? }
88
+ end
89
+
90
+ def render_associated_partial
91
+ @view_context.render "#{Showcase.previews_path}/#{id}", showcase: self
92
+ nil
93
+ end
94
+ end