primer_view_components 0.0.94 → 0.0.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -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/components/primer/alpha/dialog.rb +3 -3
  6. data/app/components/primer/alpha/toggle_switch.js +3 -0
  7. data/app/components/primer/alpha/toggle_switch.ts +3 -0
  8. data/app/components/primer/alpha/tool_tip.js +17 -2
  9. data/app/components/primer/alpha/tool_tip.ts +14 -2
  10. data/app/components/primer/beta/icon_button.html.erb +1 -1
  11. data/app/components/primer/beta/icon_button.rb +5 -3
  12. data/app/components/primer/button_component.rb +1 -1
  13. data/app/components/primer/experimental/action_bar.d.ts +14 -0
  14. data/app/components/primer/experimental/action_bar.js +141 -0
  15. data/app/components/primer/icon_button.rb +1 -1
  16. data/app/lib/primer/fetch_or_fallback_helper.rb +1 -1
  17. data/lib/primer/deprecations.rb +2 -2
  18. data/lib/primer/forms/builder.rb +2 -2
  19. data/lib/primer/forms/check_box.html.erb +5 -1
  20. data/lib/primer/forms/check_box.rb +11 -0
  21. data/lib/primer/forms/dsl/check_box_group_input.rb +17 -4
  22. data/lib/primer/forms/dsl/check_box_input.rb +21 -2
  23. data/lib/primer/view_components/version.rb +1 -1
  24. data/lib/tasks/coverage.rake +1 -1
  25. data/lib/tasks/docs.rake +19 -15
  26. data/lib/tasks/static.rake +1 -1
  27. data/lib/tasks/test.rake +54 -0
  28. data/lib/tasks/utilities.rake +1 -0
  29. data/static/arguments.json +2042 -0
  30. data/static/audited_at.json +0 -2
  31. data/static/constants.json +0 -4
  32. data/static/statuses.json +2 -4
  33. metadata +6 -6
  34. data/app/components/primer/base_button.rb +0 -7
  35. data/app/components/primer/button_group.rb +0 -7
  36. data/lib/tasks/deprecated.rake +0 -27
  37. data/static/arguments.yml +0 -1358
@@ -105,10 +105,10 @@ module Primer
105
105
  # <% end %>
106
106
  # @param id [String] The id of the dialog.
107
107
  # @param title [String] Describes the content of the dialog.
108
- # @param subtitle [String] Provides dditional context for the dialog, also setting the `aria-describedby` attribute.
108
+ # @param subtitle [String] Provides additional context for the dialog, also setting the `aria-describedby` attribute.
109
109
  # @param size [Symbol] The size of the dialog. <%= one_of(Primer::Alpha::Dialog::SIZE_OPTIONS) %>
110
- # @param position [Symbol] The size of the dialog. <%= one_of(Primer::Alpha::Dialog::POSITION_OPTIONS) %>
111
- # @param position_narrow [Symbol] The size of the dialog. <%= one_of(Primer::Alpha::Dialog::POSITION_NARROW_OPTIONS) %>
110
+ # @param position [Symbol] The position of the dialog. <%= one_of(Primer::Alpha::Dialog::POSITION_OPTIONS) %>
111
+ # @param position_narrow [Symbol] The position of the dialog when narrow. <%= one_of(Primer::Alpha::Dialog::POSITION_NARROW_OPTIONS) %>
112
112
  # @param visually_hide_title [Boolean] If true will hide the heading title, while still making it available to Screen Readers.
113
113
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
114
114
  def initialize(
@@ -102,6 +102,9 @@ let ToggleSwitchElement = class ToggleSwitchElement extends HTMLElement {
102
102
  const response = await fetch(this.src, {
103
103
  credentials: 'same-origin',
104
104
  method: 'POST',
105
+ headers: {
106
+ 'Requested-With': 'XMLHttpRequest'
107
+ },
105
108
  body
106
109
  });
107
110
  if (response.ok) {
@@ -124,6 +124,9 @@ export class ToggleSwitchElement extends HTMLElement {
124
124
  const response = await fetch(this.src, {
125
125
  credentials: 'same-origin',
126
126
  method: 'POST',
127
+ headers: {
128
+ 'Requested-With': 'XMLHttpRequest'
129
+ },
127
130
  body
128
131
  })
129
132
  if (response.ok) {
@@ -247,7 +247,12 @@ class ToolTipElement extends HTMLElement {
247
247
  if (this.type === 'label') {
248
248
  let labelledBy = this.control.getAttribute('aria-labelledby');
249
249
  if (labelledBy) {
250
- labelledBy = `${labelledBy} ${this.id}`;
250
+ if (!labelledBy.split(' ').includes(this.id)) {
251
+ labelledBy = `${labelledBy} ${this.id}`;
252
+ }
253
+ else {
254
+ labelledBy = `${labelledBy}`;
255
+ }
251
256
  }
252
257
  else {
253
258
  labelledBy = this.id;
@@ -258,7 +263,17 @@ class ToolTipElement extends HTMLElement {
258
263
  }
259
264
  else {
260
265
  let describedBy = this.control.getAttribute('aria-describedby');
261
- describedBy ? (describedBy = `${describedBy} ${this.id}`) : (describedBy = this.id);
266
+ if (describedBy) {
267
+ if (!describedBy.split(' ').includes(this.id)) {
268
+ describedBy = `${describedBy} ${this.id}`;
269
+ }
270
+ else {
271
+ describedBy = `${describedBy}`;
272
+ }
273
+ }
274
+ else {
275
+ describedBy = this.id;
276
+ }
262
277
  this.control.setAttribute('aria-describedby', describedBy);
263
278
  }
264
279
  }
@@ -264,7 +264,11 @@ class ToolTipElement extends HTMLElement {
264
264
  if (this.type === 'label') {
265
265
  let labelledBy = this.control.getAttribute('aria-labelledby')
266
266
  if (labelledBy) {
267
- labelledBy = `${labelledBy} ${this.id}`
267
+ if (!labelledBy.split(' ').includes(this.id)) {
268
+ labelledBy = `${labelledBy} ${this.id}`
269
+ } else {
270
+ labelledBy = `${labelledBy}`
271
+ }
268
272
  } else {
269
273
  labelledBy = this.id
270
274
  }
@@ -274,7 +278,15 @@ class ToolTipElement extends HTMLElement {
274
278
  this.setAttribute('aria-hidden', 'true')
275
279
  } else {
276
280
  let describedBy = this.control.getAttribute('aria-describedby')
277
- describedBy ? (describedBy = `${describedBy} ${this.id}`) : (describedBy = this.id)
281
+ if (describedBy) {
282
+ if (!describedBy.split(' ').includes(this.id)) {
283
+ describedBy = `${describedBy} ${this.id}`
284
+ } else {
285
+ describedBy = `${describedBy}`
286
+ }
287
+ } else {
288
+ describedBy = this.id
289
+ }
278
290
  this.control.setAttribute('aria-describedby', describedBy)
279
291
  }
280
292
  } else if (name === 'data-direction') {
@@ -1,4 +1,4 @@
1
- <%= render Primer::ConditionalWrapper.new(condition: render_tooltip?, tag: :div, classes: "Button-withTooltip") do %>
1
+ <%= render Primer::ConditionalWrapper.new(condition: render_tooltip?, tag: :div, classes: "Button-withTooltip", **@wrapper_arguments) do %>
2
2
  <%= render Primer::Beta::BaseButton.new(**@system_arguments) do -%>
3
3
  <%= render Primer::OcticonComponent.new(icon: @icon, classes: "Button-visual") %>
4
4
  <% end -%>
@@ -47,17 +47,19 @@ module Primer
47
47
  # <%= render(Primer::Beta::IconButton.new(icon: :search, "aria-label": "Search", tooltip_direction: :e)) %>
48
48
  #
49
49
  # @param icon [String] Name of <%= link_to_octicons %> to use.
50
+ # @param wrapper_arguments [Hash] Optional keyword arguments to be passed to the wrapper `<div>` tag.
50
51
  # @param scheme [Symbol] <%= one_of(Primer::Beta::IconButton::SCHEME_OPTIONS) %>
51
52
  # @param size [Symbol] <%= one_of(Primer::Beta::Button::SIZE_OPTIONS) %>
52
- # @param tag [Symbol] <%= one_of(Primer::BaseButton::TAG_OPTIONS) %>
53
- # @param type [Symbol] <%= one_of(Primer::BaseButton::TYPE_OPTIONS) %>
53
+ # @param tag [Symbol] <%= one_of(Primer::Beta::BaseButton::TAG_OPTIONS) %>
54
+ # @param type [Symbol] <%= one_of(Primer::Beta::BaseButton::TYPE_OPTIONS) %>
54
55
  # @param aria-label [String] String that can be read by assistive technology. A label should be short and concise. See the accessibility section for more information.
55
56
  # @param aria-description [String] String that can be read by assistive technology. A description can be longer as it is intended to provide more context and information. See the accessibility section for more information.
56
57
  # @param tooltip_direction [Symbol] (Primer::Alpha::Tooltip::DIRECTION_DEFAULT) <%= one_of(Primer::Alpha::Tooltip::DIRECTION_OPTIONS) %>
57
58
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
58
- def initialize(icon:, scheme: DEFAULT_SCHEME, tooltip_direction: Primer::Alpha::Tooltip::DIRECTION_DEFAULT, size: Primer::Beta::Button::DEFAULT_SIZE, **system_arguments)
59
+ def initialize(icon:, scheme: DEFAULT_SCHEME, wrapper_arguments: {}, tooltip_direction: Primer::Alpha::Tooltip::DIRECTION_DEFAULT, size: Primer::Beta::Button::DEFAULT_SIZE, **system_arguments)
59
60
  @icon = icon
60
61
 
62
+ @wrapper_arguments = wrapper_arguments
61
63
  @system_arguments = system_arguments
62
64
  @system_arguments[:id] ||= "icon-button-#{SecureRandom.hex(4)}"
63
65
 
@@ -3,7 +3,7 @@
3
3
  module Primer
4
4
  # Use `Button` for actions (e.g. in forms). Use links for destinations, or moving from one page to another.
5
5
  class ButtonComponent < Primer::Component
6
- status :beta
6
+ status :deprecated
7
7
 
8
8
  DEFAULT_SCHEME = :default
9
9
  LINK_SCHEME = :link
@@ -0,0 +1,14 @@
1
+ export declare class ActionBarElement extends HTMLElement {
2
+ #private;
3
+ items: HTMLElement[];
4
+ menuItems: HTMLElement[];
5
+ itemContainer: HTMLElement;
6
+ moreMenu: HTMLElement;
7
+ connectedCallback(): void;
8
+ disconnectedCallback(): void;
9
+ }
10
+ declare global {
11
+ interface Window {
12
+ ActionBarElement: typeof ActionBarElement;
13
+ }
14
+ }
@@ -0,0 +1,141 @@
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
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
14
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
15
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
16
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
17
+ };
18
+ var _ActionBarElement_instances, _ActionBarElement_observer, _ActionBarElement_initialBarWidth, _ActionBarElement_previousBarWidth, _ActionBarElement_itemGap, _ActionBarElement_focusController, _ActionBarElement_focusSettings, _ActionBarElement_availableSpace, _ActionBarElement_shrinking, _ActionBarElement_growing;
19
+ import { controller, targets, target } from '@github/catalyst';
20
+ import { focusZone, FocusKeys } from '@primer/behaviors';
21
+ let ActionBarElement = class ActionBarElement extends HTMLElement {
22
+ constructor() {
23
+ super(...arguments);
24
+ _ActionBarElement_instances.add(this);
25
+ _ActionBarElement_observer.set(this, void 0);
26
+ _ActionBarElement_initialBarWidth.set(this, void 0);
27
+ _ActionBarElement_previousBarWidth.set(this, void 0);
28
+ _ActionBarElement_itemGap.set(this, void 0);
29
+ _ActionBarElement_focusController.set(this, void 0);
30
+ _ActionBarElement_focusSettings.set(this, {
31
+ bindKeys: FocusKeys.ArrowHorizontal | FocusKeys.HomeAndEnd
32
+ });
33
+ }
34
+ connectedCallback() {
35
+ var _a, _b, _c;
36
+ __classPrivateFieldSet(this, _ActionBarElement_initialBarWidth, this.offsetWidth, "f");
37
+ __classPrivateFieldSet(this, _ActionBarElement_previousBarWidth, this.offsetWidth, "f");
38
+ __classPrivateFieldSet(this, _ActionBarElement_itemGap, parseInt((_a = window.getComputedStyle(this.itemContainer)) === null || _a === void 0 ? void 0 : _a.columnGap, 10) || 0, "f");
39
+ // Calculate the width of all the items before hiding anything
40
+ for (const item of this.items) {
41
+ const width = item.getBoundingClientRect().width;
42
+ const marginLeft = parseInt((_b = window.getComputedStyle(item)) === null || _b === void 0 ? void 0 : _b.marginLeft, 10);
43
+ const marginRight = parseInt((_c = window.getComputedStyle(item)) === null || _c === void 0 ? void 0 : _c.marginRight, 10);
44
+ item.setAttribute('data-offset-width', `${width + marginLeft + marginRight}`);
45
+ }
46
+ __classPrivateFieldSet(this, _ActionBarElement_focusController, focusZone(this, __classPrivateFieldGet(this, _ActionBarElement_focusSettings, "f")), "f");
47
+ this.style.maxWidth = `${this.itemContainer.offsetWidth}px`;
48
+ // Calculate visible items on page load until there is enough space
49
+ // to show all items or the first item is hidden
50
+ while (__classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_availableSpace).call(this) < this.moreMenu.offsetWidth + __classPrivateFieldGet(this, _ActionBarElement_itemGap, "f") * 0.5 && !this.items[0].hidden) {
51
+ __classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_shrinking).call(this);
52
+ }
53
+ this.style.overflow = 'visible';
54
+ __classPrivateFieldSet(this, _ActionBarElement_observer, new ResizeObserver(entries => {
55
+ for (const entry of entries) {
56
+ // Only recalculate if the bar width changed
57
+ if (__classPrivateFieldGet(this, _ActionBarElement_initialBarWidth, "f") !== entry.contentRect.width) {
58
+ if (entry.contentRect.width < __classPrivateFieldGet(this, _ActionBarElement_previousBarWidth, "f")) {
59
+ __classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_shrinking).call(this);
60
+ }
61
+ else {
62
+ __classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_growing).call(this);
63
+ }
64
+ __classPrivateFieldSet(this, _ActionBarElement_previousBarWidth, entry.contentRect.width, "f");
65
+ }
66
+ }
67
+ }), "f");
68
+ __classPrivateFieldGet(this, _ActionBarElement_observer, "f").observe(this);
69
+ }
70
+ disconnectedCallback() {
71
+ var _a;
72
+ (_a = __classPrivateFieldGet(this, _ActionBarElement_focusController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
73
+ __classPrivateFieldGet(this, _ActionBarElement_observer, "f").unobserve(this);
74
+ }
75
+ };
76
+ _ActionBarElement_observer = new WeakMap(), _ActionBarElement_initialBarWidth = new WeakMap(), _ActionBarElement_previousBarWidth = new WeakMap(), _ActionBarElement_itemGap = new WeakMap(), _ActionBarElement_focusController = new WeakMap(), _ActionBarElement_focusSettings = new WeakMap(), _ActionBarElement_instances = new WeakSet(), _ActionBarElement_availableSpace = function _ActionBarElement_availableSpace() {
77
+ // Get the offset of the item container from the container edge
78
+ return this.offsetWidth - this.itemContainer.offsetWidth;
79
+ }, _ActionBarElement_shrinking = function _ActionBarElement_shrinking() {
80
+ var _a;
81
+ if (this.items[0].hidden) {
82
+ return;
83
+ }
84
+ const gapSpace = __classPrivateFieldGet(this, _ActionBarElement_itemGap, "f") * 0.5;
85
+ if (__classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_availableSpace).call(this) < this.moreMenu.offsetWidth + gapSpace) {
86
+ const visibleItems = this.items.filter(item => !item.hidden);
87
+ const hiddenMenuItems = this.menuItems.filter(item => item.hidden);
88
+ visibleItems[visibleItems.length - 1].hidden = true;
89
+ hiddenMenuItems[hiddenMenuItems.length - 1].hidden = false;
90
+ if (this.moreMenu.hidden) {
91
+ this.moreMenu.hidden = false;
92
+ }
93
+ // Reset focus controller
94
+ (_a = __classPrivateFieldGet(this, _ActionBarElement_focusController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
95
+ __classPrivateFieldSet(this, _ActionBarElement_focusController, focusZone(this, __classPrivateFieldGet(this, _ActionBarElement_focusSettings, "f")), "f");
96
+ }
97
+ }, _ActionBarElement_growing = function _ActionBarElement_growing() {
98
+ var _a;
99
+ if (this.moreMenu.hidden) {
100
+ return;
101
+ }
102
+ const gapSpace = __classPrivateFieldGet(this, _ActionBarElement_itemGap, "f") * 0.5;
103
+ const hiddenItems = this.items.filter(item => item.hidden);
104
+ if (hiddenItems.length === 0) {
105
+ return;
106
+ }
107
+ const hiddenItemWidth = Number(hiddenItems[0].getAttribute('data-offset-width'));
108
+ if (__classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_availableSpace).call(this) >= this.moreMenu.offsetWidth + hiddenItemWidth + gapSpace) {
109
+ const visibleMenuItems = this.menuItems.filter(item => !item.hidden);
110
+ hiddenItems[0].hidden = false;
111
+ visibleMenuItems[0].hidden = true;
112
+ if (hiddenItems.length === 2) {
113
+ hiddenItems[1].hidden = false;
114
+ visibleMenuItems[1].hidden = true;
115
+ }
116
+ // If there was only one item left, hide the more menu
117
+ if (hiddenItems.length <= 2) {
118
+ this.moreMenu.hidden = true;
119
+ }
120
+ // Reset focus controller
121
+ (_a = __classPrivateFieldGet(this, _ActionBarElement_focusController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
122
+ __classPrivateFieldSet(this, _ActionBarElement_focusController, focusZone(this, __classPrivateFieldGet(this, _ActionBarElement_focusSettings, "f")), "f");
123
+ }
124
+ };
125
+ __decorate([
126
+ targets
127
+ ], ActionBarElement.prototype, "items", void 0);
128
+ __decorate([
129
+ targets
130
+ ], ActionBarElement.prototype, "menuItems", void 0);
131
+ __decorate([
132
+ target
133
+ ], ActionBarElement.prototype, "itemContainer", void 0);
134
+ __decorate([
135
+ target
136
+ ], ActionBarElement.prototype, "moreMenu", void 0);
137
+ ActionBarElement = __decorate([
138
+ controller
139
+ ], ActionBarElement);
140
+ export { ActionBarElement };
141
+ window.ActionBarElement = ActionBarElement;
@@ -12,7 +12,7 @@ module Primer
12
12
  # Either `aria-label` or `aria-description` will be used for the `Tooltip` text, depending on which one is present.
13
13
  # [Learn more about best functional image practices (WAI Images)](https://www.w3.org/WAI/tutorials/images/functional)
14
14
  class IconButton < Primer::Component
15
- status :beta
15
+ status :deprecated
16
16
 
17
17
  DEFAULT_SCHEME = :default
18
18
  SCHEME_MAPPINGS = {
@@ -33,7 +33,7 @@ module Primer
33
33
 
34
34
  given_value
35
35
  else
36
- if fallback_raises && ENV["RAILS_ENV"] != "production" && ENV["STORYBOOK"] != "true"
36
+ if fallback_raises && ENV["RAILS_ENV"] != "production"
37
37
  raise InvalidValueError, <<~MSG
38
38
  fetch_or_fallback was called with an invalid value.
39
39
 
@@ -7,10 +7,9 @@ module Primer
7
7
  DEPRECATED_COMPONENTS = {
8
8
  "Primer::Alpha::AutoComplete" => "Primer::Beta::AutoComplete",
9
9
  "Primer::Alpha::AutoComplete::Item" => "Primer::Beta::AutoComplete::Item",
10
- "Primer::BaseButton" => "Primer::Beta::BaseButton",
11
10
  "Primer::BlankslateComponent" => "Primer::Beta::Blankslate",
12
11
  "Primer::BoxComponent" => "Primer::Box",
13
- "Primer::ButtonGroup" => "Primer::Beta::ButtonGroup",
12
+ "Primer::ButtonComponent" => "Primer::Beta::Button",
14
13
  "Primer::CloseButton" => "Primer::Beta::CloseButton",
15
14
  "Primer::CounterComponent" => "Primer::Beta::Counter",
16
15
  "Primer::DetailsComponent" => "Primer::Beta::Details",
@@ -19,6 +18,7 @@ module Primer
19
18
  "Primer::FlexItemComponent" => nil,
20
19
  "Primer::HeadingComponent" => "Primer::Beta::Heading",
21
20
  "Primer::HiddenTextExpander" => "Primer::Alpha::HiddenTextExpander",
21
+ "Primer::IconButton" => "Primer::Beta::IconButton",
22
22
  "Primer::Tooltip" => "Primer::Alpha::Tooltip"
23
23
  }.freeze
24
24
 
@@ -14,8 +14,8 @@ module Primer
14
14
  super(*args, classify(options), &block)
15
15
  end
16
16
 
17
- def check_box(*args, **options, &block)
18
- super(*args, classify(options), &block)
17
+ def check_box(method, options = {}, checked_value = 1, unchecked_value = 0, &block)
18
+ super(method, classify(options), checked_value, unchecked_value, &block)
19
19
  end
20
20
 
21
21
  def radio_button(*args, **options, &block)
@@ -1,5 +1,9 @@
1
1
  <div class="FormControl-checkbox-wrap">
2
- <%= builder.check_box(@input.name, **@input.input_arguments) %>
2
+ <% if @input.scheme == :array %>
3
+ <%= builder.check_box(@input.name, @input.input_arguments, checked_value, nil) %>
4
+ <% else %>
5
+ <%= builder.check_box(@input.name, @input.input_arguments) %>
6
+ <% end %>
3
7
  <span class="FormControl-checkbox-labelWrap">
4
8
  <%= builder.label(@input.name, **@input.label_arguments) do %>
5
9
  <%= @input.label %>
@@ -10,6 +10,17 @@ module Primer
10
10
  @input = input
11
11
  @input.add_label_classes("FormControl-label")
12
12
  @input.add_input_classes("FormControl-checkbox")
13
+
14
+ return unless @input.scheme == :array
15
+
16
+ @input.input_arguments[:multiple] = true
17
+ @input.label_arguments[:value] = checked_value
18
+ end
19
+
20
+ private
21
+
22
+ def checked_value
23
+ @input.value || "1"
13
24
  end
14
25
  end
15
26
  end
@@ -7,7 +7,8 @@ module Primer
7
7
  class CheckBoxGroupInput < Input
8
8
  attr_reader :label, :check_boxes
9
9
 
10
- def initialize(label: nil, **system_arguments)
10
+ def initialize(name: nil, label: nil, **system_arguments)
11
+ @name = name
11
12
  @label = label
12
13
  @check_boxes = []
13
14
 
@@ -31,9 +32,21 @@ module Primer
31
32
  end
32
33
 
33
34
  def check_box(**system_arguments)
34
- @check_boxes << CheckBoxInput.new(
35
- builder: @builder, form: @form, **system_arguments
36
- )
35
+ args = {
36
+ name: @name,
37
+ **system_arguments,
38
+ builder: @builder,
39
+ form: @form,
40
+ scheme: scheme
41
+ }
42
+
43
+ @check_boxes << CheckBoxInput.new(**args)
44
+ end
45
+
46
+ private
47
+
48
+ def scheme
49
+ @name ? :array : :boolean
37
50
  end
38
51
  end
39
52
  end
@@ -5,11 +5,20 @@ module Primer
5
5
  module Dsl
6
6
  # :nodoc:
7
7
  class CheckBoxInput < Input
8
- attr_reader :name, :label
8
+ DEFAULT_SCHEME = :boolean
9
+ SCHEMES = [DEFAULT_SCHEME, :array].freeze
10
+
11
+ attr_reader :name, :label, :value, :scheme
12
+
13
+ def initialize(name:, label:, value: nil, scheme: DEFAULT_SCHEME, **system_arguments)
14
+ raise ArgumentError, "Check box scheme must be one of #{SCHEMES.join(', ')}" unless SCHEMES.include?(scheme)
15
+
16
+ raise ArgumentError, "Check box needs an explicit value if scheme is array" if scheme == :array && value.nil?
9
17
 
10
- def initialize(name:, label:, **system_arguments)
11
18
  @name = name
12
19
  @label = label
20
+ @value = value
21
+ @scheme = scheme
13
22
 
14
23
  super(**system_arguments)
15
24
  end
@@ -21,6 +30,16 @@ module Primer
21
30
  def type
22
31
  :check_box
23
32
  end
33
+
34
+ private
35
+
36
+ def caption_template_name
37
+ @caption_template_name ||= if @scheme == :array
38
+ :"#{name}_#{value}"
39
+ else
40
+ name.to_sym
41
+ end
42
+ end
24
43
  end
25
44
  end
26
45
  end
@@ -5,7 +5,7 @@ module Primer
5
5
  module VERSION
6
6
  MAJOR = 0
7
7
  MINOR = 0
8
- PATCH = 94
8
+ PATCH = 95
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH].join(".")
11
11
  end
@@ -5,7 +5,7 @@ namespace :coverage do
5
5
  require "simplecov"
6
6
  require "simplecov-console"
7
7
 
8
- SimpleCov.minimum_coverage 100
8
+ SimpleCov.minimum_coverage 99
9
9
 
10
10
  SimpleCov.collate Dir["simplecov-resultset-*/.resultset.json"], "rails" do
11
11
  formatter SimpleCov::Formatter::Console
data/lib/tasks/docs.rake CHANGED
@@ -131,7 +131,7 @@ namespace :docs do
131
131
  f.puts("componentId: #{data[:component_id]}")
132
132
  f.puts("status: #{data[:status]}")
133
133
  f.puts("source: #{data[:source]}")
134
- f.puts("storybook: #{data[:storybook]}")
134
+ f.puts("lookbook: #{data[:lookbook]}") if preview_exists?(component)
135
135
  f.puts("---")
136
136
  f.puts
137
137
  f.puts("import Example from '#{data[:example_path]}'")
@@ -276,8 +276,8 @@ namespace :docs do
276
276
  raise
277
277
  end
278
278
 
279
- File.open("static/arguments.yml", "w") do |f|
280
- f.puts YAML.dump(args_for_components)
279
+ File.open("static/arguments.json", "w") do |f|
280
+ f.puts JSON.pretty_generate(args_for_components)
281
281
  end
282
282
 
283
283
  # Build system arguments docs from BaseComponent
@@ -371,27 +371,25 @@ namespace :docs do
371
371
 
372
372
  yard_example_tags = initialize_method.tags(:example)
373
373
 
374
- path = Pathname.new("test/previews/primer/docs/#{short_name.underscore}_preview.rb")
374
+ path = Pathname.new("test/previews/docs/#{short_name.underscore}_preview.rb")
375
375
  path.dirname.mkdir unless path.dirname.exist?
376
376
 
377
377
  File.open(path, "w") do |f|
378
- f.puts("module Primer")
379
- f.puts(" module Docs")
380
- f.puts(" class #{short_name}Preview < ViewComponent::Preview")
378
+ f.puts("module Docs")
379
+ f.puts(" class #{short_name}Preview < ViewComponent::Preview")
381
380
 
382
381
  yard_example_tags.each_with_index do |tag, index|
383
382
  name, _, code = parse_example_tag(tag)
384
383
  method_name = name.split("|").first.downcase.parameterize.underscore
385
- f.puts(" def #{method_name}; end")
384
+ f.puts(" def #{method_name}; end")
386
385
  f.puts unless index == yard_example_tags.size - 1
387
- path = Pathname.new("test/previews/primer/docs/#{short_name.underscore}_preview/#{method_name}.html.erb")
386
+ path = Pathname.new("test/previews/docs/#{short_name.underscore}_preview/#{method_name}.html.erb")
388
387
  path.dirname.mkdir unless path.dirname.exist?
389
388
  File.open(path, "w") do |view_file|
390
389
  view_file.puts(code.to_s)
391
390
  end
392
391
  end
393
392
 
394
- f.puts(" end")
395
393
  f.puts(" end")
396
394
  f.puts("end")
397
395
  end
@@ -399,7 +397,7 @@ namespace :docs do
399
397
  end
400
398
 
401
399
  def generate_yard_registry
402
- ENV["SKIP_STORYBOOK_PRELOAD"] = "1"
400
+ ENV["RAILS_ENV"] = "test"
403
401
  require File.expand_path("./../../demo/config/environment.rb", __dir__)
404
402
  require "primer/view_components"
405
403
  require "yard/docs_helper"
@@ -465,7 +463,7 @@ namespace :docs do
465
463
  component_id: short_name.underscore,
466
464
  status: status.capitalize,
467
465
  source: source_url(component),
468
- storybook: storybook_url(component),
466
+ lookbook: lookbook_url(component),
469
467
  path: "docs/content/components/#{status_path}#{short_name.downcase}.md",
470
468
  example_path: example_path(component),
471
469
  require_js_path: require_js_path(component)
@@ -478,10 +476,16 @@ namespace :docs do
478
476
  "https://github.com/primer/view_components/tree/main/app/components/#{path}.rb"
479
477
  end
480
478
 
481
- def storybook_url(component)
482
- path = component.name.split("::").map { |n| n.underscore.dasherize }.join("-")
479
+ def lookbook_url(component)
480
+ path = component.name.underscore.gsub("_component", "")
483
481
 
484
- "https://primer.style/view-components/stories/?path=/story/#{path}"
482
+ "https://primer.style/view-components/lookbook/inspect/#{path}/default/"
483
+ end
484
+
485
+ def preview_exists?(component)
486
+ path = component.name.underscore
487
+
488
+ File.exist?("test/previews/#{path}_preview.rb")
485
489
  end
486
490
 
487
491
  def example_path(component)
@@ -2,7 +2,7 @@
2
2
 
3
3
  namespace :static do
4
4
  task :dump do
5
- ENV["SKIP_STORYBOOK_PRELOAD"] = "1"
5
+ ENV["RAILS_ENV"] = "test"
6
6
  require File.expand_path("./../../demo/config/environment.rb", __dir__)
7
7
  require "primer/view_components"
8
8
  # Loads all components for `.descendants` to work properly
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake/testtask"
4
+
5
+ namespace :test do
6
+ desc "Run all tests"
7
+ task all: [:fast, :system]
8
+
9
+ Rake::TestTask.new(:single) do |t|
10
+ ENV["TZ"] = "Asia/Taipei"
11
+
12
+ t.libs << "test"
13
+ t.libs << "lib"
14
+ t.test_files = FileList[ENV["TESTS"]]
15
+ end
16
+
17
+ Rake::TestTask.new(:fast) do |t|
18
+ ENV["TZ"] = "Asia/Taipei"
19
+
20
+ t.libs << "test"
21
+ t.libs << "lib"
22
+ t.test_files = FileList[
23
+ "test/components/**/*_test.rb",
24
+ "test/lib/**/*_test.rb",
25
+ "test/primer/**/*_test.rb",
26
+ "test/linters/**/*_test.rb",
27
+ "test/rubocop/**/*_test.rb"
28
+ ]
29
+ end
30
+
31
+ Rake::TestTask.new(:system) do |t|
32
+ ENV["TZ"] = "Asia/Taipei"
33
+
34
+ t.libs << "test"
35
+ t.libs << "lib"
36
+ t.test_files = FileList["test/system/**/*_test.rb", "test/previews/**/*_test.rb"]
37
+ end
38
+
39
+ Rake::TestTask.new(:bench) do |t|
40
+ t.libs << "test"
41
+ t.test_files = FileList["test/benchmarks/**/bench_*.rb"]
42
+ t.verbose = true
43
+ end
44
+ end
45
+
46
+ task :test do
47
+ if ENV["TESTS"]
48
+ Rake::Task["test:single"].invoke
49
+ else
50
+ Rake::Task["test:all"].invoke
51
+ end
52
+ end
53
+
54
+ task bench: "test:bench"
@@ -4,6 +4,7 @@ namespace :utilities do
4
4
  task :build do
5
5
  require "yaml"
6
6
  require "json"
7
+ ENV["RAILS_ENV"] = "test"
7
8
  require File.expand_path("./../../demo/config/environment.rb", __dir__)
8
9
  require "primer/classify/utilities"
9
10