primer_view_components 0.29.0 → 0.31.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -0
- data/app/assets/javascripts/app/components/primer/alpha/select_panel_element.d.ts +0 -1
- data/app/assets/javascripts/app/components/primer/alpha/toggle_switch.d.ts +4 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/assets/styles/primer_view_components.css +1 -1
- data/app/assets/styles/primer_view_components.css.map +1 -1
- data/app/components/primer/alpha/action_list/item.html.erb +1 -5
- data/app/components/primer/alpha/action_list/item.rb +10 -3
- data/app/components/primer/alpha/action_list.css +1 -1
- data/app/components/primer/alpha/action_list.css.map +1 -1
- data/app/components/primer/alpha/action_list.pcss +1 -1
- data/app/components/primer/alpha/segmented_control.css +1 -1
- data/app/components/primer/alpha/segmented_control.css.map +1 -1
- data/app/components/primer/alpha/segmented_control.pcss +1 -0
- data/app/components/primer/alpha/select_panel.rb +2 -1
- data/app/components/primer/alpha/select_panel_element.d.ts +0 -1
- data/app/components/primer/alpha/select_panel_element.js +72 -18
- data/app/components/primer/alpha/select_panel_element.ts +77 -22
- data/app/components/primer/alpha/toggle_switch.d.ts +4 -0
- data/app/components/primer/alpha/toggle_switch.js +16 -4
- data/app/components/primer/alpha/toggle_switch.rb +4 -2
- data/app/components/primer/alpha/toggle_switch.ts +19 -4
- data/app/components/primer/alpha/tool_tip.js +2 -2
- data/app/components/primer/alpha/tool_tip.ts +2 -2
- data/app/components/primer/beta/relative_time.rb +4 -4
- data/app/components/primer/beta/state.css +1 -1
- data/app/components/primer/beta/state.css.map +1 -1
- data/app/components/primer/beta/state.pcss +4 -0
- data/lib/primer/view_components/linters/details_menu_migration.rb +30 -1
- data/lib/primer/view_components/linters/tooltipped_migration.rb +1 -1
- data/lib/primer/view_components/version.rb +1 -1
- data/previews/primer/alpha/action_list_preview.rb +1 -1
- data/previews/primer/alpha/select_panel_preview/list_of_links.html.erb +16 -0
- data/previews/primer/alpha/select_panel_preview/playground.html.erb +2 -1
- data/previews/primer/alpha/select_panel_preview.rb +12 -1
- data/previews/primer/alpha/toggle_switch_preview.rb +4 -0
- data/static/arguments.json +11 -5
- data/static/info_arch.json +77 -11
- data/static/previews.json +27 -0
- metadata +3 -2
@@ -11,7 +11,6 @@ type SelectedItem = {
|
|
11
11
|
label: string | null | undefined
|
12
12
|
value: string | null | undefined
|
13
13
|
inputName: string | null | undefined
|
14
|
-
element: SelectPanelItem
|
15
14
|
}
|
16
15
|
|
17
16
|
const validSelectors = ['[role="option"]']
|
@@ -54,6 +53,7 @@ const updateWhenVisible = (() => {
|
|
54
53
|
})
|
55
54
|
resizeObserver.observe(el.ownerDocument.documentElement)
|
56
55
|
el.addEventListener('dialog:close', () => {
|
56
|
+
el.invokerElement?.setAttribute('aria-expanded', 'false')
|
57
57
|
anchors.delete(el)
|
58
58
|
})
|
59
59
|
el.addEventListener('dialog:open', () => {
|
@@ -84,6 +84,7 @@ export class SelectPanelElement extends HTMLElement {
|
|
84
84
|
#selectedItems: Map<string, SelectedItem> = new Map()
|
85
85
|
#loadingDelayTimeoutId: number | null = null
|
86
86
|
#loadingAnnouncementTimeoutId: number | null = null
|
87
|
+
#hasLoadedData = false
|
87
88
|
|
88
89
|
get open(): boolean {
|
89
90
|
return this.dialog.open
|
@@ -215,6 +216,17 @@ export class SelectPanelElement extends HTMLElement {
|
|
215
216
|
for (const entry of entries) {
|
216
217
|
const elem = entry.target
|
217
218
|
if (entry.isIntersecting && elem === this.dialog) {
|
219
|
+
// Focus on the filter input when the dialog opens to work around a Safari limitation
|
220
|
+
// that prevents the autofocus attribute from working as it does in other browsers
|
221
|
+
if (this.filterInputTextField) {
|
222
|
+
if (document.activeElement !== this.filterInputTextField) {
|
223
|
+
this.filterInputTextField.focus()
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
// signal that any focus hijinks are finished (thanks Safari)
|
228
|
+
this.dialog.setAttribute('data-ready', 'true')
|
229
|
+
|
218
230
|
this.updateAnchorPosition()
|
219
231
|
|
220
232
|
if (this.#fetchStrategy === FetchStrategy.LOCAL) {
|
@@ -227,12 +239,12 @@ export class SelectPanelElement extends HTMLElement {
|
|
227
239
|
this.#waitForCondition(
|
228
240
|
() => Boolean(this.dialog),
|
229
241
|
() => {
|
242
|
+
this.#dialogIntersectionObserver.observe(this.dialog)
|
243
|
+
this.dialog.addEventListener('close', this, {signal})
|
244
|
+
|
230
245
|
if (this.getAttribute('data-open-on-load') === 'true') {
|
231
246
|
this.show()
|
232
247
|
}
|
233
|
-
|
234
|
-
this.#dialogIntersectionObserver.observe(this.dialog)
|
235
|
-
this.dialog.addEventListener('close', this, {signal})
|
236
248
|
},
|
237
249
|
)
|
238
250
|
|
@@ -295,7 +307,7 @@ export class SelectPanelElement extends HTMLElement {
|
|
295
307
|
}
|
296
308
|
|
297
309
|
// <li> elements should not themselves be tabbable
|
298
|
-
item.
|
310
|
+
item.removeAttribute('tabindex')
|
299
311
|
}
|
300
312
|
} else {
|
301
313
|
for (const item of this.items) {
|
@@ -309,7 +321,7 @@ export class SelectPanelElement extends HTMLElement {
|
|
309
321
|
}
|
310
322
|
|
311
323
|
// <li> elements should not themselves be tabbable
|
312
|
-
item.
|
324
|
+
item.removeAttribute('tabindex')
|
313
325
|
}
|
314
326
|
}
|
315
327
|
|
@@ -369,6 +381,7 @@ export class SelectPanelElement extends HTMLElement {
|
|
369
381
|
}
|
370
382
|
}
|
371
383
|
}
|
384
|
+
|
372
385
|
this.#updateInput()
|
373
386
|
}
|
374
387
|
|
@@ -383,14 +396,15 @@ export class SelectPanelElement extends HTMLElement {
|
|
383
396
|
value,
|
384
397
|
label: itemContent.querySelector('.ActionListItem-label')?.textContent?.trim(),
|
385
398
|
inputName: itemContent.getAttribute('data-input-name'),
|
386
|
-
element: item,
|
387
399
|
})
|
388
400
|
}
|
389
401
|
}
|
390
402
|
|
391
|
-
#removeSelectedItem(item:
|
392
|
-
const
|
403
|
+
#removeSelectedItem(item: SelectPanelItem) {
|
404
|
+
const itemContent = this.#getItemContent(item)
|
405
|
+
if (!itemContent) return
|
393
406
|
|
407
|
+
const value = itemContent.getAttribute('data-value')
|
394
408
|
if (value) {
|
395
409
|
this.#selectedItems.delete(value)
|
396
410
|
}
|
@@ -450,6 +464,10 @@ export class SelectPanelElement extends HTMLElement {
|
|
450
464
|
}
|
451
465
|
|
452
466
|
if (event.target === this.dialog && event.type === 'close') {
|
467
|
+
// Remove data-ready so it can be set the next time the panel is opened
|
468
|
+
this.dialog.removeAttribute('data-ready')
|
469
|
+
this.invokerElement?.setAttribute('aria-expanded', 'false')
|
470
|
+
|
453
471
|
this.dispatchEvent(
|
454
472
|
new CustomEvent('panelClosed', {
|
455
473
|
detail: {panel: this},
|
@@ -606,12 +624,41 @@ export class SelectPanelElement extends HTMLElement {
|
|
606
624
|
}
|
607
625
|
|
608
626
|
#handleSearchFieldEvent(event: Event) {
|
609
|
-
if (event.type === 'keydown'
|
610
|
-
|
611
|
-
|
612
|
-
|
627
|
+
if (event.type === 'keydown') {
|
628
|
+
const key = (event as KeyboardEvent).key
|
629
|
+
|
630
|
+
if (key === 'Enter') {
|
631
|
+
const item = this.visibleItems[0] as HTMLLIElement | null
|
632
|
+
|
633
|
+
if (item) {
|
634
|
+
const itemContent = this.#getItemContent(item)
|
635
|
+
if (itemContent) itemContent.click()
|
636
|
+
}
|
637
|
+
} else if (key === 'ArrowDown') {
|
638
|
+
const item = (this.focusableItem || this.#getItemContent(this.visibleItems[0])) as HTMLLIElement
|
639
|
+
|
640
|
+
if (item) {
|
641
|
+
item.focus()
|
642
|
+
event.preventDefault()
|
643
|
+
}
|
644
|
+
} else if (key === 'Home') {
|
645
|
+
const item = this.visibleItems[0] as HTMLLIElement | null
|
646
|
+
|
647
|
+
if (item) {
|
648
|
+
const itemContent = this.#getItemContent(item)
|
649
|
+
if (itemContent) itemContent.focus()
|
650
|
+
event.preventDefault()
|
651
|
+
}
|
652
|
+
} else if (key === 'End') {
|
653
|
+
if (this.visibleItems.length > 0) {
|
654
|
+
const item = this.visibleItems[this.visibleItems.length - 1] as HTMLLIElement
|
655
|
+
const itemContent = this.#getItemContent(item)
|
656
|
+
if (itemContent) itemContent.focus()
|
657
|
+
event.preventDefault()
|
658
|
+
}
|
613
659
|
}
|
614
660
|
}
|
661
|
+
|
615
662
|
if (event.type !== 'input') return
|
616
663
|
|
617
664
|
// remote-input-element does not trigger another loadstart event if a request is
|
@@ -660,8 +707,12 @@ export class SelectPanelElement extends HTMLElement {
|
|
660
707
|
if (!itemContent) continue
|
661
708
|
|
662
709
|
const value = itemContent.getAttribute('data-value')
|
663
|
-
|
664
|
-
|
710
|
+
if (this.#hasLoadedData) {
|
711
|
+
if (value && !this.#selectedItems.has(value)) {
|
712
|
+
itemContent.setAttribute(this.ariaSelectionType, 'false')
|
713
|
+
}
|
714
|
+
} else if (value && !this.#selectedItems.has(value) && this.isItemChecked(item)) {
|
715
|
+
this.#hasLoadedData = true
|
665
716
|
this.#addSelectedItem(item)
|
666
717
|
}
|
667
718
|
}
|
@@ -823,19 +874,21 @@ export class SelectPanelElement extends HTMLElement {
|
|
823
874
|
const itemContent = this.#getItemContent(item)
|
824
875
|
|
825
876
|
if (this.selectVariant === 'single') {
|
877
|
+
const value = this.selectedItems[0]?.value
|
878
|
+
const element = this.visibleItems.find(el => this.#getItemContent(el)?.getAttribute('data-value') === value)
|
879
|
+
|
880
|
+
if (element) {
|
881
|
+
this.#getItemContent(element)?.setAttribute(this.ariaSelectionType, 'false')
|
882
|
+
}
|
883
|
+
|
884
|
+
this.#selectedItems.clear()
|
885
|
+
|
826
886
|
// Only check, never uncheck here. Single-select mode does not allow unchecking a checked item.
|
827
887
|
if (checked) {
|
828
888
|
this.#addSelectedItem(item)
|
829
889
|
itemContent?.setAttribute(this.ariaSelectionType, 'true')
|
830
890
|
}
|
831
891
|
|
832
|
-
for (const checkedItem of this.querySelectorAll(`[${this.ariaSelectionType}]`)) {
|
833
|
-
if (checkedItem !== itemContent) {
|
834
|
-
this.#removeSelectedItem(checkedItem)
|
835
|
-
checkedItem.setAttribute(this.ariaSelectionType, 'false')
|
836
|
-
}
|
837
|
-
}
|
838
|
-
|
839
892
|
this.#setDynamicLabel()
|
840
893
|
} else {
|
841
894
|
// multi-select mode allows unchecking a checked item
|
@@ -862,6 +915,7 @@ export class SelectPanelElement extends HTMLElement {
|
|
862
915
|
show() {
|
863
916
|
this.updateAnchorPosition()
|
864
917
|
this.dialog.showModal()
|
918
|
+
this.invokerElement?.setAttribute('aria-expanded', 'true')
|
865
919
|
const event = new CustomEvent('dialog:open', {
|
866
920
|
detail: {dialog: this.dialog},
|
867
921
|
})
|
@@ -952,6 +1006,7 @@ export class SelectPanelElement extends HTMLElement {
|
|
952
1006
|
element => element.parentElement! as SelectPanelItem,
|
953
1007
|
)
|
954
1008
|
}
|
1009
|
+
|
955
1010
|
get focusableItem(): HTMLElement | undefined {
|
956
1011
|
for (const item of this.items) {
|
957
1012
|
const itemContent = this.#getItemContent(item)
|
@@ -2,6 +2,7 @@ declare class ToggleSwitchElement extends HTMLElement {
|
|
2
2
|
switch: HTMLElement;
|
3
3
|
loadingSpinner: HTMLElement;
|
4
4
|
errorIcon: HTMLElement;
|
5
|
+
turbo: boolean;
|
5
6
|
private toggling;
|
6
7
|
get src(): string | null;
|
7
8
|
get csrf(): string | null;
|
@@ -25,6 +26,9 @@ declare class ToggleSwitchElement extends HTMLElement {
|
|
25
26
|
declare global {
|
26
27
|
interface Window {
|
27
28
|
ToggleSwitchElement: typeof ToggleSwitchElement;
|
29
|
+
Turbo: {
|
30
|
+
renderStreamMessage: (message: string) => void;
|
31
|
+
};
|
28
32
|
}
|
29
33
|
}
|
30
34
|
export {};
|
@@ -4,10 +4,11 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
4
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
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
6
6
|
};
|
7
|
-
import { controller, target } from '@github/catalyst';
|
7
|
+
import { controller, target, attr } from '@github/catalyst';
|
8
8
|
let ToggleSwitchElement = class ToggleSwitchElement extends HTMLElement {
|
9
9
|
constructor() {
|
10
10
|
super(...arguments);
|
11
|
+
this.turbo = false;
|
11
12
|
this.toggling = false;
|
12
13
|
}
|
13
14
|
get src() {
|
@@ -129,13 +130,17 @@ let ToggleSwitchElement = class ToggleSwitchElement extends HTMLElement {
|
|
129
130
|
if (!this.src)
|
130
131
|
throw new Error('invalid src');
|
131
132
|
let response;
|
133
|
+
const requestHeaders = {
|
134
|
+
'Requested-With': 'XMLHttpRequest',
|
135
|
+
};
|
136
|
+
if (this.turbo) {
|
137
|
+
requestHeaders['Accept'] = 'text/vnd.turbo-stream.html';
|
138
|
+
}
|
132
139
|
try {
|
133
140
|
response = await fetch(this.src, {
|
134
141
|
credentials: 'same-origin',
|
135
142
|
method: 'POST',
|
136
|
-
headers:
|
137
|
-
'Requested-With': 'XMLHttpRequest',
|
138
|
-
},
|
143
|
+
headers: requestHeaders,
|
139
144
|
body,
|
140
145
|
});
|
141
146
|
}
|
@@ -145,6 +150,10 @@ let ToggleSwitchElement = class ToggleSwitchElement extends HTMLElement {
|
|
145
150
|
if (!response.ok) {
|
146
151
|
throw new Error(await response.text());
|
147
152
|
}
|
153
|
+
const contentType = response.headers.get('Content-Type');
|
154
|
+
if (window.Turbo && this.turbo && contentType?.startsWith('text/vnd.turbo-stream.html')) {
|
155
|
+
window.Turbo.renderStreamMessage(await response.text());
|
156
|
+
}
|
148
157
|
}
|
149
158
|
};
|
150
159
|
__decorate([
|
@@ -156,6 +165,9 @@ __decorate([
|
|
156
165
|
__decorate([
|
157
166
|
target
|
158
167
|
], ToggleSwitchElement.prototype, "errorIcon", void 0);
|
168
|
+
__decorate([
|
169
|
+
attr
|
170
|
+
], ToggleSwitchElement.prototype, "turbo", void 0);
|
159
171
|
ToggleSwitchElement = __decorate([
|
160
172
|
controller
|
161
173
|
], ToggleSwitchElement);
|
@@ -26,12 +26,14 @@ module Primer
|
|
26
26
|
# @param enabled [Boolean] Whether or not the toggle switch responds to user input.
|
27
27
|
# @param size [Symbol] What size toggle switch to render. <%= one_of(Primer::Alpha::ToggleSwitch::SIZE_OPTIONS) %>
|
28
28
|
# @param status_label_position [Symbol] Which side of the toggle switch to render the status label. <%= one_of(Primer::Alpha::ToggleSwitch::STATUS_LABEL_POSITION_OPTIONS) %>
|
29
|
+
# @param turbo [Boolean] Whether or not to request a turbo stream and render the response as such.
|
29
30
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
30
|
-
def initialize(src: nil, csrf_token: nil, checked: false, enabled: true, size: SIZE_DEFAULT, status_label_position: STATUS_LABEL_POSITION_DEFAULT, **system_arguments)
|
31
|
+
def initialize(src: nil, csrf_token: nil, checked: false, enabled: true, size: SIZE_DEFAULT, status_label_position: STATUS_LABEL_POSITION_DEFAULT, turbo: false, **system_arguments)
|
31
32
|
@src = src
|
32
33
|
@csrf_token = csrf_token
|
33
34
|
@checked = checked
|
34
35
|
@enabled = enabled
|
36
|
+
@turbo = turbo
|
35
37
|
@system_arguments = system_arguments
|
36
38
|
|
37
39
|
@size = fetch_or_fallback(SIZE_OPTIONS, size, SIZE_DEFAULT)
|
@@ -82,7 +84,7 @@ module Primer
|
|
82
84
|
|
83
85
|
@system_arguments[:data] = merge_data(
|
84
86
|
@system_arguments,
|
85
|
-
{ data: { csrf: @csrf_token } }
|
87
|
+
{ data: { csrf: @csrf_token, turbo: @turbo } }
|
86
88
|
)
|
87
89
|
end
|
88
90
|
end
|
@@ -1,10 +1,11 @@
|
|
1
|
-
import {controller, target} from '@github/catalyst'
|
1
|
+
import {controller, target, attr} from '@github/catalyst'
|
2
2
|
|
3
3
|
@controller
|
4
4
|
class ToggleSwitchElement extends HTMLElement {
|
5
5
|
@target switch: HTMLElement
|
6
6
|
@target loadingSpinner: HTMLElement
|
7
7
|
@target errorIcon: HTMLElement
|
8
|
+
@attr turbo = false
|
8
9
|
|
9
10
|
private toggling = false
|
10
11
|
|
@@ -158,13 +159,19 @@ class ToggleSwitchElement extends HTMLElement {
|
|
158
159
|
|
159
160
|
let response
|
160
161
|
|
162
|
+
const requestHeaders: {[key: string]: string} = {
|
163
|
+
'Requested-With': 'XMLHttpRequest',
|
164
|
+
}
|
165
|
+
|
166
|
+
if (this.turbo) {
|
167
|
+
requestHeaders['Accept'] = 'text/vnd.turbo-stream.html'
|
168
|
+
}
|
169
|
+
|
161
170
|
try {
|
162
171
|
response = await fetch(this.src, {
|
163
172
|
credentials: 'same-origin',
|
164
173
|
method: 'POST',
|
165
|
-
headers:
|
166
|
-
'Requested-With': 'XMLHttpRequest',
|
167
|
-
},
|
174
|
+
headers: requestHeaders,
|
168
175
|
body,
|
169
176
|
})
|
170
177
|
} catch (error) {
|
@@ -174,12 +181,20 @@ class ToggleSwitchElement extends HTMLElement {
|
|
174
181
|
if (!response.ok) {
|
175
182
|
throw new Error(await response.text())
|
176
183
|
}
|
184
|
+
|
185
|
+
const contentType = response.headers.get('Content-Type')
|
186
|
+
if (window.Turbo && this.turbo && contentType?.startsWith('text/vnd.turbo-stream.html')) {
|
187
|
+
window.Turbo.renderStreamMessage(await response.text())
|
188
|
+
}
|
177
189
|
}
|
178
190
|
}
|
179
191
|
|
180
192
|
declare global {
|
181
193
|
interface Window {
|
182
194
|
ToggleSwitchElement: typeof ToggleSwitchElement
|
195
|
+
Turbo: {
|
196
|
+
renderStreamMessage: (message: string) => void
|
197
|
+
}
|
183
198
|
}
|
184
199
|
}
|
185
200
|
|
@@ -86,7 +86,7 @@ class ToolTipElement extends HTMLElement {
|
|
86
86
|
--tooltip-left: var(--tool-tip-position-left, 0);
|
87
87
|
padding: var(--overlay-paddingBlock-condensed) var(--overlay-padding-condensed) !important;
|
88
88
|
font: var(--text-body-shorthand-small);
|
89
|
-
color: var(--fgColor
|
89
|
+
color: var(--tooltip-fgColor, var(--fgColor-onEmphasis)) !important;
|
90
90
|
text-align: center;
|
91
91
|
text-decoration: none;
|
92
92
|
text-shadow: none;
|
@@ -94,7 +94,7 @@ class ToolTipElement extends HTMLElement {
|
|
94
94
|
letter-spacing: normal;
|
95
95
|
word-wrap: break-word;
|
96
96
|
white-space: pre;
|
97
|
-
background: var(--bgColor
|
97
|
+
background: var(--tooltip-bgColor, var(--bgColor-emphasis)) !important;
|
98
98
|
border-radius: var(--borderRadius-medium);
|
99
99
|
border: 0 !important;
|
100
100
|
opacity: 0;
|
@@ -71,7 +71,7 @@ class ToolTipElement extends HTMLElement {
|
|
71
71
|
--tooltip-left: var(--tool-tip-position-left, 0);
|
72
72
|
padding: var(--overlay-paddingBlock-condensed) var(--overlay-padding-condensed) !important;
|
73
73
|
font: var(--text-body-shorthand-small);
|
74
|
-
color: var(--fgColor
|
74
|
+
color: var(--tooltip-fgColor, var(--fgColor-onEmphasis)) !important;
|
75
75
|
text-align: center;
|
76
76
|
text-decoration: none;
|
77
77
|
text-shadow: none;
|
@@ -79,7 +79,7 @@ class ToolTipElement extends HTMLElement {
|
|
79
79
|
letter-spacing: normal;
|
80
80
|
word-wrap: break-word;
|
81
81
|
white-space: pre;
|
82
|
-
background: var(--bgColor
|
82
|
+
background: var(--tooltip-bgColor, var(--bgColor-emphasis)) !important;
|
83
83
|
border-radius: var(--borderRadius-medium);
|
84
84
|
border: 0 !important;
|
85
85
|
opacity: 0;
|
@@ -86,7 +86,7 @@ module Primer
|
|
86
86
|
|
87
87
|
# @param datetime [Time] The time to be formatted.
|
88
88
|
# @param tense [Symbol] Which tense to use. <%= one_of(Primer::Beta::RelativeTime::TENSE_OPTIONS) %>
|
89
|
-
# @param prefix [
|
89
|
+
# @param prefix [String] What to prefix the relative time display with.
|
90
90
|
# @param second [Symbol] What format seconds should take. <%= one_of(Primer::Beta::RelativeTime::SECOND_OPTIONS) %>
|
91
91
|
# @param minute [Symbol] What format minues should take. <%= one_of(Primer::Beta::RelativeTime::MINUTE_OPTIONS) %>
|
92
92
|
# @param hour [Symbol] What format hours should take. <%= one_of(Primer::Beta::RelativeTime::HOUR_OPTIONS) %>
|
@@ -95,12 +95,12 @@ module Primer
|
|
95
95
|
# @param month [Symbol] What format months should take. <%= one_of(Primer::Beta::RelativeTime::MONTH_OPTIONS) %>
|
96
96
|
# @param year [Symbol] What format years should take. <%= one_of(Primer::Beta::RelativeTime::YEAR_OPTIONS) %>
|
97
97
|
# @param time_zone_name [Symbol] What format the time zone should take. <%= one_of(Primer::Beta::RelativeTime::TIMEZONENAME_OPTIONS) %>
|
98
|
-
# @param threshold [
|
98
|
+
# @param threshold [String] The threshold, in ISO-8601 'durations' format, at which relative time displays become absolute.
|
99
99
|
# @param precision [Symbol] The precision elapsed time should display. <%= one_of(Primer::Beta::RelativeTime::PRECISION_OPTIONS) %>
|
100
100
|
# @param format [Symbol] The format the display should take. <%= one_of(Primer::Beta::RelativeTime::FORMAT_OPTIONS) %>
|
101
101
|
# @param format_style [Symbol] The format the display should take. <%= one_of(Primer::Beta::RelativeTime::FORMAT_STYLE_OPTIONS) %>
|
102
|
-
# @param lang [
|
103
|
-
# @param title [
|
102
|
+
# @param lang [String] The language to use.
|
103
|
+
# @param title [String] Provide a custom title to the element.
|
104
104
|
# @param no_title [Boolean] Removes the `title` attribute provided on the element by default.
|
105
105
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
106
106
|
def initialize(
|
@@ -1 +1 @@
|
|
1
|
-
.State,.state{border-radius:2em;display:inline-block;font-size:var(--text-body-size-medium);font-weight:var(--base-text-weight-medium);line-height:var(--control-medium-lineBoxHeight);padding:5px var(--control-medium-paddingInline-normal);text-align:center;white-space:nowrap}.State,.State--draft,.state{background-color:var(--bgColor-neutral-emphasis);border:var(--borderWidth-thin) solid #0000;color:var(--fgColor-onEmphasis)}.State--open{background-color:var(--bgColor-open-emphasis,var(--color-open-emphasis))}.State--merged,.State--open{color:var(--fgColor-onEmphasis)}.State--merged{background-color:var(--bgColor-done-emphasis,var(--color-done-emphasis))}.State--closed{background-color:var(--bgColor-closed-emphasis,var(--color-closed-emphasis));color:var(--fgColor-onEmphasis)}.State--small{font-size:var(--text-body-size-small);line-height:var(--base-size-24);padding:0 10px}.State--small .octicon{width:1em}
|
1
|
+
.State,.state{border-radius:2em;display:inline-block;font-size:var(--text-body-size-medium);font-weight:var(--base-text-weight-medium);line-height:var(--control-medium-lineBoxHeight);padding:5px var(--control-medium-paddingInline-normal);text-align:center;white-space:nowrap}.State,.State--draft,.state{background-color:var(--bgColor-neutral-emphasis);border:var(--borderWidth-thin) solid #0000;box-shadow:var(--boxShadow-thin) var(--borderColor-neutral-emphasis);color:var(--fgColor-onEmphasis)}.State--open{background-color:var(--bgColor-open-emphasis,var(--color-open-emphasis));box-shadow:var(--boxShadow-thin) var(--borderColor-open-emphasis)}.State--merged,.State--open{color:var(--fgColor-onEmphasis)}.State--merged{background-color:var(--bgColor-done-emphasis,var(--color-done-emphasis));box-shadow:var(--boxShadow-thin) var(--borderColor-done-emphasis)}.State--closed{background-color:var(--bgColor-closed-emphasis,var(--color-closed-emphasis));box-shadow:var(--boxShadow-thin) var(--borderColor-closed-emphasis);color:var(--fgColor-onEmphasis)}.State--small{font-size:var(--text-body-size-small);line-height:var(--base-size-24);padding:0 10px}.State--small .octicon{width:1em}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["state.pcss"],"names":[],"mappings":"AAIA,cASE,iBAAkB,CAPlB,oBAAqB,CAErB,sCAAuC,CACvC,0CAA2C,CAC3C,+CAAgD,CAHhD,sDAAuD,CAIvD,iBAAkB,CAClB,kBAEF,CAEA,4BAIE,gDAAiD,CACjD,0CAAiD,
|
1
|
+
{"version":3,"sources":["state.pcss"],"names":[],"mappings":"AAIA,cASE,iBAAkB,CAPlB,oBAAqB,CAErB,sCAAuC,CACvC,0CAA2C,CAC3C,+CAAgD,CAHhD,sDAAuD,CAIvD,iBAAkB,CAClB,kBAEF,CAEA,4BAIE,gDAAiD,CACjD,0CAAiD,CACjD,oEAAqE,CAHrE,+BAIF,CAEA,aAEE,wEAA0E,CAC1E,iEACF,CAEA,4BALE,+BASF,CAJA,eAEE,wEAA0E,CAC1E,iEACF,CAEA,eAEE,4EAA8E,CAC9E,mEAAoE,CAFpE,+BAGF,CAIA,cAEE,qCAAsC,CACtC,+BAAgC,CAFhC,cAOF,CAHE,uBACE,SACF","file":"state.css","sourcesContent":["/* State */\n\n/* Default 32px */\n\n.state, /* TODO: Deprecate */\n.State {\n display: inline-block;\n padding: 5px var(--control-medium-paddingInline-normal);\n font-size: var(--text-body-size-medium);\n font-weight: var(--base-text-weight-medium);\n line-height: var(--control-medium-lineBoxHeight);\n text-align: center;\n white-space: nowrap;\n border-radius: 2em;\n}\n\n.state, /* TODO: Deprecate */\n.State,\n.State--draft {\n color: var(--fgColor-onEmphasis);\n background-color: var(--bgColor-neutral-emphasis);\n border: var(--borderWidth-thin) solid transparent;\n box-shadow: var(--boxShadow-thin) var(--borderColor-neutral-emphasis);\n}\n\n.State--open {\n color: var(--fgColor-onEmphasis);\n background-color: var(--bgColor-open-emphasis, var(--color-open-emphasis));\n box-shadow: var(--boxShadow-thin) var(--borderColor-open-emphasis);\n}\n\n.State--merged {\n color: var(--fgColor-onEmphasis);\n background-color: var(--bgColor-done-emphasis, var(--color-done-emphasis));\n box-shadow: var(--boxShadow-thin) var(--borderColor-done-emphasis);\n}\n\n.State--closed {\n color: var(--fgColor-onEmphasis);\n background-color: var(--bgColor-closed-emphasis, var(--color-closed-emphasis));\n box-shadow: var(--boxShadow-thin) var(--borderColor-closed-emphasis);\n}\n\n/* Small 24px */\n\n.State--small {\n padding: 0 10px;\n font-size: var(--text-body-size-small);\n line-height: var(--base-size-24);\n\n & .octicon {\n width: 1em; /* Ensures different icons don't change State indicator width */\n }\n}\n"]}
|
@@ -20,21 +20,25 @@
|
|
20
20
|
color: var(--fgColor-onEmphasis);
|
21
21
|
background-color: var(--bgColor-neutral-emphasis);
|
22
22
|
border: var(--borderWidth-thin) solid transparent;
|
23
|
+
box-shadow: var(--boxShadow-thin) var(--borderColor-neutral-emphasis);
|
23
24
|
}
|
24
25
|
|
25
26
|
.State--open {
|
26
27
|
color: var(--fgColor-onEmphasis);
|
27
28
|
background-color: var(--bgColor-open-emphasis, var(--color-open-emphasis));
|
29
|
+
box-shadow: var(--boxShadow-thin) var(--borderColor-open-emphasis);
|
28
30
|
}
|
29
31
|
|
30
32
|
.State--merged {
|
31
33
|
color: var(--fgColor-onEmphasis);
|
32
34
|
background-color: var(--bgColor-done-emphasis, var(--color-done-emphasis));
|
35
|
+
box-shadow: var(--boxShadow-thin) var(--borderColor-done-emphasis);
|
33
36
|
}
|
34
37
|
|
35
38
|
.State--closed {
|
36
39
|
color: var(--fgColor-onEmphasis);
|
37
40
|
background-color: var(--bgColor-closed-emphasis, var(--color-closed-emphasis));
|
41
|
+
box-shadow: var(--boxShadow-thin) var(--borderColor-closed-emphasis);
|
38
42
|
}
|
39
43
|
|
40
44
|
/* Small 24px */
|
@@ -14,6 +14,13 @@ module ERBLint
|
|
14
14
|
" https://primer.style/design/components/action-menu/rails/alpha"
|
15
15
|
DETAILS_MENU_RUBY_PATTERN = /tag:?\s+:"details-menu"/.freeze
|
16
16
|
|
17
|
+
# Allow custom pattern matching for ERB nodes
|
18
|
+
class ConfigSchema < LinterConfig
|
19
|
+
property :custom_erb_pattern, accepts: array_of?(Regexp),
|
20
|
+
default: -> { [] }
|
21
|
+
end
|
22
|
+
self.config_schema = ConfigSchema
|
23
|
+
|
17
24
|
def run(processed_source)
|
18
25
|
# HTML tags
|
19
26
|
tags(processed_source).each do |tag|
|
@@ -25,9 +32,31 @@ module ERBLint
|
|
25
32
|
# ERB nodes
|
26
33
|
erb_nodes(processed_source).each do |node|
|
27
34
|
code = extract_ruby_from_erb_node(node)
|
28
|
-
|
35
|
+
|
36
|
+
if contains_offense?(code)
|
37
|
+
generate_node_offense(self.class, processed_source, node, MIGRATE_FROM_DETAILS_MENU)
|
38
|
+
end
|
29
39
|
end
|
30
40
|
end
|
41
|
+
|
42
|
+
def contains_offense?(code)
|
43
|
+
return true if code.match?(DETAILS_MENU_RUBY_PATTERN)
|
44
|
+
return code.match?(custom_erb_pattern) if custom_erb_pattern
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
def custom_erb_pattern
|
49
|
+
unless defined?(@custom_erb_pattern)
|
50
|
+
@custom_erb_pattern =
|
51
|
+
if @config.custom_erb_pattern.empty?
|
52
|
+
nil
|
53
|
+
else
|
54
|
+
Regexp.new(@config.custom_erb_pattern.join("|"), true)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
@custom_erb_pattern
|
59
|
+
end
|
31
60
|
end
|
32
61
|
end
|
33
62
|
end
|
@@ -10,7 +10,7 @@ module ERBLint
|
|
10
10
|
include LinterRegistry
|
11
11
|
include Helpers::RuleHelpers
|
12
12
|
|
13
|
-
MIGRATE_TO_NEWER_TOOLTIP = ".tooltipped has been deprecated.
|
13
|
+
MIGRATE_TO_NEWER_TOOLTIP = ".tooltipped has been deprecated. Due to major accessibility concerns with using this tooltip, please migrate to a Primer Tooltip component or rework the design to eliminate the tooltip. See https://primer.style/guides/rails/migration-guides/primer-css-tooltipped."
|
14
14
|
TOOLTIPPED_RUBY_PATTERN = /classes:.*tooltipped|class:.*tooltipped/.freeze
|
15
15
|
|
16
16
|
def run(processed_source)
|
@@ -380,7 +380,7 @@ module Primer
|
|
380
380
|
aria: { label: "List heading" }
|
381
381
|
)) do |component|
|
382
382
|
component.with_item(label: "Default item", href: "/") do |item|
|
383
|
-
item.with_description.with_content("This is a description")
|
383
|
+
item.with_description(test_selector: "some-selector").with_content("This is a description")
|
384
384
|
end
|
385
385
|
end
|
386
386
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<% subject_id = SecureRandom.hex %>
|
2
|
+
|
3
|
+
<%= render(Primer::Alpha::SelectPanel.new(
|
4
|
+
data: { interaction_subject: subject_id },
|
5
|
+
select_variant: :single,
|
6
|
+
fetch_strategy: :local,
|
7
|
+
open_on_load: open_on_load
|
8
|
+
)) do |panel| %>
|
9
|
+
<% panel.with_show_button { "Panel" } %>
|
10
|
+
<% panel.with_item(label: "GitHub", href: "https://github.com") %>
|
11
|
+
<% panel.with_item(label: "Microsoft", href: "https://microsoft.com") %>
|
12
|
+
<% panel.with_item(label: "Primer", href: "https://primer.style") %>
|
13
|
+
<% panel.with_item(label: "Catalyst", href: "https://catalyst.rocks") %>
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<%= render partial: "primer/alpha/select_panel_preview/interaction_subject_js", locals: { subject_id: subject_id } %>
|
@@ -18,6 +18,7 @@ module Primer
|
|
18
18
|
# @param open_on_load toggle
|
19
19
|
# @param anchor_align [Symbol] select [start, center, end]
|
20
20
|
# @param anchor_side [Symbol] select [outside_bottom, outside_top, outside_left, outside_right]
|
21
|
+
# @param select_items toggle
|
21
22
|
def playground(
|
22
23
|
title: "Sci-fi equipment",
|
23
24
|
subtitle: "Various tools from your favorite shows",
|
@@ -31,10 +32,12 @@ module Primer
|
|
31
32
|
show_filter: true,
|
32
33
|
open_on_load: false,
|
33
34
|
anchor_align: :start,
|
34
|
-
anchor_side: :outside_bottom
|
35
|
+
anchor_side: :outside_bottom,
|
36
|
+
select_items: true
|
35
37
|
)
|
36
38
|
render_with_template(locals: {
|
37
39
|
subtitle: subtitle,
|
40
|
+
select_items: select_items,
|
38
41
|
system_arguments: {
|
39
42
|
title: title,
|
40
43
|
size: size,
|
@@ -234,6 +237,14 @@ module Primer
|
|
234
237
|
def multiselect_form(open_on_load: false, route_format: :html)
|
235
238
|
render_with_template(locals: { open_on_load: open_on_load, route_format: route_format })
|
236
239
|
end
|
240
|
+
|
241
|
+
# @label List of links
|
242
|
+
#
|
243
|
+
# @snapshot interactive
|
244
|
+
# @param open_on_load toggle
|
245
|
+
def list_of_links(open_on_load: false)
|
246
|
+
render_with_template(locals: { open_on_load: open_on_load })
|
247
|
+
end
|
237
248
|
end
|
238
249
|
end
|
239
250
|
end
|
@@ -58,6 +58,10 @@ module Primer
|
|
58
58
|
def with_bad_csrf_token
|
59
59
|
render(Primer::Alpha::ToggleSwitch.new(src: UrlHelpers.toggle_switch_index_path, csrf_token: "i_am_a_criminal"))
|
60
60
|
end
|
61
|
+
|
62
|
+
def with_turbo
|
63
|
+
render(Primer::Alpha::ToggleSwitch.new(src: UrlHelpers.toggle_switch_index_path, turbo: true))
|
64
|
+
end
|
61
65
|
end
|
62
66
|
end
|
63
67
|
end
|