primer_view_components 0.0.118 → 0.0.120

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -0
  3. data/app/assets/javascripts/primer_view_components.js +1 -1
  4. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  5. data/app/assets/styles/primer_view_components.css +1 -1
  6. data/app/assets/styles/primer_view_components.css.map +1 -1
  7. data/app/components/primer/alpha/action_list/item.rb +1 -1
  8. data/app/components/primer/alpha/action_list.rb +1 -1
  9. data/app/components/primer/alpha/dialog.rb +2 -2
  10. data/app/components/primer/alpha/modal_dialog.js +2 -0
  11. data/app/components/primer/alpha/modal_dialog.ts +2 -0
  12. data/app/components/primer/alpha/text_field.css +1 -1
  13. data/app/components/primer/alpha/text_field.css.json +1 -1
  14. data/app/components/primer/alpha/text_field.css.map +1 -1
  15. data/app/components/primer/alpha/text_field.pcss +6 -0
  16. data/app/components/primer/alpha/toggle_switch.css +1 -1
  17. data/app/components/primer/alpha/toggle_switch.css.json +1 -1
  18. data/app/components/primer/alpha/toggle_switch.css.map +1 -1
  19. data/app/components/primer/alpha/toggle_switch.html.erb +4 -2
  20. data/app/components/primer/alpha/toggle_switch.js +23 -11
  21. data/app/components/primer/alpha/toggle_switch.pcss +6 -0
  22. data/app/components/primer/alpha/toggle_switch.ts +27 -11
  23. data/app/components/primer/alpha/tool_tip.js +0 -3
  24. data/app/components/primer/alpha/tool_tip.ts +0 -4
  25. data/app/components/primer/alpha/tooltip.rb +4 -3
  26. data/app/components/primer/beta/icon_button.rb +1 -1
  27. data/app/components/primer/component.rb +4 -0
  28. data/app/components/primer/icon_button.rb +1 -1
  29. data/app/components/primer/primer.d.ts +1 -0
  30. data/app/components/primer/primer.js +1 -0
  31. data/app/components/primer/primer.ts +1 -0
  32. data/app/forms/example_toggle_switch_form.rb +1 -1
  33. data/lib/primer/deprecations.yml +0 -13
  34. data/lib/primer/forms/dsl/input.rb +1 -1
  35. data/lib/primer/forms/toggle_switch.html.erb +7 -2
  36. data/lib/primer/forms/toggle_switch.rb +2 -4
  37. data/lib/primer/forms/toggle_switch_input.d.ts +5 -0
  38. data/lib/primer/forms/toggle_switch_input.js +29 -0
  39. data/lib/primer/forms/toggle_switch_input.ts +19 -0
  40. data/lib/primer/view_components/linters/button_component_migration_counter.rb +2 -2
  41. data/lib/primer/view_components/version.rb +1 -1
  42. data/lib/primer/view_components.rb +5 -0
  43. data/lib/primer/yard/backend.rb +38 -0
  44. data/lib/primer/yard/component_manifest.rb +123 -0
  45. data/lib/primer/yard/docs_helper.rb +81 -0
  46. data/lib/primer/yard/legacy_gatsby_backend.rb +271 -0
  47. data/lib/primer/yard/registry.rb +146 -0
  48. data/lib/primer/yard/renders_many_handler.rb +23 -0
  49. data/lib/primer/yard/renders_one_handler.rb +23 -0
  50. data/lib/rubocop/config/default.yml +3 -0
  51. data/lib/rubocop/cop/primer/test_selector.rb +48 -0
  52. data/lib/tasks/docs.rake +37 -405
  53. data/previews/primer/alpha/dialog_preview/body_has_scrollbar_overflow.html.erb +9 -0
  54. data/previews/primer/alpha/dialog_preview.rb +15 -0
  55. data/previews/primer/alpha/tooltip_preview.rb +8 -8
  56. data/previews/primer/beta/clipboard_copy_preview.rb +1 -1
  57. data/previews/primer/forms/forms_preview/example_toggle_switch_form.html.erb +3 -1
  58. data/static/arguments.json +2 -34
  59. data/static/audited_at.json +0 -3
  60. data/static/constants.json +0 -20
  61. data/static/statuses.json +0 -3
  62. metadata +20 -29
  63. data/app/components/primer/box_component.rb +0 -7
  64. data/app/components/primer/clipboard_copy.rb +0 -7
  65. data/app/components/primer/dropdown_menu_component.html.erb +0 -8
  66. data/app/components/primer/dropdown_menu_component.rb +0 -58
  67. data/lib/yard/docs_helper.rb +0 -79
  68. data/lib/yard/renders_many_handler.rb +0 -19
  69. data/lib/yard/renders_one_handler.rb +0 -19
@@ -107,10 +107,11 @@ module Primer
107
107
 
108
108
  @text = text
109
109
  @system_arguments = system_arguments
110
+ @system_arguments[:id] ||= self.class.generate_id
110
111
  @system_arguments[:tag] = :"tool-tip"
111
112
  @system_arguments[:for] = for_id
112
- system_arguments[:classes] = class_names(
113
- system_arguments[:classes],
113
+ @system_arguments[:classes] = class_names(
114
+ @system_arguments[:classes],
114
115
  "sr-only"
115
116
  )
116
117
  @system_arguments[:position] = :absolute
@@ -121,6 +122,6 @@ module Primer
121
122
  def call
122
123
  render(Primer::BaseComponent.new(**@system_arguments)) { @text }
123
124
  end
124
- end
125
125
  end
126
126
  end
127
+ end
@@ -63,7 +63,7 @@ module Primer
63
63
  @wrapper_arguments = wrapper_arguments
64
64
  @show_tooltip = show_tooltip
65
65
  @system_arguments = system_arguments
66
- @system_arguments[:id] ||= "icon-button-#{SecureRandom.hex(4)}"
66
+ @system_arguments[:id] ||= self.class.generate_id
67
67
 
68
68
  @system_arguments[:classes] = class_names(
69
69
  "Button",
@@ -22,6 +22,10 @@ module Primer
22
22
  status == :deprecated
23
23
  end
24
24
 
25
+ def self.generate_id
26
+ "#{name.demodulize.underscore.dasherize}-#{SecureRandom.uuid}"
27
+ end
28
+
25
29
  private
26
30
 
27
31
  def raise_on_invalid_options?
@@ -65,7 +65,7 @@ module Primer
65
65
 
66
66
  @system_arguments = system_arguments
67
67
 
68
- @system_arguments[:id] ||= "icon-button-#{SecureRandom.hex(4)}"
68
+ @system_arguments[:id] ||= self.class.generate_id
69
69
 
70
70
  @system_arguments[:classes] = class_names(
71
71
  "btn-octicon",
@@ -13,3 +13,4 @@ import './alpha/tab_container';
13
13
  import './time_ago_component';
14
14
  import '../../../lib/primer/forms/primer_multi_input';
15
15
  import '../../../lib/primer/forms/primer_text_field';
16
+ import '../../../lib/primer/forms/toggle_switch_input';
@@ -13,3 +13,4 @@ import './alpha/tab_container';
13
13
  import './time_ago_component';
14
14
  import '../../../lib/primer/forms/primer_multi_input';
15
15
  import '../../../lib/primer/forms/primer_text_field';
16
+ import '../../../lib/primer/forms/toggle_switch_input';
@@ -13,3 +13,4 @@ import './alpha/tab_container'
13
13
  import './time_ago_component'
14
14
  import '../../../lib/primer/forms/primer_multi_input'
15
15
  import '../../../lib/primer/forms/primer_text_field'
16
+ import '../../../lib/primer/forms/toggle_switch_input'
@@ -3,6 +3,6 @@
3
3
  # :nodoc:
4
4
  class ExampleToggleSwitchForm < Primer::Forms::ToggleSwitchForm
5
5
  def initialize(**system_arguments)
6
- super(name: :example_field, label: "Example", **system_arguments)
6
+ super(name: :example_field, label: "Example", caption: "This is an example toggle switch.", **system_arguments)
7
7
  end
8
8
  end
@@ -17,24 +17,11 @@ deprecations:
17
17
  autocorrect: true
18
18
  replacement: "Primer::Beta::Blankslate"
19
19
 
20
- - component: "Primer::BoxComponent"
21
- autocorrect: true
22
- replacement: "Primer::Box"
23
-
24
20
  - component: "Primer::ButtonComponent"
25
21
  autocorrect: false
26
22
  replacement: "Primer::Beta::Button"
27
23
  guide: "https://primer.style/view-components/guides/primer_button_component"
28
24
 
29
- - component: "Primer::ClipboardCopy"
30
- autocorrect: true
31
- replacement: "Primer::Beta::ClipboardCopy"
32
-
33
- - component: "Primer::DropdownMenuComponent"
34
- autocorrect: false
35
- replacement: "Primer::Beta::Dropdown"
36
- guide: "https://primer.style/view-components/guides/primer_dropdown_menu_component"
37
-
38
25
  - component: "Primer::Dropdown"
39
26
  autocorrect: true
40
27
  replacement: "Primer::Alpha::Dropdown"
@@ -76,7 +76,7 @@ module Primer
76
76
 
77
77
  @input_arguments[:invalid] = "true" if invalid?
78
78
 
79
- base_id = SecureRandom.hex[0..5]
79
+ base_id = SecureRandom.uuid
80
80
 
81
81
  @ids = {}.tap do |id_map|
82
82
  id_map[:validation] = "validation-#{base_id}" if invalid?
@@ -1,9 +1,14 @@
1
- <%= content_tag(:div, **@form_group_arguments) do %>
1
+ <%= content_tag("toggle-switch-input", **@input.input_arguments) do %>
2
2
  <span style="flex-grow: 1">
3
3
  <%= builder.label(@input.name, **@input.label_arguments) do %>
4
4
  <%= @input.label %>
5
5
  <% end %>
6
- <%= render(Caption.new(input: @input)) %>
6
+
7
+ <%= content_tag(:div, data: { target: "toggle-switch-input.validationElement" }, **@input.validation_arguments) do %>
8
+ <%= content_tag(:span, @input.validation_messages.first, data: { target: "toggle-switch-input.validationMessageElement" }, **@input.validation_message_arguments) %>
9
+ <% end %>
10
+
11
+ <div><%= render(Caption.new(input: @input)) %></div>
7
12
  </span>
8
13
  <%
9
14
  csrf = @input.csrf || @view_context.form_authenticity_token(
@@ -9,10 +9,8 @@ module Primer
9
9
  def initialize(input:)
10
10
  @input = input
11
11
  @input.add_label_classes("FormControl-label")
12
-
13
- @form_group_arguments = { class: "d-flex" }
14
-
15
- @form_group_arguments[:hidden] = "hidden" if @input.hidden?
12
+ @input.add_input_classes("FormControl-toggleSwitchInput")
13
+ @input.input_arguments[:hidden] = "hidden" if @input.hidden?
16
14
  end
17
15
  end
18
16
  end
@@ -0,0 +1,5 @@
1
+ export declare class ToggleSwitchInputElement extends HTMLElement {
2
+ validationElement: HTMLElement;
3
+ validationMessageElement: HTMLElement;
4
+ connectedCallback(): void;
5
+ }
@@ -0,0 +1,29 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { controller, target } from '@github/catalyst';
8
+ let ToggleSwitchInputElement = class ToggleSwitchInputElement extends HTMLElement {
9
+ connectedCallback() {
10
+ this.addEventListener('toggleSwitchError', (event) => {
11
+ this.validationMessageElement.innerText = event.detail;
12
+ this.validationElement.removeAttribute('hidden');
13
+ });
14
+ this.addEventListener('toggleSwitchSuccess', () => {
15
+ this.validationMessageElement.innerText = '';
16
+ this.validationElement.setAttribute('hidden', 'hidden');
17
+ });
18
+ }
19
+ };
20
+ __decorate([
21
+ target
22
+ ], ToggleSwitchInputElement.prototype, "validationElement", void 0);
23
+ __decorate([
24
+ target
25
+ ], ToggleSwitchInputElement.prototype, "validationMessageElement", void 0);
26
+ ToggleSwitchInputElement = __decorate([
27
+ controller
28
+ ], ToggleSwitchInputElement);
29
+ export { ToggleSwitchInputElement };
@@ -0,0 +1,19 @@
1
+ import {controller, target} from '@github/catalyst'
2
+
3
+ @controller
4
+ export class ToggleSwitchInputElement extends HTMLElement {
5
+ @target validationElement: HTMLElement
6
+ @target validationMessageElement: HTMLElement
7
+
8
+ connectedCallback() {
9
+ this.addEventListener('toggleSwitchError', (event: Event) => {
10
+ this.validationMessageElement.innerText = (event as CustomEvent).detail
11
+ this.validationElement.removeAttribute('hidden')
12
+ })
13
+
14
+ this.addEventListener('toggleSwitchSuccess', () => {
15
+ this.validationMessageElement.innerText = ''
16
+ this.validationElement.setAttribute('hidden', 'hidden')
17
+ })
18
+ }
19
+ }
@@ -18,9 +18,9 @@ module ERBLint
18
18
  # CloseButton component has preference when this class is seen in conjunction with `btn`.
19
19
  DISALLOWED_CLASSES = %w[close-button].freeze
20
20
  CLASSES = %w[btn btn-link].freeze
21
- MESSAGE = "We are migrating buttons to use [Primer::ButtonComponent](https://primer.style/view-components/components/button), please try to use that instead of raw HTML."
21
+ MESSAGE = "We are migrating buttons to use [Primer::Beta::Button](https://primer.style/view-components/components/beta/button), please try to use that instead of raw HTML."
22
22
  ARGUMENT_MAPPER = ArgumentMappers::Button
23
- COMPONENT = "Primer::ButtonComponent"
23
+ COMPONENT = "Primer::Beta::Button"
24
24
  end
25
25
  end
26
26
  end
@@ -6,7 +6,7 @@ module Primer
6
6
  module VERSION
7
7
  MAJOR = 0
8
8
  MINOR = 0
9
- PATCH = 118
9
+ PATCH = 120
10
10
 
11
11
  STRING = [MAJOR, MINOR, PATCH].join(".")
12
12
  end
@@ -54,5 +54,10 @@ module Primer
54
54
  def self.read(stats)
55
55
  File.read(File.join(DEFAULT_STATIC_PATH, FILE_NAMES[stats]))
56
56
  end
57
+
58
+ # primer/view_components root directory.
59
+ def self.root
60
+ Pathname(File.expand_path(File.join("..", ".."), __dir__))
61
+ end
57
62
  end
58
63
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nocov:
4
+ module Primer
5
+ module YARD
6
+ # Shared functionality for generating documentation from YARD comments.
7
+ class Backend
8
+ include DocsHelper
9
+
10
+ private
11
+
12
+ def pretty_default_value(tag, component)
13
+ params = tag.object.parameters.find { |param| [tag.name.to_s, "#{tag.name}:"].include?(param[0]) }
14
+ default = tag.defaults&.first || params&.second
15
+
16
+ return "N/A" unless default
17
+
18
+ constant_name = "#{component.name}::#{default}"
19
+ constant_value = default.safe_constantize || constant_name.safe_constantize
20
+
21
+ return pretty_value(default) if constant_value.nil?
22
+
23
+ pretty_value(constant_value)
24
+ end
25
+
26
+ def view_context
27
+ @view_context ||= begin
28
+ # Rails controller for rendering arbitrary ERB
29
+ vc = ApplicationController.new.tap { |c| c.request = ActionDispatch::TestRequest.create }.view_context
30
+ vc.singleton_class.include(DocsHelper)
31
+ vc.singleton_class.include(Primer::ViewHelper)
32
+ vc
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ # :nocov:
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nocov:
4
+ module Primer
5
+ module YARD
6
+ # The set of documented components (and associated metadata).
7
+ class ComponentManifest
8
+ COMPONENTS = {
9
+ Primer::Beta::RelativeTime => {},
10
+ Primer::Beta::IconButton => {},
11
+ Primer::Beta::Button => {},
12
+ Primer::Alpha::SegmentedControl => {},
13
+ Primer::Alpha::Layout => {},
14
+ Primer::Alpha::HellipButton => {},
15
+ Primer::Alpha::Image => {},
16
+ Primer::LocalTime => { js: true },
17
+ Primer::Alpha::OcticonSymbols => {},
18
+ Primer::Alpha::ImageCrop => { js: true },
19
+ Primer::IconButton => { js: true },
20
+ Primer::Beta::AutoComplete => { js: true },
21
+ Primer::Beta::AutoComplete::Item => {},
22
+ Primer::Beta::Avatar => {},
23
+ Primer::Beta::AvatarStack => {},
24
+ Primer::Beta::BaseButton => {},
25
+ Primer::Alpha::Banner => { js: true },
26
+ Primer::Beta::Blankslate => {},
27
+ Primer::Beta::BorderBox => {},
28
+ Primer::Beta::BorderBox::Header => {},
29
+ Primer::Box => {},
30
+ Primer::Beta::Breadcrumbs => {},
31
+ Primer::ButtonComponent => { js: true },
32
+ Primer::Beta::ButtonGroup => {},
33
+ Primer::Alpha::ButtonMarketing => {},
34
+ Primer::Beta::ClipboardCopy => { js: true },
35
+ Primer::Beta::CloseButton => {},
36
+ Primer::Beta::Counter => {},
37
+ Primer::Beta::Details => {},
38
+ Primer::Alpha::Dialog => {},
39
+ Primer::Alpha::Dropdown => { js: true },
40
+ Primer::Beta::Flash => {},
41
+ Primer::Beta::Heading => {},
42
+ Primer::Alpha::HiddenTextExpander => {},
43
+ Primer::Beta::Label => {},
44
+ Primer::LayoutComponent => {},
45
+ Primer::Beta::Link => { js: true },
46
+ Primer::Beta::Markdown => {},
47
+ Primer::Alpha::Menu => {},
48
+ Primer::Navigation::TabComponent => {},
49
+ Primer::Beta::Octicon => {},
50
+ Primer::Beta::Popover => {},
51
+ Primer::Beta::ProgressBar => {},
52
+ Primer::Beta::State => {},
53
+ Primer::Beta::Spinner => {},
54
+ Primer::Beta::Subhead => {},
55
+ Primer::Alpha::TabContainer => { js: true },
56
+ Primer::Beta::Text => {},
57
+ Primer::TimeAgoComponent => { js: true },
58
+ Primer::Beta::TimelineItem => {},
59
+ Primer::Tooltip => {},
60
+ Primer::Truncate => {},
61
+ Primer::Beta::Truncate => {},
62
+ Primer::Alpha::UnderlineNav => {},
63
+ Primer::Alpha::UnderlinePanels => { js: true },
64
+ Primer::Alpha::TabNav => {},
65
+ Primer::Alpha::TabPanels => { js: true },
66
+ Primer::Alpha::Tooltip => { js: true },
67
+ Primer::Alpha::ToggleSwitch => { js: true },
68
+
69
+ # Examples can be seen in the NavList docs
70
+ Primer::Alpha::NavList => { js: true },
71
+ Primer::Alpha::NavList::Item => { js: true, examples: false },
72
+ Primer::Alpha::NavList::Section => { js: true, examples: false },
73
+
74
+ # ActionList is a base component that should not be used by itself, and thus
75
+ # does not have examples of its own
76
+ Primer::Alpha::ActionList => { js: true, examples: false },
77
+ Primer::Alpha::ActionList::Divider => { examples: false },
78
+ Primer::Alpha::ActionList::Heading => { examples: false },
79
+ Primer::Alpha::ActionList::Item => { examples: false },
80
+
81
+ # Forms
82
+ Primer::Alpha::TextField => { form_component: true }
83
+ }.freeze
84
+
85
+ class << self
86
+ def each(&block)
87
+ COMPONENTS.keys.each(&block)
88
+ end
89
+
90
+ def components_with_docs
91
+ @components_with_docs ||= COMPONENTS.keys
92
+ end
93
+
94
+ def all_components
95
+ @all_components ||= Primer::Component.descendants - [Primer::BaseComponent]
96
+ end
97
+
98
+ def components_without_docs
99
+ @components_without_docs ||= all_components - components_with_docs
100
+ end
101
+
102
+ def components_with_examples
103
+ @components_with_examples ||= COMPONENTS.keys.select do |c|
104
+ COMPONENTS[c].fetch(:examples, true)
105
+ end
106
+ end
107
+
108
+ def components_requiring_js
109
+ @components_requiring_js ||= COMPONENTS.keys.select do |c|
110
+ COMPONENTS[c].fetch(:js, false)
111
+ end
112
+ end
113
+
114
+ def form_components
115
+ @form_components ||= COMPONENTS.keys.select do |c|
116
+ COMPONENTS[c].fetch(:form_component, false)
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ # :nocov:
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nocov:
4
+
5
+ module Primer
6
+ module YARD
7
+ # Helper methods to use for yard documentation
8
+ module DocsHelper
9
+ def one_of(enumerable, lower: false, sort: true)
10
+ # Sort the array if requested
11
+ if sort
12
+ enumerable = enumerable.sort do |a, b|
13
+ a.instance_of?(b.class) ? a <=> b : a.class.to_s <=> b.class.to_s
14
+ end
15
+ end
16
+
17
+ values =
18
+ case enumerable
19
+ when Hash
20
+ enumerable.map do |key, value|
21
+ "#{pretty_value(key)} (#{pretty_value(value)})"
22
+ end
23
+ else
24
+ enumerable.map do |key|
25
+ pretty_value(key)
26
+ end
27
+ end
28
+
29
+ prefix = "One of"
30
+ prefix = prefix.downcase if lower
31
+
32
+ "#{prefix} #{values.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')}."
33
+ end
34
+
35
+ def link_to_accessibility
36
+ "[Accessibility](#accessibility)"
37
+ end
38
+
39
+ def link_to_system_arguments_docs
40
+ "[System arguments](/system-arguments)"
41
+ end
42
+
43
+ def link_to_typography_docs
44
+ "[Typography](/system-arguments#typography)"
45
+ end
46
+
47
+ def link_to_component(component)
48
+ status_module, short_name, class_name = status_module_and_short_name(component)
49
+ status_path = status_module.nil? ? "" : "#{status_module}/"
50
+
51
+ "[#{class_name}](/components/#{status_path}#{short_name.downcase})"
52
+ end
53
+
54
+ def link_to_octicons
55
+ "[Octicon](https://primer.style/octicons/)"
56
+ end
57
+
58
+ def link_to_heading_practices
59
+ "[Learn more about best heading practices (WAI Headings)](https://www.w3.org/WAI/tutorials/page-structure/headings/)"
60
+ end
61
+
62
+ def status_module_and_short_name(component)
63
+ name_with_status = component.name.gsub(/Primer::|Component/, "")
64
+
65
+ m = name_with_status.match(/(?<status>Beta|Alpha|Deprecated)?(?<_colons>::)?(?<name>.*)/)
66
+ [m[:status]&.downcase, m[:name].gsub("::", ""), m[:name]]
67
+ end
68
+
69
+ def pretty_value(val)
70
+ case val
71
+ when nil
72
+ "`nil`"
73
+ when Symbol
74
+ "`:#{val}`"
75
+ else
76
+ "`#{val}`"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end