primer_view_components 0.1.0 → 0.1.1
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 +20 -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/divider.rb +2 -2
- data/app/components/primer/alpha/action_list/heading.html.erb +1 -1
- data/app/components/primer/alpha/action_list/heading.rb +10 -4
- data/app/components/primer/alpha/action_list/item.rb +3 -3
- data/app/components/primer/alpha/action_list.html.erb +6 -8
- data/app/components/primer/alpha/action_list.rb +5 -10
- data/app/components/primer/alpha/nav_list/{section.rb → group.rb} +5 -5
- data/app/components/primer/alpha/nav_list/item.html.erb +1 -1
- data/app/components/primer/alpha/nav_list/item.rb +15 -1
- data/app/components/primer/alpha/nav_list.d.ts +1 -0
- data/app/components/primer/alpha/nav_list.html.erb +8 -8
- data/app/components/primer/alpha/nav_list.js +21 -0
- data/app/components/primer/alpha/nav_list.rb +28 -32
- data/app/components/primer/alpha/nav_list.ts +23 -0
- data/app/components/primer/alpha/navigation/tab.rb +168 -0
- data/app/components/primer/alpha/overlay.rb +19 -6
- data/app/components/primer/alpha/tab_nav.rb +10 -3
- data/app/components/primer/alpha/tab_panels.rb +2 -2
- data/app/components/primer/alpha/underline_nav.css +1 -1
- data/app/components/primer/alpha/underline_nav.css.map +1 -1
- data/app/components/primer/alpha/underline_nav.pcss +1 -0
- data/app/components/primer/alpha/underline_nav.rb +2 -2
- data/app/components/primer/alpha/underline_panels.rb +2 -2
- data/app/components/primer/beta/button.html.erb +1 -1
- data/app/components/primer/beta/button.rb +2 -1
- data/app/components/primer/component.rb +34 -0
- data/app/components/primer/navigation/tab_component.rb +3 -157
- data/lib/primer/deprecations.yml +4 -0
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/primer/yard/component_manifest.rb +2 -1
- data/lib/tasks/docs.rake +1 -1
- data/previews/primer/alpha/action_list_preview.rb +6 -14
- data/previews/primer/alpha/nav_list_preview/trailing_action.html.erb +19 -0
- data/previews/primer/alpha/nav_list_preview.rb +19 -30
- data/previews/primer/alpha/overlay_preview.rb +7 -2
- data/previews/primer/alpha/tab_nav_preview/with_extra.html.erb +8 -0
- data/previews/primer/alpha/tab_nav_preview.rb +5 -0
- data/previews/primer/alpha/tab_panels_preview/with_extra.html.erb +17 -0
- data/previews/primer/alpha/tab_panels_preview.rb +5 -0
- data/static/arguments.json +63 -7
- data/static/audited_at.json +2 -1
- data/static/constants.json +20 -8
- data/static/previews.json +10 -0
- data/static/statuses.json +3 -2
- metadata +8 -6
- data/app/components/primer/alpha/nav_list/section.html.erb +0 -3
- data/previews/primer/alpha/action_list_preview/heading.html.erb +0 -4
- /data/app/components/primer/{navigation/tab_component.html.erb → alpha/navigation/tab.html.erb} +0 -0
@@ -68,14 +68,18 @@ module Primer
|
|
68
68
|
# Optional button to open the Overlay.
|
69
69
|
#
|
70
70
|
# @param system_arguments [Hash] The same arguments as <%= link_to_component(Primer::ButtonComponent) %>.
|
71
|
-
renders_one :show_button, lambda {
|
71
|
+
renders_one :show_button, lambda { |icon: nil, **system_arguments|
|
72
72
|
system_arguments[:classes] = class_names(
|
73
73
|
system_arguments[:classes]
|
74
74
|
)
|
75
|
-
system_arguments[:id] =
|
76
|
-
system_arguments["popovertoggletarget"] =
|
77
|
-
system_arguments[:
|
78
|
-
|
75
|
+
system_arguments[:id] = show_button_id
|
76
|
+
system_arguments["popovertoggletarget"] = overlay_id
|
77
|
+
system_arguments[:aria] = (system_arguments[:aria] || {}).merge({ controls: overlay_id, haspopup: "true" })
|
78
|
+
if icon.present?
|
79
|
+
Primer::Beta::IconButton.new(icon: icon, **system_arguments)
|
80
|
+
else
|
81
|
+
Primer::Beta::Button.new(**system_arguments)
|
82
|
+
end
|
79
83
|
}
|
80
84
|
|
81
85
|
# Header content.
|
@@ -165,7 +169,6 @@ module Primer
|
|
165
169
|
@system_arguments[:classes] = class_names(
|
166
170
|
"Overlay",
|
167
171
|
SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)],
|
168
|
-
"Overlay--motion-scaleFade",
|
169
172
|
system_arguments[:classes]
|
170
173
|
)
|
171
174
|
@system_arguments[:tag] = "anchored-position"
|
@@ -189,6 +192,16 @@ module Primer
|
|
189
192
|
with_header unless header?
|
190
193
|
with_body unless body?
|
191
194
|
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def overlay_id
|
199
|
+
@system_arguments[:id]
|
200
|
+
end
|
201
|
+
|
202
|
+
def show_button_id
|
203
|
+
"overlay-show-#{overlay_id}"
|
204
|
+
end
|
192
205
|
end
|
193
206
|
end
|
194
207
|
end
|
@@ -9,7 +9,7 @@ module Primer
|
|
9
9
|
# - By default, `TabNav` renders links within a `<nav>` element. `<nav>` has an
|
10
10
|
# implicit landmark role of `navigation` which should be reserved for main links.
|
11
11
|
# For all other set of links, set tag to `:div`.
|
12
|
-
# - See <%= link_to_component(Primer::Navigation::
|
12
|
+
# - See <%= link_to_component(Primer::Alpha::Navigation::Tab) %> for additional
|
13
13
|
# accessibility considerations.
|
14
14
|
class TabNav < Primer::Component
|
15
15
|
include Primer::TabbedComponentHelper
|
@@ -22,13 +22,13 @@ module Primer
|
|
22
22
|
TAG_DEFAULT = :nav
|
23
23
|
TAG_OPTIONS = [TAG_DEFAULT, :div].freeze
|
24
24
|
|
25
|
-
# Tabs to be rendered. For more information, refer to <%= link_to_component(Primer::Navigation::
|
25
|
+
# Tabs to be rendered. For more information, refer to <%= link_to_component(Primer::Alpha::Navigation::Tab) %>.
|
26
26
|
#
|
27
27
|
# @param selected [Boolean] Whether the tab is selected.
|
28
28
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
29
29
|
renders_many :tabs, lambda { |selected: false, **system_arguments|
|
30
30
|
system_arguments[:classes] = tab_nav_tab_classes(system_arguments[:classes])
|
31
|
-
Primer::Navigation::
|
31
|
+
Primer::Alpha::Navigation::Tab.new(
|
32
32
|
list: true,
|
33
33
|
selected: selected,
|
34
34
|
**system_arguments
|
@@ -124,6 +124,13 @@ module Primer
|
|
124
124
|
|
125
125
|
aria_label_for_page_nav(label)
|
126
126
|
end
|
127
|
+
|
128
|
+
def before_render
|
129
|
+
# Eagerly evaluate content to avoid https://github.com/primer/view_components/issues/1790
|
130
|
+
content
|
131
|
+
|
132
|
+
super
|
133
|
+
end
|
127
134
|
end
|
128
135
|
end
|
129
136
|
end
|
@@ -14,7 +14,7 @@ module Primer
|
|
14
14
|
TAG_DEFAULT = :nav
|
15
15
|
TAG_OPTIONS = [TAG_DEFAULT, :div].freeze
|
16
16
|
|
17
|
-
# Tabs to be rendered. For more information, refer to <%= link_to_component(Primer::Navigation::
|
17
|
+
# Tabs to be rendered. For more information, refer to <%= link_to_component(Primer::Alpha::Navigation::Tab) %>.
|
18
18
|
#
|
19
19
|
# @param id [String] Unique ID of tab.
|
20
20
|
# @param selected [Boolean] Whether the tab is selected.
|
@@ -23,7 +23,7 @@ module Primer
|
|
23
23
|
system_arguments[:id] = id
|
24
24
|
system_arguments[:classes] = tab_nav_tab_classes(system_arguments[:classes])
|
25
25
|
|
26
|
-
Primer::Navigation::
|
26
|
+
Primer::Alpha::Navigation::Tab.new(
|
27
27
|
selected: selected,
|
28
28
|
with_panel: true,
|
29
29
|
list: true,
|
@@ -1 +1 @@
|
|
1
|
-
.UnderlineNav{-webkit-overflow-scrolling:auto;box-shadow:inset 0 -1px 0 var(--color-border-muted);display:flex;justify-content:space-between;min-height:var(--base-size-48,48px);overflow-x:auto;overflow-y:hidden}.UnderlineNav .Counter{background-color:var(--color-neutral-muted);color:var(--color-fg-default);margin-left:var(--primer-control-medium-gap,8px)}.UnderlineNav .Counter--primary{background-color:var(--color-neutral-emphasis);color:var(--color-fg-on-emphasis)}.UnderlineNav-body{align-items:center;display:flex;gap:var(--primer-control-medium-gap,8px);list-style:none}.UnderlineNav-item{align-items:center;background-color:initial;border:0;border-radius:var(--primer-borderRadius-medium,6px);color:var(--color-fg-default);cursor:pointer;display:flex;font-size:var(--primer-text-body-size-medium,14px);line-height:30px;padding:0 var(--primer-control-medium-paddingInline-condensed,8px);position:relative;text-align:center;white-space:nowrap}.UnderlineNav-item:focus,.UnderlineNav-item:focus-visible,.UnderlineNav-item:hover{border-bottom-color:var(--color-neutral-muted);color:var(--color-fg-default);outline-offset:-2px;text-decoration:none;transition:border-bottom-color .12s ease-out}.UnderlineNav-item [data-content]:before{content:attr(data-content);display:block;font-weight:var(--base-text-weight-semibold,600);height:0;visibility:hidden}.UnderlineNav-item:before{content:"";height:100%;left:50%;min-height:48px;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%);width:100%}@media (pointer:fine){.UnderlineNav-item:hover{background:var(--color-action-list-item-default-hover-bg);color:var(--color-fg-default);text-decoration:none;transition:background .12s ease-out}}.UnderlineNav-item.selected,.UnderlineNav-item[aria-current]:not([aria-current=false]),.UnderlineNav-item[role=tab][aria-selected=true]{border-bottom-color:var(--color-primer-border-active);color:var(--color-fg-default);font-weight:var(--base-text-weight-semibold,600)}.UnderlineNav-item.selected:after,.UnderlineNav-item[aria-current]:not([aria-current=false]):after,.UnderlineNav-item[role=tab][aria-selected=true]:after{background:var(--color-primer-border-active);border-radius:var(--primer-borderRadius-medium,6px);bottom:calc(50% - 25px);content:"";height:2px;position:absolute;right:50%;transform:translate(50%,-50%);width:100
|
1
|
+
.UnderlineNav{-webkit-overflow-scrolling:auto;box-shadow:inset 0 -1px 0 var(--color-border-muted);display:flex;justify-content:space-between;min-height:var(--base-size-48,48px);overflow-x:auto;overflow-y:hidden}.UnderlineNav .Counter{background-color:var(--color-neutral-muted);color:var(--color-fg-default);margin-left:var(--primer-control-medium-gap,8px)}.UnderlineNav .Counter--primary{background-color:var(--color-neutral-emphasis);color:var(--color-fg-on-emphasis)}.UnderlineNav-body{align-items:center;display:flex;gap:var(--primer-control-medium-gap,8px);list-style:none}.UnderlineNav-item{align-items:center;background-color:initial;border:0;border-radius:var(--primer-borderRadius-medium,6px);color:var(--color-fg-default);cursor:pointer;display:flex;font-size:var(--primer-text-body-size-medium,14px);line-height:30px;padding:0 var(--primer-control-medium-paddingInline-condensed,8px);position:relative;text-align:center;white-space:nowrap}.UnderlineNav-item:focus,.UnderlineNav-item:focus-visible,.UnderlineNav-item:hover{border-bottom-color:var(--color-neutral-muted);color:var(--color-fg-default);outline-offset:-2px;text-decoration:none;transition:border-bottom-color .12s ease-out}.UnderlineNav-item [data-content]:before{content:attr(data-content);display:block;font-weight:var(--base-text-weight-semibold,600);height:0;visibility:hidden}.UnderlineNav-item:before{content:"";height:100%;left:50%;min-height:48px;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%);width:100%}@media (pointer:fine){.UnderlineNav-item:hover{background:var(--color-action-list-item-default-hover-bg);color:var(--color-fg-default);text-decoration:none;transition:background .12s ease-out}}.UnderlineNav-item.selected,.UnderlineNav-item[aria-current]:not([aria-current=false]),.UnderlineNav-item[role=tab][aria-selected=true]{border-bottom-color:var(--color-primer-border-active);color:var(--color-fg-default);font-weight:var(--base-text-weight-semibold,600)}.UnderlineNav-item.selected:after,.UnderlineNav-item[aria-current]:not([aria-current=false]):after,.UnderlineNav-item[role=tab][aria-selected=true]:after{background:var(--color-primer-border-active);border-radius:var(--primer-borderRadius-medium,6px);bottom:calc(50% - 25px);content:"";height:2px;position:absolute;right:50%;transform:translate(50%,-50%);width:100%;z-index:1}.UnderlineNav--right{justify-content:flex-end}.UnderlineNav--right .UnderlineNav-actions{flex:1 1 auto}.UnderlineNav-actions{align-self:center}.UnderlineNav--full{display:block}.UnderlineNav--full .UnderlineNav-body{min-height:var(--base-size-48,48px)}.UnderlineNav-octicon{fill:var(--color-fg-muted);color:var(--color-fg-muted);display:inline!important;margin-right:var(--primer-control-medium-gap,8px)}.UnderlineNav-container{display:flex;justify-content:space-between}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["underline_nav.pcss","<no source>"],"names":[],"mappings":"AAEA,cAME,+BAAgC,CADhC,mDAAoD,CAJpD,YAAa,CAMb,6BAA8B,CAL9B,mCAAqC,CACrC,eAAgB,CAChB,iBAeF,CAVE,uBAGE,2CAA4C,CAD5C,6BAA8B,CAD9B,gDAGF,CAEA,gCAEE,8CAA+C,CAD/C,iCAEF,CAGF,mBAEE,kBAAmB,CADnB,YAAa,CAEb,wCAA0C,CAC1C,eACF,CAEA,mBAaE,kBAAmB,CAHnB,wBAA6B,CAC7B,QAAS,CACT,mDAAqD,CANrD,6BAA8B,CAG9B,cAAe,CAPf,YAAa,CAEb,kDAAoD,CACpD,gBAAiB,CAFjB,kEAAoE,CAFpE,iBAAkB,CAMlB,iBAAkB,CAClB,
|
1
|
+
{"version":3,"sources":["underline_nav.pcss","<no source>"],"names":[],"mappings":"AAEA,cAME,+BAAgC,CADhC,mDAAoD,CAJpD,YAAa,CAMb,6BAA8B,CAL9B,mCAAqC,CACrC,eAAgB,CAChB,iBAeF,CAVE,uBAGE,2CAA4C,CAD5C,6BAA8B,CAD9B,gDAGF,CAEA,gCAEE,8CAA+C,CAD/C,iCAEF,CAGF,mBAEE,kBAAmB,CADnB,YAAa,CAEb,wCAA0C,CAC1C,eACF,CAEA,mBAaE,kBAAmB,CAHnB,wBAA6B,CAC7B,QAAS,CACT,mDAAqD,CANrD,6BAA8B,CAG9B,cAAe,CAPf,YAAa,CAEb,kDAAoD,CACpD,gBAAiB,CAFjB,kEAAoE,CAFpE,iBAAkB,CAMlB,iBAAkB,CAClB,kBA8DF,CAvDE,mFAKE,8CAA+C,CAF/C,6BAA8B,CAG9B,mBAAoB,CAFpB,oBAAqB,CAGrB,4CACF,CAGA,yCAKE,0BAA2B,CAJ3B,aAAc,CAEd,gDAAkD,CADlD,QAAS,CAET,iBAEF,CAIE,0BClEJ,WAAA,YAAA,SAAA,gBAAA,kBAAA,QAAA,4CAAA,UDkE8B,CAI5B,sBACE,yBAGE,yDAA0D,CAF1D,6BAA8B,CAC9B,oBAAqB,CAErB,mCACF,CACF,CAEA,wIAKE,qDAAsD,CADtD,6BAA8B,CAD9B,gDAiBF,CAZE,0JAQE,4CAA6C,CAC7C,mDAAqD,CALrD,uBAAwB,CAGxB,UAAW,CADX,UAAW,CALX,iBAAkB,CAElB,SAAU,CAOV,6BAA+B,CAL/B,UAAW,CAHX,SASF,CAIJ,qBACE,wBAKF,CAHE,2CACE,aACF,CAGF,sBACE,iBACF,CAEA,oBACE,aAMF,CAHE,uCACE,mCACF,CAGF,sBAIE,0BAA2B,CAD3B,2BAA4B,CAF5B,wBAA0B,CAC1B,iDAGF,CAEA,wBACE,YAAa,CACb,6BACF","file":"underline_nav.css","sourcesContent":["/* UnderlineNav */\n\n.UnderlineNav {\n display: flex;\n min-height: var(--base-size-48, 48px);\n overflow-x: auto;\n overflow-y: hidden;\n box-shadow: inset 0 -1px 0 var(--color-border-muted);\n -webkit-overflow-scrolling: auto;\n justify-content: space-between;\n\n & .Counter {\n margin-left: var(--primer-control-medium-gap, 8px);\n color: var(--color-fg-default);\n background-color: var(--color-neutral-muted);\n }\n\n & .Counter--primary {\n color: var(--color-fg-on-emphasis);\n background-color: var(--color-neutral-emphasis);\n }\n}\n\n.UnderlineNav-body {\n display: flex;\n align-items: center;\n gap: var(--primer-control-medium-gap, 8px);\n list-style: none;\n}\n\n.UnderlineNav-item {\n position: relative;\n display: flex;\n padding: 0 var(--primer-control-medium-paddingInline-condensed, 8px);\n font-size: var(--primer-text-body-size-medium, 14px);\n line-height: 30px;\n color: var(--color-fg-default);\n text-align: center;\n white-space: nowrap;\n cursor: pointer;\n background-color: transparent;\n border: 0;\n border-radius: var(--primer-borderRadius-medium, 6px);\n align-items: center;\n\n &:hover,\n &:focus,\n &:focus-visible {\n color: var(--color-fg-default);\n text-decoration: none;\n border-bottom-color: var(--color-neutral-muted);\n outline-offset: -2px;\n transition: border-bottom-color 0.12s ease-out;\n }\n\n /* renders a visibly hidden \"copy\" of the label in bold, reserving box space for when label becomes bold on selected */\n & [data-content]::before {\n display: block;\n height: 0;\n font-weight: var(--base-text-weight-semibold, 600);\n visibility: hidden;\n content: attr(data-content);\n }\n\n /* increase touch target area */\n &::before {\n @mixin minTouchTarget 48px;\n }\n\n /* hover state was \"sticking\" on mobile after click */\n @media (pointer: fine) {\n &:hover {\n color: var(--color-fg-default);\n text-decoration: none;\n background: var(--color-action-list-item-default-hover-bg);\n transition: background 0.12s ease-out;\n }\n }\n\n &.selected,\n &[role='tab'][aria-selected='true'],\n &[aria-current]:not([aria-current='false']) {\n font-weight: var(--base-text-weight-semibold, 600);\n color: var(--color-fg-default);\n border-bottom-color: var(--color-primer-border-active);\n\n /* current/selected underline */\n &::after {\n position: absolute;\n z-index: 1; /* raise above full-width flash banner */\n right: 50%;\n bottom: calc(50% - 25px); /* 48px total height / 2 (24px) + 1px */\n width: 100%;\n height: 2px;\n content: '';\n background: var(--color-primer-border-active);\n border-radius: var(--primer-borderRadius-medium, 6px);\n transform: translate(50%, -50%);\n }\n }\n}\n\n.UnderlineNav--right {\n justify-content: flex-end;\n\n & .UnderlineNav-actions {\n flex: 1 1 auto;\n }\n}\n\n.UnderlineNav-actions {\n align-self: center;\n}\n\n.UnderlineNav--full {\n display: block;\n\n /* required for underline to align with additional wrapper element */\n & .UnderlineNav-body {\n min-height: var(--base-size-48, 48px);\n }\n}\n\n.UnderlineNav-octicon {\n display: inline !important;\n margin-right: var(--primer-control-medium-gap, 8px);\n color: var(--color-fg-muted);\n fill: var(--color-fg-muted);\n}\n\n.UnderlineNav-container {\n display: flex;\n justify-content: space-between;\n}\n",null]}
|
@@ -12,7 +12,7 @@ module Primer
|
|
12
12
|
# - By default, `UnderlineNav` renders links within a `<nav>` element. `<nav>` has an
|
13
13
|
# implicit landmark role of `navigation` which should be reserved for main links.
|
14
14
|
# For all other set of links, set tag to `:div`.
|
15
|
-
# - See <%= link_to_component(Primer::Navigation::
|
15
|
+
# - See <%= link_to_component(Primer::Alpha::Navigation::Tab) %> for additional
|
16
16
|
# accessibility considerations.
|
17
17
|
class UnderlineNav < Primer::Component
|
18
18
|
include Primer::TabbedComponentHelper
|
@@ -29,7 +29,7 @@ module Primer
|
|
29
29
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
30
30
|
renders_many :tabs, lambda { |selected: false, **system_arguments|
|
31
31
|
system_arguments[:classes] = underline_nav_tab_classes(system_arguments[:classes])
|
32
|
-
Primer::Navigation::
|
32
|
+
Primer::Alpha::Navigation::Tab.new(
|
33
33
|
list: true,
|
34
34
|
selected: selected,
|
35
35
|
icon_classes: "UnderlineNav-octicon",
|
@@ -6,7 +6,7 @@ module Primer
|
|
6
6
|
class UnderlinePanels < Primer::Component
|
7
7
|
include Primer::TabbedComponentHelper
|
8
8
|
include Primer::UnderlineNavHelper
|
9
|
-
# Use to render a button and an associated panel slot. See the example below or refer to <%= link_to_component(Primer::Navigation::
|
9
|
+
# Use to render a button and an associated panel slot. See the example below or refer to <%= link_to_component(Primer::Alpha::Navigation::Tab) %>.
|
10
10
|
#
|
11
11
|
# @param id [String] Unique ID of tab.
|
12
12
|
# @param selected [Boolean] Whether the tab is selected.
|
@@ -15,7 +15,7 @@ module Primer
|
|
15
15
|
system_arguments[:id] = id
|
16
16
|
system_arguments[:classes] = underline_nav_tab_classes(system_arguments[:classes])
|
17
17
|
|
18
|
-
Primer::Navigation::
|
18
|
+
Primer::Alpha::Navigation::Tab.new(
|
19
19
|
selected: selected,
|
20
20
|
with_panel: true,
|
21
21
|
list: true,
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<%= render Primer::ConditionalWrapper.new(condition: tooltip.present?, tag: :div, classes: "Button-withTooltip") do -%>
|
1
|
+
<%= render Primer::ConditionalWrapper.new(condition: tooltip.present?, tag: :div, display: (:block if @block), classes: "Button-withTooltip") do -%>
|
2
2
|
<%= render Primer::Beta::BaseButton.new(**@system_arguments) do -%>
|
3
3
|
<span class="<%= @align_content_classes %>">
|
4
4
|
<% if leading_visual %>
|
@@ -144,6 +144,7 @@ module Primer
|
|
144
144
|
**system_arguments
|
145
145
|
)
|
146
146
|
@scheme = scheme
|
147
|
+
@block = block
|
147
148
|
|
148
149
|
@system_arguments = system_arguments
|
149
150
|
|
@@ -162,7 +163,7 @@ module Primer
|
|
162
163
|
SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_OPTIONS, scheme, DEFAULT_SCHEME)],
|
163
164
|
SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)],
|
164
165
|
"Button",
|
165
|
-
"Button--fullWidth" => block
|
166
|
+
"Button--fullWidth" => @block
|
166
167
|
)
|
167
168
|
end
|
168
169
|
|
@@ -58,6 +58,40 @@ module Primer
|
|
58
58
|
system_arguments[:"aria-#{val}"] || system_arguments.dig(:aria, val.to_sym)
|
59
59
|
end
|
60
60
|
|
61
|
+
# Merges hashes that contain "aria-*" keys and nested aria: hashes. Removes keys from
|
62
|
+
# each hash and returns them in the new hash.
|
63
|
+
#
|
64
|
+
# Eg. merge_aria({ "aria-disabled": "true" }, { aria: { invalid: "true" } })
|
65
|
+
# => { disabled: "true", invalid: "true" }
|
66
|
+
#
|
67
|
+
# It's designed to be used to normalize and merge aria information from system_arguments
|
68
|
+
# hashes. Consider using this pattern in component initializers:
|
69
|
+
#
|
70
|
+
# @system_arguments[:aria] = merge_aria(
|
71
|
+
# @system_arguments,
|
72
|
+
# { aria: { labelled_by: id } }
|
73
|
+
# )
|
74
|
+
def merge_aria(*hashes)
|
75
|
+
{}.tap do |result|
|
76
|
+
hashes.each do |hash|
|
77
|
+
next unless hash
|
78
|
+
|
79
|
+
result.merge!(hash.delete(:aria) || {})
|
80
|
+
|
81
|
+
hash.delete_if do |key, val|
|
82
|
+
key_s = key.to_s
|
83
|
+
|
84
|
+
if key.start_with?("aria-")
|
85
|
+
result[key_s.sub("aria-", "").to_sym] = val
|
86
|
+
true
|
87
|
+
else
|
88
|
+
false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
61
95
|
def validate_aria_label
|
62
96
|
aria_label = aria("label", @system_arguments)
|
63
97
|
aria_labelledby = aria("labelledby", @system_arguments)
|
@@ -2,163 +2,9 @@
|
|
2
2
|
|
3
3
|
module Primer
|
4
4
|
module Navigation
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
# @accessibility
|
9
|
-
# `TabComponent` renders the selected anchor tab with `aria-current="page"` by default.
|
10
|
-
# When the selected tab does not correspond to the current page, such as in a nested inner tab, make sure to use aria-current="true"
|
11
|
-
class TabComponent < Primer::Component
|
12
|
-
DEFAULT_ARIA_CURRENT_FOR_ANCHOR = :page
|
13
|
-
ARIA_CURRENT_OPTIONS_FOR_ANCHOR = [true, DEFAULT_ARIA_CURRENT_FOR_ANCHOR].freeze
|
14
|
-
# Panel controlled by the Tab. This will not render anything in the tab itself.
|
15
|
-
# It will provide a accessor for the Tab's parent to call and render the panel
|
16
|
-
# content in the appropriate place.
|
17
|
-
# Refer to `UnderlineNav` and `TabNav` implementations for examples.
|
18
|
-
#
|
19
|
-
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
20
|
-
renders_one :panel, lambda { |**system_arguments|
|
21
|
-
return unless @with_panel
|
22
|
-
|
23
|
-
deny_tag_argument(**system_arguments)
|
24
|
-
system_arguments[:id] = @panel_id
|
25
|
-
system_arguments[:tag] = :div
|
26
|
-
system_arguments[:role] ||= :tabpanel
|
27
|
-
system_arguments[:tabindex] = 0
|
28
|
-
system_arguments[:hidden] = true unless @selected
|
29
|
-
|
30
|
-
label_present = aria("label", system_arguments) || aria("labelledby", system_arguments)
|
31
|
-
unless label_present
|
32
|
-
if @id.present?
|
33
|
-
system_arguments[:"aria-labelledby"] = @id
|
34
|
-
elsif !Rails.env.production?
|
35
|
-
raise ArgumentError, "Panels must be labelled. Either set a unique `id` on the tab, or set an `aria-label` directly on the panel"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
Primer::BaseComponent.new(**system_arguments)
|
40
|
-
}
|
41
|
-
|
42
|
-
# Icon to be rendered in the Tab left.
|
43
|
-
#
|
44
|
-
# @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::Beta::Octicon) %>.
|
45
|
-
renders_one :icon, lambda { |icon = nil, **system_arguments|
|
46
|
-
system_arguments[:classes] = class_names(
|
47
|
-
@icon_classes,
|
48
|
-
system_arguments[:classes]
|
49
|
-
)
|
50
|
-
Primer::Beta::Octicon.new(icon, **system_arguments)
|
51
|
-
}
|
52
|
-
|
53
|
-
# The Tab's text.
|
54
|
-
#
|
55
|
-
# @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::Beta::Text) %>.
|
56
|
-
renders_one :text, Primer::Beta::Text
|
57
|
-
|
58
|
-
# Counter to be rendered in the Tab right.
|
59
|
-
#
|
60
|
-
# @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::Beta::Counter) %>.
|
61
|
-
renders_one :counter, Primer::Beta::Counter
|
62
|
-
|
63
|
-
attr_reader :selected
|
64
|
-
|
65
|
-
# @example Default
|
66
|
-
# <%= render(Primer::Navigation::TabComponent.new(selected: true)) do |component| %>
|
67
|
-
# <% component.with_text { "Selected" } %>
|
68
|
-
# <% end %>
|
69
|
-
# <%= render(Primer::Navigation::TabComponent.new) do |component| %>
|
70
|
-
# <% component.with_text { "Not selected" } %>
|
71
|
-
# <% end %>
|
72
|
-
#
|
73
|
-
# @example With icons and counters
|
74
|
-
# <%= render(Primer::Navigation::TabComponent.new) do |component| %>
|
75
|
-
# <% component.with_icon(:star) %>
|
76
|
-
# <% component.with_text { "Tab" } %>
|
77
|
-
# <% end %>
|
78
|
-
# <%= render(Primer::Navigation::TabComponent.new) do |component| %>
|
79
|
-
# <% component.with_icon(:star) %>
|
80
|
-
# <% component.with_text { "Tab" } %>
|
81
|
-
# <% component.with_counter(count: 10) %>
|
82
|
-
# <% end %>
|
83
|
-
# <%= render(Primer::Navigation::TabComponent.new) do |component| %>
|
84
|
-
# <% component.with_text { "Tab" } %>
|
85
|
-
# <% component.with_counter(count: 10) %>
|
86
|
-
# <% end %>
|
87
|
-
#
|
88
|
-
# @example Inside a list
|
89
|
-
# <%= render(Primer::Navigation::TabComponent.new(list: true)) do |component| %>
|
90
|
-
# <% component.with_text { "Tab" } %>
|
91
|
-
# <% end %>
|
92
|
-
#
|
93
|
-
# @example With custom HTML
|
94
|
-
# <%= render(Primer::Navigation::TabComponent.new) do %>
|
95
|
-
# <div>
|
96
|
-
# This is my <strong>custom HTML</strong>
|
97
|
-
# </div>
|
98
|
-
# <% end %>
|
99
|
-
#
|
100
|
-
# @param list [Boolean] Whether the Tab is an item in a `<ul>` list.
|
101
|
-
# @param selected [Boolean] Whether the Tab is selected or not.
|
102
|
-
# @param with_panel [Boolean] Whether the Tab has an associated panel.
|
103
|
-
# @param panel_id [String] Only applies if `with_panel` is `true`. Unique id of panel.
|
104
|
-
# @param icon_classes [Boolean] Classes that must always be applied to icons.
|
105
|
-
# @param wrapper_arguments [Hash] <%= link_to_system_arguments_docs %> to be used in the `<li>` wrapper when the tab is an item in a list.
|
106
|
-
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
107
|
-
def initialize(list: false, selected: false, with_panel: false, panel_id: "", icon_classes: "", wrapper_arguments: {}, **system_arguments)
|
108
|
-
@selected = selected
|
109
|
-
@icon_classes = icon_classes
|
110
|
-
@list = list
|
111
|
-
@with_panel = with_panel
|
112
|
-
|
113
|
-
@system_arguments = system_arguments
|
114
|
-
@id = @system_arguments[:id]
|
115
|
-
@wrapper_arguments = wrapper_arguments
|
116
|
-
|
117
|
-
if with_panel || @system_arguments[:tag] == :button
|
118
|
-
@system_arguments[:tag] = :button
|
119
|
-
@system_arguments[:type] = :button
|
120
|
-
@system_arguments[:role] = :tab
|
121
|
-
panel_id(panel_id)
|
122
|
-
# https://www.w3.org/TR/wai-aria-practices/#presentation_role
|
123
|
-
@wrapper_arguments[:role] = :presentation
|
124
|
-
else
|
125
|
-
@system_arguments[:tag] = :a
|
126
|
-
end
|
127
|
-
|
128
|
-
@wrapper_arguments[:tag] = :li
|
129
|
-
@wrapper_arguments[:display] ||= :inline_flex
|
130
|
-
|
131
|
-
return unless @selected
|
132
|
-
|
133
|
-
if @system_arguments[:tag] == :a
|
134
|
-
aria_current = aria("current", system_arguments) || DEFAULT_ARIA_CURRENT_FOR_ANCHOR
|
135
|
-
@system_arguments[:"aria-current"] = fetch_or_fallback(ARIA_CURRENT_OPTIONS_FOR_ANCHOR, aria_current, DEFAULT_ARIA_CURRENT_FOR_ANCHOR)
|
136
|
-
else
|
137
|
-
@system_arguments[:"aria-selected"] = true
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def wrapper
|
142
|
-
unless @list
|
143
|
-
yield
|
144
|
-
return # returning `yield` caused a double render
|
145
|
-
end
|
146
|
-
|
147
|
-
render(Primer::BaseComponent.new(**@wrapper_arguments)) do
|
148
|
-
yield if block_given?
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
private
|
153
|
-
|
154
|
-
def panel_id(panel_id)
|
155
|
-
if panel_id.blank?
|
156
|
-
raise ArgumentError, "`panel_id` is required" unless Rails.env.production?
|
157
|
-
else
|
158
|
-
@panel_id = panel_id
|
159
|
-
@system_arguments[:"aria-controls"] = @panel_id
|
160
|
-
end
|
161
|
-
end
|
5
|
+
# nodoc
|
6
|
+
class TabComponent < Primer::Alpha::Navigation::Tab
|
7
|
+
status :deprecated
|
162
8
|
end
|
163
9
|
end
|
164
10
|
end
|
data/lib/primer/deprecations.yml
CHANGED
@@ -26,6 +26,10 @@ deprecations:
|
|
26
26
|
autocorrect: true
|
27
27
|
replacement: "Primer::Beta::IconButton"
|
28
28
|
|
29
|
+
- component: "Primer::Navigation::TabComponent"
|
30
|
+
autocorrect: true
|
31
|
+
replacement: "Primer::Alpha::Navigation::Tab"
|
32
|
+
|
29
33
|
- component: "Primer::Tooltip"
|
30
34
|
autocorrect: true
|
31
35
|
replacement: "Primer::Alpha::Tooltip"
|
@@ -45,6 +45,7 @@ module Primer
|
|
45
45
|
Primer::Beta::Markdown => {},
|
46
46
|
Primer::Alpha::Menu => {},
|
47
47
|
Primer::Navigation::TabComponent => {},
|
48
|
+
Primer::Alpha::Navigation::Tab => {},
|
48
49
|
Primer::Beta::Octicon => {},
|
49
50
|
Primer::Beta::Popover => {},
|
50
51
|
Primer::Beta::ProgressBar => {},
|
@@ -68,7 +69,7 @@ module Primer
|
|
68
69
|
# Examples can be seen in the NavList docs
|
69
70
|
Primer::Alpha::NavList => { js: true },
|
70
71
|
Primer::Alpha::NavList::Item => { js: true, examples: false },
|
71
|
-
Primer::Alpha::NavList::
|
72
|
+
Primer::Alpha::NavList::Group => { js: true, examples: false },
|
72
73
|
|
73
74
|
# ActionList is a base component that should not be used by itself, and thus
|
74
75
|
# does not have examples of its own
|
data/lib/tasks/docs.rake
CHANGED
@@ -64,7 +64,7 @@ namespace :docs do
|
|
64
64
|
|
65
65
|
if components_needing_docs.any?
|
66
66
|
puts
|
67
|
-
puts "The following components
|
67
|
+
puts "The following components need docs. Care to contribute them? #{components_needing_docs.map(&:name).join(', ')}"
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
@@ -17,8 +17,7 @@ module Primer
|
|
17
17
|
render(Primer::Alpha::ActionList.new(
|
18
18
|
role: role,
|
19
19
|
scheme: scheme,
|
20
|
-
show_dividers: show_dividers
|
21
|
-
aria: { label: "Action List" }
|
20
|
+
show_dividers: show_dividers
|
22
21
|
)) do |component|
|
23
22
|
component.with_heading(title: "Action List")
|
24
23
|
component.with_item(label: "Item one", href: "/") do |item|
|
@@ -46,8 +45,7 @@ module Primer
|
|
46
45
|
render(Primer::Alpha::ActionList.new(
|
47
46
|
role: role,
|
48
47
|
scheme: scheme,
|
49
|
-
show_dividers: show_dividers
|
50
|
-
aria: { label: "Action List" }
|
48
|
+
show_dividers: show_dividers
|
51
49
|
)) do |component|
|
52
50
|
component.with_heading(title: "Action List")
|
53
51
|
component.with_item(label: "Item one", href: "/") do |item|
|
@@ -75,8 +73,7 @@ module Primer
|
|
75
73
|
render(Primer::Alpha::ActionList.new(
|
76
74
|
role: role,
|
77
75
|
scheme: scheme,
|
78
|
-
show_dividers: show_dividers
|
79
|
-
aria: { label: "Action List" }
|
76
|
+
show_dividers: show_dividers
|
80
77
|
)) do |component|
|
81
78
|
component.with_heading(title: "Action List")
|
82
79
|
component.with_item(label: "Leading SVG visual", href: "/") do |item|
|
@@ -113,14 +110,9 @@ module Primer
|
|
113
110
|
list_id: "unique-id",
|
114
111
|
subtitle: "This is a subtitle"
|
115
112
|
)
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
title: title,
|
120
|
-
list_id: list_id,
|
121
|
-
subtitle: subtitle
|
122
|
-
}
|
123
|
-
)
|
113
|
+
render(Primer::Alpha::ActionList::Heading.new(
|
114
|
+
scheme: scheme, list_id: list_id, title: title, subtitle: subtitle
|
115
|
+
))
|
124
116
|
end
|
125
117
|
|
126
118
|
# @label Item [playground]
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<%= render(Primer::Alpha::NavList.new) do |list| %>
|
2
|
+
<% list.with_group do |group| %>
|
3
|
+
<%= group.with_heading(title: "Shopping list") %>
|
4
|
+
<% group.with_item(label: "Bread", href: "/list/1") do |item| %>
|
5
|
+
<%= item.with_trailing_action(show_on_hover: true, icon: :plus, aria: { label: "Activate alert" }, name: "bread_button") %>
|
6
|
+
<% end %>
|
7
|
+
<% group.with_item(label: "Cheese", href: "/list/2") do |item| %>
|
8
|
+
<%= item.with_trailing_action(icon: :plus, aria: { label: "Activate alert" }, name: "cheese_button") %>
|
9
|
+
<% end %>
|
10
|
+
<% end %>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<script type="text/javascript" data-eval="true">
|
14
|
+
const breadButton = document.querySelector("[name=bread_button]")
|
15
|
+
breadButton.addEventListener("click", () => alert("You selected bread."))
|
16
|
+
|
17
|
+
const cheeseButton = document.querySelector("[name=cheese_button]")
|
18
|
+
cheeseButton.addEventListener("click", () => alert("You selected cheese."))
|
19
|
+
</script>
|
@@ -7,22 +7,22 @@ module Primer
|
|
7
7
|
# @label Playground
|
8
8
|
def playground
|
9
9
|
render(Primer::Alpha::NavList.new(selected_item_id: :code_review_limits)) do |list|
|
10
|
-
list.
|
11
|
-
|
10
|
+
list.with_group do |group|
|
11
|
+
group.with_heading(title: "Repository settings")
|
12
12
|
|
13
|
-
|
13
|
+
group.with_item(label: "General", href: "/general") do |item|
|
14
14
|
item.with_leading_visual_icon(icon: :gear)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
list.
|
19
|
-
|
18
|
+
list.with_group do |group|
|
19
|
+
group.with_heading(title: "Access")
|
20
20
|
|
21
|
-
|
21
|
+
group.with_item(label: "Collaborators and teams", href: "/collaborators", selected_by_ids: :collaborators) do |item|
|
22
22
|
item.with_leading_visual_icon(icon: :people)
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
group.with_item(label: "Moderation options") do |item|
|
26
26
|
item.with_leading_visual_icon(icon: :"comment-discussion")
|
27
27
|
|
28
28
|
item.with_item(label: "Interaction limits", href: "/interaction-limits", selected_by_ids: :interaction_limits)
|
@@ -36,22 +36,22 @@ module Primer
|
|
36
36
|
# @label Default
|
37
37
|
def default
|
38
38
|
render(Primer::Alpha::NavList.new(selected_item_id: :code_review_limits)) do |list|
|
39
|
-
list.
|
40
|
-
|
39
|
+
list.with_group do |group|
|
40
|
+
group.with_heading(title: "Repository settings")
|
41
41
|
|
42
|
-
|
42
|
+
group.with_item(label: "General", href: "/general") do |item|
|
43
43
|
item.with_leading_visual_icon(icon: :gear)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
list.
|
48
|
-
|
47
|
+
list.with_group do |group|
|
48
|
+
group.with_heading(title: "Access")
|
49
49
|
|
50
|
-
|
50
|
+
group.with_item(label: "Collaborators and teams", href: "/collaborators", selected_by_ids: :collaborators) do |item|
|
51
51
|
item.with_leading_visual_icon(icon: :people)
|
52
52
|
end
|
53
53
|
|
54
|
-
|
54
|
+
group.with_item(label: "Moderation options") do |item|
|
55
55
|
item.with_leading_visual_icon(icon: :"comment-discussion")
|
56
56
|
|
57
57
|
item.with_item(label: "Interaction limits", href: "/interaction-limits", selected_by_ids: :interaction_limits)
|
@@ -65,28 +65,17 @@ module Primer
|
|
65
65
|
# @label Show more item
|
66
66
|
def show_more_item
|
67
67
|
render(Primer::Alpha::NavList.new) do |list|
|
68
|
-
list.
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
68
|
+
list.with_group do |group|
|
69
|
+
group.with_heading(title: "My favorite foods")
|
70
|
+
group.with_item(label: "Popplers", href: "/foods/popplers")
|
71
|
+
group.with_item(label: "Slurm", href: "/foods/slurm")
|
72
|
+
group.with_show_more_item(label: "Show more", src: "/nav_list_items", pages: 2)
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
77
|
# @label Trailing action
|
78
78
|
def trailing_action
|
79
|
-
render(Primer::Alpha::NavList.new) do |list|
|
80
|
-
list.with_section(aria: { label: "List of items to buy" }) do |section|
|
81
|
-
section.with_heading(title: "Shopping list")
|
82
|
-
section.with_item(label: "Bread", href: "/list/1") do |item|
|
83
|
-
item.with_trailing_action(show_on_hover: true, icon: :plus, aria: { label: "Button tooltip" })
|
84
|
-
end
|
85
|
-
section.with_item(label: "Cheese", href: "/list/2") do |item|
|
86
|
-
item.with_trailing_action(icon: :plus, aria: { label: "Button tooltip" })
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
79
|
end
|
91
80
|
end
|
92
81
|
end
|