primer_view_components 0.0.94 → 0.0.95
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/components/primer/alpha/dialog.rb +3 -3
- data/app/components/primer/alpha/toggle_switch.js +3 -0
- data/app/components/primer/alpha/toggle_switch.ts +3 -0
- data/app/components/primer/alpha/tool_tip.js +17 -2
- data/app/components/primer/alpha/tool_tip.ts +14 -2
- data/app/components/primer/beta/icon_button.html.erb +1 -1
- data/app/components/primer/beta/icon_button.rb +5 -3
- data/app/components/primer/button_component.rb +1 -1
- data/app/components/primer/experimental/action_bar.d.ts +14 -0
- data/app/components/primer/experimental/action_bar.js +141 -0
- data/app/components/primer/icon_button.rb +1 -1
- data/app/lib/primer/fetch_or_fallback_helper.rb +1 -1
- data/lib/primer/deprecations.rb +2 -2
- data/lib/primer/forms/builder.rb +2 -2
- data/lib/primer/forms/check_box.html.erb +5 -1
- data/lib/primer/forms/check_box.rb +11 -0
- data/lib/primer/forms/dsl/check_box_group_input.rb +17 -4
- data/lib/primer/forms/dsl/check_box_input.rb +21 -2
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/tasks/coverage.rake +1 -1
- data/lib/tasks/docs.rake +19 -15
- data/lib/tasks/static.rake +1 -1
- data/lib/tasks/test.rake +54 -0
- data/lib/tasks/utilities.rake +1 -0
- data/static/arguments.json +2042 -0
- data/static/audited_at.json +0 -2
- data/static/constants.json +0 -4
- data/static/statuses.json +2 -4
- metadata +6 -6
- data/app/components/primer/base_button.rb +0 -7
- data/app/components/primer/button_group.rb +0 -7
- data/lib/tasks/deprecated.rake +0 -27
- 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
|
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
|
111
|
-
# @param position_narrow [Symbol] The
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
|
@@ -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 :
|
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"
|
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
|
|
data/lib/primer/deprecations.rb
CHANGED
@@ -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::
|
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
|
|
data/lib/primer/forms/builder.rb
CHANGED
@@ -14,8 +14,8 @@ module Primer
|
|
14
14
|
super(*args, classify(options), &block)
|
15
15
|
end
|
16
16
|
|
17
|
-
def check_box(
|
18
|
-
super(
|
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
|
-
|
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
|
-
|
35
|
-
|
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
|
-
|
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
|
data/lib/tasks/coverage.rake
CHANGED
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("
|
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.
|
280
|
-
f.puts
|
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/
|
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
|
379
|
-
f.puts("
|
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("
|
384
|
+
f.puts(" def #{method_name}; end")
|
386
385
|
f.puts unless index == yard_example_tags.size - 1
|
387
|
-
path = Pathname.new("test/previews/
|
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["
|
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
|
-
|
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
|
482
|
-
path = component.name.
|
479
|
+
def lookbook_url(component)
|
480
|
+
path = component.name.underscore.gsub("_component", "")
|
483
481
|
|
484
|
-
"https://primer.style/view-components/
|
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)
|
data/lib/tasks/static.rake
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
namespace :static do
|
4
4
|
task :dump do
|
5
|
-
ENV["
|
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
|
data/lib/tasks/test.rake
ADDED
@@ -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"
|