polaris_view_components 0.9.1 → 0.11.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 +20 -65
  3. data/app/assets/javascripts/polaris_view_components/dropzone_controller.js +24 -5
  4. data/app/assets/javascripts/polaris_view_components/text_field_controller.js +5 -0
  5. data/app/assets/javascripts/polaris_view_components.js +28 -7
  6. data/app/assets/stylesheets/polaris_view_components/custom.css +60 -8
  7. data/app/assets/stylesheets/polaris_view_components.css +48 -9
  8. data/app/components/polaris/action_list/item_component.rb +2 -1
  9. data/app/components/polaris/button_component.html.erb +2 -2
  10. data/app/components/polaris/data_table/column_component.rb +2 -1
  11. data/app/components/polaris/data_table_component.html.erb +1 -0
  12. data/app/components/polaris/dropzone_component.html.erb +13 -8
  13. data/app/components/polaris/empty_search_results_component.html.erb +12 -0
  14. data/app/components/polaris/empty_search_results_component.rb +19 -0
  15. data/app/components/polaris/filters_component.rb +3 -1
  16. data/app/components/polaris/headless_button.html.erb +2 -2
  17. data/app/components/polaris/headless_button.rb +3 -1
  18. data/app/components/polaris/index_table/column_component.rb +2 -1
  19. data/app/components/polaris/index_table_component.html.erb +3 -5
  20. data/app/components/polaris/index_table_component.rb +10 -0
  21. data/app/components/polaris/page_component.html.erb +81 -10
  22. data/app/components/polaris/page_component.rb +91 -29
  23. data/app/components/polaris/resource_item_component.rb +4 -1
  24. data/app/components/polaris/resource_list_component.html.erb +11 -0
  25. data/app/components/polaris/resource_list_component.rb +21 -0
  26. data/app/components/polaris/text_field_component.html.erb +1 -1
  27. data/app/components/polaris/text_field_component.rb +1 -1
  28. data/app/components/polaris/visually_hidden_component.rb +0 -3
  29. data/app/helpers/polaris/url_helper.rb +37 -0
  30. data/app/helpers/polaris/view_helper.rb +1 -0
  31. data/lib/install/install.rb +57 -0
  32. data/lib/polaris/view_components/version.rb +1 -1
  33. data/lib/tasks/polaris_view_components_tasks.rake +6 -0
  34. metadata +91 -7
  35. data/lib/generators/polaris_view_components/USAGE +0 -5
  36. data/lib/generators/polaris_view_components/install_generator.rb +0 -35
  37. data/lib/generators/polaris_view_components/templates/README +0 -14
  38. data/lib/generators/polaris_view_components/templates/stimulus_index.js +0 -6
@@ -12,15 +12,13 @@
12
12
  </thead>
13
13
  <tbody>
14
14
  <% @data.each do |row| %>
15
- <tr class="Polaris-IndexTable__TableRow Polaris-IndexTable__TableRow--unclickable">
15
+ <%= render Polaris::BaseComponent.new(**row_arguments(row)) do %>
16
16
  <% columns.each_with_index do |column, index| %>
17
- <%= render_cell(
18
- flush: column.flush,
19
- ) do %>
17
+ <%= render_cell(flush: column.flush, **column.system_arguments) do %>
20
18
  <%= column.call(row) %>
21
19
  <% end %>
22
20
  <% end %>
23
- </tr>
21
+ <% end %>
24
22
  <% end %>
25
23
  </tbody>
26
24
  </table>
@@ -18,6 +18,16 @@ module Polaris
18
18
  end
19
19
  end
20
20
 
21
+ def row_arguments(row)
22
+ {tag: "tr"}.tap do |args|
23
+ args[:classes] = class_names(
24
+ "Polaris-IndexTable__TableRow",
25
+ "Polaris-IndexTable__TableRow--unclickable"
26
+ )
27
+ args[:id] = dom_id(row) if row.respond_to?(:to_key)
28
+ end
29
+ end
30
+
21
31
  def render_cell(**arguments, &block)
22
32
  render(IndexTable::CellComponent.new(**arguments), &block)
23
33
  end
@@ -1,6 +1,6 @@
1
- <%= render Polaris::BaseComponent.new(**@system_arguments) do %>
1
+ <%= render Polaris::BaseComponent.new(**system_arguments) do %>
2
2
  <% if render_header? %>
3
- <%= render Polaris::BaseComponent.new(**@header_arguments) do %>
3
+ <%= render Polaris::BaseComponent.new(**header_arguments) do %>
4
4
  <div class="Polaris-Page-Header__Row">
5
5
  <% if @back_url.present? %>
6
6
  <div class="Polaris-Page-Header__BreadcrumbWrapper">
@@ -17,7 +17,7 @@
17
17
  <% end %>
18
18
 
19
19
  <div class="Polaris-Page-Header__TitleWrapper">
20
- <div <% if thumbnail.present? %>class="Polaris-Header-Title--hasThumbnail"<% end %>>
20
+ <%= tag.div(class: ["Polaris-Header-Title--hasThumbnail": thumbnail.present?]) do %>
21
21
  <% if thumbnail.present? %>
22
22
  <div>
23
23
  <%= thumbnail %>
@@ -38,18 +38,89 @@
38
38
  <% end %>
39
39
 
40
40
  <% if @subtitle.present? %>
41
- <div class="Polaris-Header-Title__SubTitle">
41
+ <%= render Polaris::BaseComponent.new(**subtitle_arguments) do %>
42
42
  <p><%= @subtitle %></p>
43
- </div>
43
+ <% end %>
44
44
  <% end %>
45
45
  </div>
46
- </div>
46
+ <% end %>
47
47
  </div>
48
48
 
49
- <% if primary_action.present? %>
49
+ <% if @secondary_actions.any? || action_group.present? || render_primary_action? || has_pagination? %>
50
50
  <div class="Polaris-Page-Header__RightAlign">
51
- <div class="Polaris-Page-Header__PrimaryActionWrapper">
52
- <%= primary_action %>
51
+ <div class="Polaris-Page-Header__Actions">
52
+ <% if @secondary_actions.any? || action_group.present? %>
53
+ <div class="Polaris-ActionMenu Polaris-ActionMenu--mobile">
54
+ <div class="Polaris-ActionMenu-RollupActions__RollupActivator">
55
+ <%= polaris_popover(position: :below, alignment: :right) do |popover| %>
56
+ <% popover.button do |button| %>
57
+ <% button.icon(name: "HorizontalDotsMinor") %>
58
+ <% end %>
59
+ <%= polaris_action_list(style: "text-align: left;") do |action_list| %>
60
+ <% if @secondary_actions.any? %>
61
+ <% action_list.section do |section| %>
62
+ <% @secondary_actions.each do |action| %>
63
+ <% section.item(**action) { action[:content] } %>
64
+ <% end %>
65
+ <% end %>
66
+ <% end %>
67
+
68
+ <% if action_group.present? %>
69
+ <% action_list.section(title: action_group.title) do |section| %>
70
+ <% action_group.actions.each do |action| %>
71
+ <% section.item(**action) { action[:content] } %>
72
+ <% end %>
73
+ <% end %>
74
+ <% end %>
75
+ <% end %>
76
+ <% end %>
77
+ </div>
78
+ </div>
79
+ <div class="Polaris-ActionMenu Polaris-ActionMenu--desktop">
80
+ <div class="Polaris-ActionMenu-Actions__ActionsLayout">
81
+ <%= polaris_button_group(spacing: :extra_tight) do |group| %>
82
+ <% @secondary_actions.each do |action| %>
83
+ <% group.item do %>
84
+ <% destructive = action.delete(:destructive) %>
85
+ <% tag.span(class: ["Polaris-ActionMenu-SecondaryAction", "Polaris-ActionMenu-SecondaryAction--destructive": destructive]) do %>
86
+ <%= polaris_button(**action) { action[:content] } %>
87
+ <% end %>
88
+ <% end %>
89
+ <% end %>
90
+ <% if action_group.present? %>
91
+ <% group.item do %>
92
+ <span class="Polaris-ActionMenu-SecondaryAction">
93
+ <%= action_group %>
94
+ </span>
95
+ <% end %>
96
+ <% end %>
97
+ <% end %>
98
+ </div>
99
+ </div>
100
+ <% end %>
101
+ <% if render_primary_action? %>
102
+ <div class="Polaris-Page-Header__PrimaryActionWrapper">
103
+ <%= primary_action || custom_primary_action %>
104
+ </div>
105
+ <% end %>
106
+ <% if has_pagination? %>
107
+ <div class="Polaris-Page-Header__PaginationWrapper">
108
+ <nav aria-label="Pagination">
109
+ <%= polaris_button_group(segmented: true) do |group| %>
110
+ <% group.item do %>
111
+ <%= polaris_button(url: @prev_url, outline: true, disabled: @prev_url.blank?) do |button| %>
112
+ <% button.icon(name: "ChevronLeftMinor") %>
113
+ <% end %>
114
+ <% end %>
115
+ <% group.item do %>
116
+ <%= polaris_button(url: @next_url, outline: true, disabled: @next_url.blank?) do |button| %>
117
+ <% button.icon(name: "ChevronRightMinor") %>
118
+ <% end %>
119
+ <% end %>
120
+ <% end %>
121
+ </nav>
122
+ </div>
123
+ <% end %>
53
124
  </div>
54
125
  </div>
55
126
  <% end %>
@@ -57,7 +128,7 @@
57
128
  <% end %>
58
129
  <% end %>
59
130
 
60
- <%= render Polaris::BaseComponent.new(**@content_arguments) do %>
131
+ <%= render Polaris::BaseComponent.new(**content_arguments) do %>
61
132
  <%= content %>
62
133
  <% end %>
63
134
  <% end %>
@@ -2,55 +2,117 @@
2
2
 
3
3
  module Polaris
4
4
  class PageComponent < Polaris::Component
5
+ renders_one :title_metadata
6
+ renders_one :thumbnail, Polaris::ThumbnailComponent
5
7
  renders_one :primary_action, ->(primary: true, **system_arguments) do
6
8
  Polaris::ButtonComponent.new(primary: primary, **system_arguments)
7
9
  end
8
- # renders_many :secondary_actions, Polaris::ButtonComponent
9
- renders_one :title_metadata
10
- renders_one :thumbnail, Polaris::ThumbnailComponent
10
+ renders_one :custom_primary_action
11
+ renders_one :action_group, "ActionGroupComponent"
11
12
 
12
13
  def initialize(
13
14
  title: nil,
14
15
  subtitle: nil,
16
+ compact_title: false,
15
17
  back_url: nil,
18
+ prev_url: nil,
19
+ next_url: nil,
16
20
  narrow_width: false,
17
21
  full_width: false,
18
22
  divider: false,
23
+ secondary_actions: [],
19
24
  **system_arguments
20
25
  )
21
26
  @title = title
22
27
  @subtitle = subtitle
28
+ @compact_title = compact_title
23
29
  @back_url = back_url
24
-
30
+ @prev_url = prev_url
31
+ @next_url = next_url
32
+ @narrow_width = narrow_width
33
+ @full_width = full_width
34
+ @divider = divider
35
+ @secondary_actions = secondary_actions
25
36
  @system_arguments = system_arguments
26
- @system_arguments[:tag] = "div"
27
- @system_arguments[:classes] = class_names(
28
- @system_arguments[:classes],
29
- "Polaris-Page",
30
- "Polaris-Page--narrowWidth": narrow_width,
31
- "Polaris-Page--fullWidth": full_width
32
- )
33
-
34
- @header_arguments = {}
35
- @header_arguments[:tag] = "div"
36
- @header_arguments[:classes] = class_names(
37
- "Polaris-Page-Header",
38
- "Polaris-Page-Header--mobileView",
39
- "Polaris-Page-Header--mediumTitle",
40
- "Polaris-Page-Header--hasNavigation": back_url.present?,
41
- "Polaris-Page-Header--noBreadcrumbs": back_url.blank?
42
- )
43
-
44
- @content_arguments = {}
45
- @content_arguments[:tag] = "div"
46
- @content_arguments[:classes] = class_names(
47
- "Polaris-Page__Content",
48
- "Polaris-Page--divider": divider
49
- )
37
+ end
38
+
39
+ def header_arguments
40
+ {
41
+ tag: "div",
42
+ classes: class_names(
43
+ "Polaris-Page-Header",
44
+ "Polaris-Page-Header--mobileView",
45
+ "Polaris-Page-Header--mediumTitle",
46
+ "Polaris-Page-Header--hasNavigation": @back_url.present?,
47
+ "Polaris-Page-Header--noBreadcrumbs": @back_url.blank?
48
+ )
49
+ }
50
+ end
51
+
52
+ def subtitle_arguments
53
+ {
54
+ tag: "div",
55
+ classes: class_names(
56
+ "Polaris-Header-Title__SubTitle",
57
+ "Polaris-Header-Title__SubtitleCompact": @compact_title
58
+ )
59
+ }
60
+ end
61
+
62
+ def content_arguments
63
+ {
64
+ tag: "div",
65
+ classes: class_names(
66
+ "Polaris-Page__Content",
67
+ "Polaris-Page--divider": @divider
68
+ )
69
+ }
70
+ end
71
+
72
+ def system_arguments
73
+ @system_arguments.tap do |opts|
74
+ opts[:tag] = "div"
75
+ opts[:classes] = class_names(
76
+ opts[:classes],
77
+ "Polaris-Page",
78
+ "Polaris-Page--narrowWidth": @narrow_width,
79
+ "Polaris-Page--fullWidth": @full_width
80
+ )
81
+ end
50
82
  end
51
83
 
52
84
  def render_header?
53
- @title.present? || @subtitle.present? || @back_url.present? || primary_action.present?
85
+ @title.present? || @subtitle.present? || @back_url.present? || render_primary_action?
86
+ end
87
+
88
+ def render_primary_action?
89
+ primary_action.present? || custom_primary_action.present?
90
+ end
91
+
92
+ def has_pagination?
93
+ @next_url.present? || @prev_url.present?
94
+ end
95
+
96
+ class ActionGroupComponent < Polaris::Component
97
+ attr_reader :title
98
+ attr_reader :actions
99
+
100
+ def initialize(title:, actions: [])
101
+ @title = title
102
+ @actions = actions
103
+ end
104
+
105
+ def call
106
+ render(Polaris::PopoverComponent.new(position: :below)) do |popover|
107
+ popover.button(disclosure: true) { @title }
108
+
109
+ polaris_action_list do |list|
110
+ @actions.each do |action|
111
+ list.item(**action) { action[:content] }
112
+ end
113
+ end
114
+ end
115
+ end
54
116
  end
55
117
  end
56
118
  end
@@ -31,6 +31,7 @@ module Polaris
31
31
  vertical_alignment: ALIGNMENT_DEFAULT,
32
32
  cursor: CURSOR_DEFAULT,
33
33
  selectable: false,
34
+ selected: false,
34
35
  offset: false,
35
36
  wrapper_arguments: {},
36
37
  container_arguments: {},
@@ -40,6 +41,7 @@ module Polaris
40
41
  @vertical_alignment = vertical_alignment
41
42
  @cursor = fetch_or_fallback(CURSOR_OPTIONS, cursor, CURSOR_DEFAULT)
42
43
  @selectable = selectable
44
+ @selected = selected
43
45
  @offset = offset
44
46
  @wrapper_arguments = wrapper_arguments
45
47
  @container_arguments = container_arguments
@@ -79,7 +81,8 @@ module Polaris
79
81
  args[:classes] = class_names(
80
82
  args[:classes],
81
83
  "Polaris-ResourceItem",
82
- "Polaris-ResourceItem--selectable": @selectable
84
+ "Polaris-ResourceItem--selectable": @selectable,
85
+ "Polaris-ResourceItem--selected": @selected
83
86
  )
84
87
  prepend_option(args, :style, "cursor: #{@cursor};")
85
88
  prepend_option(args[:data], :action, "click->polaris-resource-item#open")
@@ -4,6 +4,17 @@
4
4
  <%= filters %>
5
5
  </div>
6
6
  <% end %>
7
+
8
+ <% if header_title.present? %>
9
+ <div class="Polaris-ResourceList__HeaderOuterWrapper">
10
+ <div class="Polaris-ResourceList__HeaderWrapper">
11
+ <div class="Polaris-ResourceList__HeaderContentWrapper">
12
+ <div class="Polaris-ResourceList__HeaderTitleWrapper"><%= header_title %></div>
13
+ </div>
14
+ </div>
15
+ </div>
16
+ <% end %>
17
+
7
18
  <%= render(Polaris::BaseComponent.new(**@system_arguments)) do %>
8
19
  <%= content %>
9
20
  <% end %>
@@ -5,9 +5,16 @@ module Polaris
5
5
  renders_one :filters, Polaris::FiltersComponent
6
6
 
7
7
  def initialize(
8
+ items: [],
9
+ resource_name: nil,
10
+ total_items_count: nil,
8
11
  wrapper_arguments: {},
9
12
  **system_arguments
10
13
  )
14
+ @items = items
15
+ @resource_name = resource_name
16
+ @total_items_count = total_items_count
17
+
11
18
  @wrapper_arguments = wrapper_arguments
12
19
  @wrapper_arguments[:tag] = "div"
13
20
  @wrapper_arguments[:classes] = class_names(
@@ -22,5 +29,19 @@ module Polaris
22
29
  "Polaris-ResourceList"
23
30
  )
24
31
  end
32
+
33
+ def header_title
34
+ count unless @total_items_count.nil?
35
+ end
36
+
37
+ def resource_string
38
+ return @resource_name[:singular] if @items.size === 1 && !@total_items_count
39
+
40
+ @resource_name[:plural]
41
+ end
42
+
43
+ def count
44
+ "Showing #{@items.size} of #{@total_items_count} #{resource_string}"
45
+ end
25
46
  end
26
47
  end
@@ -45,7 +45,7 @@
45
45
  type="button"
46
46
  class="Polaris-TextField__ClearButton
47
47
  <% if @value.blank? %>
48
- Polaris-TextField__ClearButton--hidden
48
+ Polaris-TextField__Hidden
49
49
  <% end %>"
50
50
  tabindex="0"
51
51
  data-polaris-text-field-target="clearButton"
@@ -105,7 +105,7 @@ module Polaris
105
105
  tag: "div",
106
106
  data: {
107
107
  polaris_text_field_has_value_class: "Polaris-TextField--hasValue",
108
- polaris_text_field_clear_button_hidden_class: "Polaris-TextField__ClearButton--hidden"
108
+ polaris_text_field_clear_button_hidden_class: "Polaris-TextField__Hidden"
109
109
  }
110
110
  }.deep_merge(@system_arguments).tap do |opts|
111
111
  opts[:classes] = class_names(
@@ -2,9 +2,6 @@
2
2
 
3
3
  module Polaris
4
4
  class VisuallyHiddenComponent < Polaris::Component
5
- def initialize
6
- end
7
-
8
5
  def call
9
6
  content_tag(:span, content, class: "Polaris-VisuallyHidden")
10
7
  end
@@ -15,5 +15,42 @@ module Polaris
15
15
  content
16
16
  end
17
17
  end
18
+
19
+ def polaris_link_to(name = nil, options = nil, html_options = nil, &block)
20
+ html_options, options, name = options, name, block if block
21
+ options ||= {}
22
+
23
+ html_options = convert_options_to_data_attributes(options, html_options)
24
+ html_options[:classes] = html_options[:class]
25
+ html_options.delete(:class)
26
+
27
+ url = url_target(name, options)
28
+
29
+ link = Polaris::LinkComponent.new(url: url, **html_options)
30
+ link = link.with_content(name) unless block
31
+
32
+ render(link, &block)
33
+ end
34
+
35
+ def polaris_mail_to(email_address, name = nil, html_options = {}, &block)
36
+ html_options, name = name, nil if name.is_a?(Hash)
37
+ html_options = (html_options || {}).stringify_keys
38
+ html_options[:classes] = html_options[:class]
39
+ html_options.delete(:class)
40
+
41
+ extras = %w[cc bcc body subject reply_to].map! { |item|
42
+ option = html_options.delete(item).presence || next
43
+ "#{item.dasherize}=#{ERB::Util.url_encode(option)}"
44
+ }.compact
45
+ extras = extras.empty? ? "" : "?" + extras.join("&")
46
+
47
+ encoded_email_address = ERB::Util.url_encode(email_address).gsub("%40", "@")
48
+ url = "mailto:#{encoded_email_address}#{extras}"
49
+
50
+ link = Polaris::LinkComponent.new(url: url, **html_options)
51
+ link = link.with_content(name || email_address) unless block
52
+
53
+ render(link, &block)
54
+ end
18
55
  end
19
56
  end
@@ -24,6 +24,7 @@ module Polaris
24
24
  description_list: "Polaris::DescriptionListComponent",
25
25
  display_text: "Polaris::DisplayTextComponent",
26
26
  dropzone: "Polaris::DropzoneComponent",
27
+ empty_search_results: "Polaris::EmptySearchResultsComponent",
27
28
  empty_state: "Polaris::EmptyStateComponent",
28
29
  exception_list: "Polaris::ExceptionListComponent",
29
30
  footer_help: "Polaris::FooterHelpComponent",
@@ -0,0 +1,57 @@
1
+ APPLICATION_LAYOUT_PATH = Rails.root.join("app/views/layouts/application.html.erb")
2
+ IMPORTMAP_BINSTUB = Rails.root.join("bin/importmap")
3
+ IMPORTMAP_CONFIG_PATH = Rails.root.join("config/importmap.rb")
4
+ STIMULUS_PATH = Rails.root.join("app/javascript/controllers/index.js")
5
+
6
+ if APPLICATION_LAYOUT_PATH.exist?
7
+ say "Add Polaris styles in application layout"
8
+ insert_into_file APPLICATION_LAYOUT_PATH.to_s, "\n <%= stylesheet_link_tag \"polaris_view_components\" %>", before: /\s*<\/head>/
9
+
10
+ if File.read(APPLICATION_LAYOUT_PATH).include?("<body>")
11
+ say "Add Polaris inline styles for <body> in application layout"
12
+ gsub_file APPLICATION_LAYOUT_PATH.to_s, "<body>", "<body style=\"<%= polaris_body_styles %>\">"
13
+ else
14
+ say "<body> tag is not found in application layout.", :red
15
+ say " Replace <body> with <body style=\"<%= polaris_body_styles %>\"> in your custom layour."
16
+ end
17
+ else
18
+ say "Default application.html.erb is missing!", :red
19
+ say " 1. Add <%= stylesheet_link_tag \"polaris_view_components\" %> within the <head> tag in your custom layout."
20
+ say " 2. Replace <body> with <body style=\"<%= polaris_body_styles %>\"> in your custom layour."
21
+ end
22
+
23
+ if IMPORTMAP_BINSTUB.exist?
24
+ importmaps = File.read(IMPORTMAP_CONFIG_PATH)
25
+
26
+ unless importmaps.include?("@rails/request.js")
27
+ say "Pin @rails/request.js dependency"
28
+ run "bin/importmap pin @rails/request.js --download"
29
+ end
30
+
31
+ say "Pin polaris_view_components"
32
+ append_to_file IMPORTMAP_CONFIG_PATH do
33
+ 'pin "polaris-view-components", to: "polaris_view_components.js"\n'
34
+ end
35
+ else
36
+ package_json = File.read(Rails.root.join("package.json"))
37
+
38
+ unless package_json.include?("@rails/request.js")
39
+ say "Add @rails/request.js dependency"
40
+ run "yarn add @rails/request.js"
41
+ end
42
+
43
+ say "Add polaris-view-components package"
44
+ run "yarn add polaris-view-components"
45
+ end
46
+
47
+ if STIMULUS_PATH.exist?
48
+ say "Add Polaris Stimulus controllers"
49
+ append_to_file STIMULUS_PATH do
50
+ "\nimport { registerPolarisControllers } from \"polaris-view-components\"\nregisterPolarisControllers(Stimulus)\n"
51
+ end
52
+ else
53
+ say "Default Stimulus location is missing: app/javascript/controllers/index.js", :red
54
+ say " Add to your Stimulus index.js:"
55
+ say " import { registerPolarisControllers } from \"polaris-view-components\""
56
+ say " registerPolarisControllers(Stimulus)"
57
+ end
@@ -1,5 +1,5 @@
1
1
  module Polaris
2
2
  module ViewComponents
3
- VERSION = "0.9.1"
3
+ VERSION = "0.11.0"
4
4
  end
5
5
  end
@@ -0,0 +1,6 @@
1
+ namespace :polaris_view_components do
2
+ desc "Setup Polaris::ViewComponents for the app"
3
+ task :install do
4
+ system "#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{File.expand_path("../install/install.rb", __dir__)}"
5
+ end
6
+ end