openproject-primer_view_components 0.19.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +54 -0
  3. data/app/assets/javascripts/app/components/primer/alpha/action_bar_element.d.ts +3 -2
  4. data/app/assets/javascripts/app/components/primer/dialog_helper.d.ts +15 -0
  5. data/app/assets/javascripts/app/components/primer/primer.d.ts +2 -0
  6. data/app/assets/javascripts/app/components/primer/scrollable_region.d.ts +13 -0
  7. data/app/assets/javascripts/primer_view_components.js +1 -1
  8. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  9. data/app/assets/styles/primer_view_components.css +1 -1
  10. data/app/assets/styles/primer_view_components.css.map +1 -1
  11. data/app/components/primer/alpha/action_bar.css +1 -1
  12. data/app/components/primer/alpha/action_bar.css.map +1 -1
  13. data/app/components/primer/alpha/action_bar.pcss +8 -6
  14. data/app/components/primer/alpha/action_bar_element.d.ts +3 -2
  15. data/app/components/primer/alpha/action_bar_element.js +80 -97
  16. data/app/components/primer/alpha/action_bar_element.ts +84 -89
  17. data/app/components/primer/alpha/action_list.css +1 -1
  18. data/app/components/primer/alpha/action_list.css.map +1 -1
  19. data/app/components/primer/alpha/action_menu/action_menu_element.js +15 -4
  20. data/app/components/primer/alpha/action_menu/action_menu_element.ts +13 -1
  21. data/app/components/primer/alpha/action_menu.rb +3 -1
  22. data/app/components/primer/alpha/banner.html.erb +1 -1
  23. data/app/components/primer/alpha/banner.rb +11 -3
  24. data/app/components/primer/alpha/dialog.css +1 -1
  25. data/app/components/primer/alpha/dialog.css.json +12 -27
  26. data/app/components/primer/alpha/dialog.css.map +1 -1
  27. data/app/components/primer/alpha/dialog.html.erb +5 -3
  28. data/app/components/primer/alpha/dialog.pcss +78 -143
  29. data/app/components/primer/alpha/dialog.rb +15 -14
  30. data/app/components/primer/alpha/layout.css +1 -1
  31. data/app/components/primer/alpha/layout.css.map +1 -1
  32. data/app/components/primer/alpha/modal_dialog.ts +1 -1
  33. data/app/components/primer/alpha/overlay.css +1 -1
  34. data/app/components/primer/alpha/overlay.css.json +3 -1
  35. data/app/components/primer/alpha/overlay.css.map +1 -1
  36. data/app/components/primer/alpha/overlay.pcss +7 -1
  37. data/app/components/primer/alpha/text_field.css +1 -1
  38. data/app/components/primer/alpha/text_field.css.map +1 -1
  39. data/app/components/primer/alpha/text_field.pcss +4 -3
  40. data/app/components/primer/alpha/tool_tip.js +14 -1
  41. data/app/components/primer/alpha/tool_tip.ts +15 -1
  42. data/app/components/primer/alpha/underline_nav.css +1 -1
  43. data/app/components/primer/alpha/underline_nav.css.map +1 -1
  44. data/app/components/primer/base_component.rb +20 -18
  45. data/app/components/primer/beta/button.css +1 -1
  46. data/app/components/primer/beta/button.css.map +1 -1
  47. data/app/components/primer/dialog_helper.d.ts +15 -0
  48. data/app/components/primer/dialog_helper.js +85 -0
  49. data/app/components/primer/dialog_helper.ts +88 -0
  50. data/app/components/primer/open_project/page_header.rb +3 -3
  51. data/app/components/primer/primer.d.ts +2 -0
  52. data/app/components/primer/primer.js +2 -0
  53. data/app/components/primer/primer.ts +2 -0
  54. data/app/components/primer/scrollable_region.d.ts +13 -0
  55. data/app/components/primer/scrollable_region.js +52 -0
  56. data/app/components/primer/scrollable_region.ts +48 -0
  57. data/lib/primer/classify/utilities.rb +3 -4
  58. data/lib/primer/deprecations.yml +3 -3
  59. data/lib/primer/forms/dsl/button_input.rb +4 -0
  60. data/lib/primer/forms/dsl/check_box_input.rb +6 -0
  61. data/lib/primer/forms/dsl/hidden_input.rb +4 -0
  62. data/lib/primer/forms/dsl/input.rb +7 -3
  63. data/lib/primer/forms/dsl/radio_button_input.rb +6 -0
  64. data/lib/primer/forms/dsl/select_input.rb +3 -1
  65. data/lib/primer/forms/dsl/submit_button_input.rb +4 -0
  66. data/lib/primer/forms/form_control.html.erb +3 -1
  67. data/lib/primer/view_components/linters/tooltipped_migration.rb +1 -1
  68. data/lib/primer/view_components/version.rb +1 -1
  69. data/lib/primer/yard/docs_helper.rb +1 -1
  70. data/previews/primer/alpha/action_menu_preview/in_scroll_container.html.erb +11 -0
  71. data/previews/primer/alpha/action_menu_preview.rb +7 -0
  72. data/previews/primer/alpha/banner_preview.rb +3 -2
  73. data/previews/primer/alpha/dialog_preview/nested_dialog.html.erb +1 -1
  74. data/previews/primer/alpha/dialog_preview/scroll_container.html.erb +35 -0
  75. data/previews/primer/alpha/dialog_preview.rb +61 -1
  76. data/previews/primer/alpha/tooltip_preview/tooltip_with_dialog_moving_focus_to_input.html.erb +2 -3
  77. data/previews/primer/alpha/tooltip_preview.rb +1 -1
  78. data/previews/primer/beta/button_preview/trailing_visual.html.erb +2 -1
  79. data/previews/primer/beta/button_preview.rb +4 -2
  80. data/previews/primer/open_project/page_header_preview.rb +4 -4
  81. data/static/arguments.json +12 -0
  82. data/static/classes.json +5 -20
  83. data/static/constants.json +17 -11
  84. data/static/info_arch.json +67 -3
  85. data/static/previews.json +54 -2
  86. metadata +13 -3
@@ -128,10 +128,12 @@ module Primer
128
128
  # @param subtitle [String] text
129
129
  # @param button_text [String] text
130
130
  # @param show_divider [Boolean] toggle
131
- def nested_dialog(title: "Test Dialog", subtitle: nil, button_text: "Show Dialog", show_divider: true)
131
+ # @param position [Symbol] select [center, left, right]
132
+ def nested_dialog(title: "Test Dialog", subtitle: nil, position: :center, button_text: "Show Dialog", show_divider: true)
132
133
  render_with_template(locals: {
133
134
  title: title,
134
135
  subtitle: subtitle,
136
+ position: position,
135
137
  button_text: button_text,
136
138
  show_divider: show_divider
137
139
  })
@@ -188,6 +190,64 @@ module Primer
188
190
  def autofocus_element
189
191
  render_with_template(locals: {})
190
192
  end
193
+
194
+ # @label Left Side
195
+ #
196
+ # @param title [String] text
197
+ # @param subtitle [String] text
198
+ # @param size [Symbol] select [small, medium, medium_portrait, large, xlarge]
199
+ # @param position_narrow [Symbol] select [inherit, bottom, fullscreen, left, right]
200
+ # @param visually_hide_title [Boolean] toggle
201
+ # @param button_text [String] text
202
+ # @param body_text [String] text
203
+ # @snapshot interactive
204
+ def left_side(title: "Test Dialog", subtitle: nil, size: :medium, button_text: "Show Dialog", body_text: "Content", position: :center, position_narrow: :fullscreen, visually_hide_title: false)
205
+ render(Primer::Alpha::Dialog.new(title: title, subtitle: subtitle, size: size, position: :left, position_narrow: position_narrow, visually_hide_title: visually_hide_title)) do |d|
206
+ d.with_show_button { button_text }
207
+ d.with_body { body_text }
208
+ end
209
+ end
210
+
211
+ # @label Right Side
212
+ #
213
+ # @param title [String] text
214
+ # @param subtitle [String] text
215
+ # @param size [Symbol] select [small, medium, medium_portrait, large, xlarge]
216
+ # @param position_narrow [Symbol] select [inherit, bottom, fullscreen, left, right]
217
+ # @param visually_hide_title [Boolean] toggle
218
+ # @param button_text [String] text
219
+ # @param body_text [String] text
220
+ # @snapshot interactive
221
+ def right_side(title: "Test Dialog", subtitle: nil, size: :medium, button_text: "Show Dialog", body_text: "Content", position: :center, position_narrow: :fullscreen, visually_hide_title: false)
222
+ render(Primer::Alpha::Dialog.new(title: title, subtitle: subtitle, size: size, position: :right, position_narrow: position_narrow, visually_hide_title: visually_hide_title)) do |d|
223
+ d.with_show_button { button_text }
224
+ d.with_body { body_text }
225
+ end
226
+ end
227
+
228
+ # @label Scroll container
229
+ #
230
+ # @param title [String] text
231
+ # @param subtitle [String] text
232
+ # @param size [Symbol] select [small, medium, medium_portrait, large, xlarge]
233
+ # @param position [Symbol] select [center, right, left]
234
+ # @param position_narrow [Symbol] select [inherit, bottom, fullscreen, left, right]
235
+ # @param visually_hide_title [Boolean] toggle
236
+ # @param button_text [String] text
237
+ # @param body_text [String] text
238
+ # @snapshot interactive
239
+ def scroll_container(title: "Test Dialog", subtitle: nil, position: :center, size: :medium, button_text: "Show Dialog", body_text: "Content", position_narrow: :fullscreen, visually_hide_title: false)
240
+ render_with_template(locals: {
241
+ title: title,
242
+ subtitle: subtitle,
243
+ position: position,
244
+ size: size,
245
+ button_text: button_text,
246
+ body_text: body_text,
247
+ position_narrow: position_narrow,
248
+ visually_hide_title: visually_hide_title
249
+ })
250
+ end
191
251
  end
192
252
  end
193
253
  end
@@ -16,8 +16,7 @@
16
16
  <input type="text" id="input">
17
17
  </label>
18
18
  <script>
19
- document.querySelector('#my-dialog').addEventListener('cancel', function () {
20
- console.log('!!')
21
- setTimeout(() => document.querySelector('#input').focus(), 0);
19
+ document.querySelector('#my-dialog').addEventListener('close', function () {
20
+ document.querySelector('#input').focus();
22
21
  });
23
22
  </script>
@@ -72,7 +72,7 @@ module Primer
72
72
  end
73
73
 
74
74
  # @label Tooltip with Primer::IconButton
75
- def tooltip_with_icon_button(direction: :s, tooltip_text: "You can press a button")
75
+ def tooltip_with_icon_button(direction: :s, tooltip_text: "Search")
76
76
  render(Primer::Beta::IconButton.new(icon: :search, "aria-label": tooltip_text, tooltip_direction: direction))
77
77
  end
78
78
  # @!endgroup
@@ -4,7 +4,8 @@
4
4
  block: block,
5
5
  id: id,
6
6
  align_content: align_content,
7
- tag: tag
7
+ tag: tag,
8
+ href: '#'
8
9
  )) do |component| %>
9
10
  <% component.with_trailing_visual_counter(count: "15") %>
10
11
  Comment
@@ -206,7 +206,8 @@ module Primer
206
206
  block: false,
207
207
  id: "button-preview",
208
208
  align_content: :center,
209
- tag: :a
209
+ tag: :a,
210
+ href: "#"
210
211
  )
211
212
  render(Primer::Beta::Button.new(
212
213
  scheme: scheme,
@@ -214,7 +215,8 @@ module Primer
214
215
  block: block,
215
216
  id: id,
216
217
  align_content: align_content,
217
- tag: tag
218
+ tag: tag,
219
+ href: href
218
220
  )) do |_c|
219
221
  "Button"
220
222
  end
@@ -25,7 +25,7 @@ module Primer
25
25
  # @param with_context_bar_actions [Boolean]
26
26
  # @param with_parent_link [Boolean]
27
27
  def playground(
28
- variant: :medium,
28
+ variant: :large,
29
29
  title: "Hello",
30
30
  description: "Last updated 5 minutes ago by XYZ.",
31
31
  with_back_button: false,
@@ -49,10 +49,10 @@ module Primer
49
49
  breadcrumb_items: breadcrumb_items })
50
50
  end
51
51
 
52
- # @label Large
53
- def large_title
52
+ # @label Medium title
53
+ def medium_title
54
54
  render(Primer::OpenProject::PageHeader.new) do |header|
55
- header.with_title(variant: :large) { "Hello" }
55
+ header.with_title(variant: :medium) { "Hello" }
56
56
  header.with_description { "Last updated 5 minutes ago by XYZ." }
57
57
  end
58
58
  end
@@ -610,6 +610,12 @@
610
610
  "source": "https://github.com/primer/view_components/tree/main/app/components/primer/alpha/banner.rb",
611
611
  "lookbook": "https://primer.style/view-components/lookbook/inspect/primer/alpha/banner/default/",
612
612
  "parameters": [
613
+ {
614
+ "name": "tag",
615
+ "type": "Symbol",
616
+ "default": "`:div`",
617
+ "description": "One of `:div` or `:section`."
618
+ },
613
619
  {
614
620
  "name": "full",
615
621
  "type": "Boolean",
@@ -628,6 +634,12 @@
628
634
  "default": "`:none`",
629
635
  "description": "Whether the component can be dismissed with an \"x\" button. One of `:hide`, `:none`, or `:remove`."
630
636
  },
637
+ {
638
+ "name": "dismiss_label",
639
+ "type": "String",
640
+ "default": "`Dismiss`",
641
+ "description": "The aria-label text of the dismiss \"x\" button"
642
+ },
631
643
  {
632
644
  "name": "description",
633
645
  "type": "String",
data/static/classes.json CHANGED
@@ -390,34 +390,19 @@
390
390
  "Primer::Alpha::Dialog",
391
391
  "Primer::Alpha::Overlay"
392
392
  ],
393
- "Overlay--hidden": [
394
- "Primer::Alpha::Dialog"
395
- ],
396
- "Overlay--visibilityHidden": [
397
- "Primer::Alpha::Dialog"
398
- ],
399
- "Overlay-backdrop--anchor": [
400
- "Primer::Alpha::Dialog"
401
- ],
402
- "Overlay-backdrop--anchor-whenNarrow": [
403
- "Primer::Alpha::Dialog"
404
- ],
405
- "Overlay-backdrop--center": [
393
+ "Overlay--full": [
406
394
  "Primer::Alpha::Dialog"
407
395
  ],
408
- "Overlay-backdrop--center-whenNarrow": [
396
+ "Overlay--full-whenNarrow": [
409
397
  "Primer::Alpha::Dialog"
410
398
  ],
411
- "Overlay-backdrop--full": [
412
- "Primer::Alpha::Dialog"
413
- ],
414
- "Overlay-backdrop--full-whenNarrow": [
399
+ "Overlay--hidden": [
415
400
  "Primer::Alpha::Dialog"
416
401
  ],
417
- "Overlay-backdrop--side": [
402
+ "Overlay--placement-right-whenNarrow": [
418
403
  "Primer::Alpha::Dialog"
419
404
  ],
420
- "Overlay-backdrop--side-whenNarrow": [
405
+ "Overlay--visibilityHidden": [
421
406
  "Primer::Alpha::Dialog"
422
407
  ],
423
408
  "Overlay-body": [
@@ -155,6 +155,7 @@
155
155
  "Primer::Alpha::AutoComplete::Item": {
156
156
  },
157
157
  "Primer::Alpha::Banner": {
158
+ "DEFAULT_DISMISS_LABEL": "Dismiss",
158
159
  "DEFAULT_DISMISS_SCHEME": "none",
159
160
  "DEFAULT_ICONS": {
160
161
  "default": "bell",
@@ -163,6 +164,7 @@
163
164
  "success": "check-circle"
164
165
  },
165
166
  "DEFAULT_SCHEME": "default",
167
+ "DEFAULT_TAG": "div",
166
168
  "DISMISS_SCHEMES": [
167
169
  "none",
168
170
  "remove",
@@ -179,7 +181,11 @@
179
181
  "warning": "Banner--warning",
180
182
  "danger": "Banner--error",
181
183
  "success": "Banner--success"
182
- }
184
+ },
185
+ "TAG_OPTIONS": [
186
+ "div",
187
+ "section"
188
+ ]
183
189
  },
184
190
  "Primer::Alpha::ButtonMarketing": {
185
191
  "DEFAULT_SCHEME": "default",
@@ -227,16 +233,16 @@
227
233
  "Footer": "Primer::Alpha::Dialog::Footer",
228
234
  "Header": "Primer::Alpha::Dialog::Header",
229
235
  "POSITION_MAPPINGS": {
230
- "center": "Overlay-backdrop--center",
231
- "left": "Overlay-backdrop--side Overlay-backdrop--placement-left",
232
- "right": "Overlay-backdrop--side Overlay-backdrop--placement-right"
236
+ "center": "",
237
+ "left": "Overlay--placement-left",
238
+ "right": "Overlay--placement-right"
233
239
  },
234
240
  "POSITION_NARROW_MAPPINGS": {
235
241
  "inherit": "",
236
- "bottom": "Overlay-backdrop--side-whenNarrow Overlay-backdrop--placement-bottom-whenNarrow",
237
- "fullscreen": "Overlay-backdrop--full-whenNarrow",
238
- "left": "Overlay-backdrop--side-whenNarrow Overlay-backdrop--placement-left-whenNarrow",
239
- "right": "Overlay-backdrop--side-whenNarrow Overlay-backdrop--placement-right-whenNarrow"
242
+ "bottom": "Overlay--placement-bottom-whenNarrow",
243
+ "fullscreen": "Overlay--full-whenNarrow",
244
+ "left": "Overlay--placement-left-whenNarrow",
245
+ "right": "Overlay--placement-right-whenNarrow"
240
246
  },
241
247
  "POSITION_NARROW_OPTIONS": [
242
248
  "inherit",
@@ -1427,14 +1433,14 @@
1427
1433
  "block",
1428
1434
  "none"
1429
1435
  ],
1430
- "DEFAULT_HEADER_VARIANT": "medium",
1436
+ "DEFAULT_HEADER_VARIANT": "large",
1431
1437
  "DEFAULT_PARENT_LINK_DISPLAY": [
1432
1438
  "block",
1433
1439
  "none"
1434
1440
  ],
1435
1441
  "HEADER_VARIANT_OPTIONS": [
1436
- "large",
1437
- "medium"
1442
+ "medium",
1443
+ "large"
1438
1444
  ],
1439
1445
  "HEADING_TAG_FALLBACK": "h2",
1440
1446
  "HEADING_TAG_OPTIONS": [
@@ -1627,6 +1627,19 @@
1627
1627
  ]
1628
1628
  }
1629
1629
  },
1630
+ {
1631
+ "preview_path": "primer/alpha/action_menu/in_scroll_container",
1632
+ "name": "in_scroll_container",
1633
+ "snapshot": "false",
1634
+ "skip_rules": {
1635
+ "wont_fix": [
1636
+ "region"
1637
+ ],
1638
+ "will_fix": [
1639
+ "color-contrast"
1640
+ ]
1641
+ }
1642
+ },
1630
1643
  {
1631
1644
  "preview_path": "primer/alpha/action_menu/align_end",
1632
1645
  "name": "align_end",
@@ -2230,6 +2243,12 @@
2230
2243
  "source": "https://github.com/primer/view_components/tree/main/app/components/primer/alpha/banner.rb",
2231
2244
  "lookbook": "https://primer.style/view-components/lookbook/inspect/primer/alpha/banner/default/",
2232
2245
  "parameters": [
2246
+ {
2247
+ "name": "tag",
2248
+ "type": "Symbol",
2249
+ "default": "`:div`",
2250
+ "description": "One of `:div` or `:section`."
2251
+ },
2233
2252
  {
2234
2253
  "name": "full",
2235
2254
  "type": "Boolean",
@@ -2248,6 +2267,12 @@
2248
2267
  "default": "`:none`",
2249
2268
  "description": "Whether the component can be dismissed with an \"x\" button. One of `:hide`, `:none`, or `:remove`."
2250
2269
  },
2270
+ {
2271
+ "name": "dismiss_label",
2272
+ "type": "String",
2273
+ "default": "`Dismiss`",
2274
+ "description": "The aria-label text of the dismiss \"x\" button"
2275
+ },
2251
2276
  {
2252
2277
  "name": "description",
2253
2278
  "type": "String",
@@ -3311,6 +3336,45 @@
3311
3336
  "color-contrast"
3312
3337
  ]
3313
3338
  }
3339
+ },
3340
+ {
3341
+ "preview_path": "primer/alpha/dialog/left_side",
3342
+ "name": "left_side",
3343
+ "snapshot": "interactive",
3344
+ "skip_rules": {
3345
+ "wont_fix": [
3346
+ "region"
3347
+ ],
3348
+ "will_fix": [
3349
+ "color-contrast"
3350
+ ]
3351
+ }
3352
+ },
3353
+ {
3354
+ "preview_path": "primer/alpha/dialog/right_side",
3355
+ "name": "right_side",
3356
+ "snapshot": "interactive",
3357
+ "skip_rules": {
3358
+ "wont_fix": [
3359
+ "region"
3360
+ ],
3361
+ "will_fix": [
3362
+ "color-contrast"
3363
+ ]
3364
+ }
3365
+ },
3366
+ {
3367
+ "preview_path": "primer/alpha/dialog/scroll_container",
3368
+ "name": "scroll_container",
3369
+ "snapshot": "interactive",
3370
+ "skip_rules": {
3371
+ "wont_fix": [
3372
+ "region"
3373
+ ],
3374
+ "will_fix": [
3375
+ "color-contrast"
3376
+ ]
3377
+ }
3314
3378
  }
3315
3379
  ],
3316
3380
  "subcomponents": [
@@ -16200,8 +16264,8 @@
16200
16264
  }
16201
16265
  },
16202
16266
  {
16203
- "preview_path": "primer/open_project/page_header/large_title",
16204
- "name": "large_title",
16267
+ "preview_path": "primer/open_project/page_header/medium_title",
16268
+ "name": "medium_title",
16205
16269
  "snapshot": "false",
16206
16270
  "skip_rules": {
16207
16271
  "wont_fix": [
@@ -16408,6 +16472,6 @@
16408
16472
  "component": "BaseComponent",
16409
16473
  "fully_qualified_name": "Primer::BaseComponent",
16410
16474
  "description_md": "All Primer ViewComponents accept a standard set of options called system arguments, mimicking the [styled-system API](https://styled-system.com/table) used by [Primer React](https://primer.style/components/system-props).\n\nUnder the hood, system arguments are [mapped](https://github.com/primer/view_components/blob/main/lib/primer/classify.rb) to Primer CSS classes, with any remaining options passed to Rails' [`content_tag`](https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag).\n\n## Responsive values\n\nTo apply different values across responsive breakpoints, pass an array with up to five values in the order `[default, small, medium, large, xlarge]`. To skip a breakpoint, pass `nil`.\n\nFor example:\n\n```erb\n<%= render Primer::Beta::Heading.new(mt: [0, nil, nil, 4, 2]) do %>\n Hello world\n<% end %>\n```\n\nRenders:\n\n```html\n<h1 class=\"mt-0 mt-lg-4 mt-xl-2\">Hello world</h1>\n```",
16411
- "args_md": "## HTML attributes\n\nSystem arguments include most HTML attributes. For example:\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `aria` | `Hash` | Aria attributes: `aria: { label: \"foo\" }` renders `aria-label='foo'`. |\n| `data` | `Hash` | Data attributes: `data: { foo: :bar }` renders `data-foo='bar'`. |\n| `height` | `Integer` | Height. |\n| `hidden` | `Boolean` | Whether to assign the `hidden` attribute. |\n| `style` | `String` | Inline styles. |\n| `title` | `String` | The `title` attribute. |\n| `width` | `Integer` | Width. |\n\n## Animation\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `animation` | Symbol | One of `:fade_down`, `:fade_in`, `:fade_out`, `:fade_up`, `:grow_x`, `:hover_grow`, `:pulse`, `:pulse_in`, `:rotate`, `:scale_in`, or `:shrink_x`. |\n\n## Border\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `border_bottom` | Integer | Set to `0` to remove the bottom border. |\n| `border_left` | Integer | Set to `0` to remove the left border. |\n| `border_radius` | Integer | One of `0`, `1`, `2`, or `3`. |\n| `border_right` | Integer | Set to `0` to remove the right border. |\n| `border_top` | Integer | Set to `0` to remove the top border. |\n| `border` | Symbol | One of `:bottom`, `:left`, `:right`, `:top`, `:x`, `:y`, or `true`. |\n| `box_shadow` | Boolean, Symbol | Box shadow. One of `:extra_large`, `:large`, `:medium`, `:none`, or `true`. |\n\n## Color\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `bg` | Symbol | Background color. One of `:accent`, `:accent_emphasis`, `:attention`, `:attention_emphasis`, `:closed`, `:closed_emphasis`, `:danger`, `:danger_emphasis`, `:default`, `:done`, `:done_emphasis`, `:emphasis`, `:inset`, `:open`, `:open_emphasis`, `:overlay`, `:severe`, `:severe_emphasis`, `:sponsors`, `:sponsors_emphasis`, `:subtle`, `:success`, `:success_emphasis`, or `:transparent`. |\n| `border_color` | Symbol | Border color. One of `:accent`, `:accent_emphasis`, `:attention`, `:attention_emphasis`, `:closed`, `:closed_emphasis`, `:danger`, `:danger_emphasis`, `:default`, `:done`, `:done_emphasis`, `:muted`, `:open`, `:open_emphasis`, `:severe`, `:severe_emphasis`, `:sponsors`, `:sponsors_emphasis`, `:subtle`, `:success`, or `:success_emphasis`. |\n| `color` | Symbol | Text color. One of `:accent`, `:attention`, `:closed`, `:danger`, `:default`, `:done`, `:inherit`, `:muted`, `:on_emphasis`, `:open`, `:severe`, `:sponsors`, `:subtle`, or `:success`. |\n\n## Flex\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `align_items` | Symbol | One of `:baseline`, `:center`, `:flex_end`, `:flex_start`, or `:stretch`. |\n| `align_self` | Symbol | One of `:auto`, `:baseline`, `:center`, `:end`, `:start`, or `:stretch`. |\n| `direction` | Symbol | One of `:column`, `:column_reverse`, `:row`, or `:row_reverse`. |\n| `flex` | Integer, Symbol | One of `1` or `:auto`. |\n| `flex_grow` | Integer | To enable, set to `0`. |\n| `flex_shrink` | Integer | To enable, set to `0`. |\n| `flex_wrap` | Symbol | One of `:nowrap`, `:reverse`, or `:wrap`. |\n| `justify_content` | Symbol | One of `:center`, `:flex_end`, `:flex_start`, `:space_around`, or `:space_between`. |\n\n## Grid\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `clearfix` | Boolean | Whether to assign the `clearfix` class. |\n| `col` | Integer | Number of columns. One of `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n| `container` | Symbol | Size of the container. One of `:lg`, `:md`, `:sm`, or `:xl`. |\n\n## Layout\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `display` | Symbol | One of `:block`, `:flex`, `:inline`, `:inline_block`, `:inline_flex`, `:none`, `:table`, or `:table_cell`. |\n| `w` | Symbol | One of `:auto`, `:fit`, or `:full`. |\n| `h` | Symbol | One of `:fit` or `:full`. |\n| `hide` | Symbol | Hide the element at a specific breakpoint. One of `:lg`, `:md`, `:sm`, `:whenNarrow`, `:whenRegular`, `:whenWide`, or `:xl`. |\n| `visibility` | Symbol | Visibility. One of `:hidden` or `:visible`. |\n| `vertical_align` | Symbol | One of `:baseline`, `:bottom`, `:middle`, `:text_bottom`, `:text_top`, or `:top`. |\n\n## Position\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `bottom` | Boolean | If `false`, sets `bottom: 0`. |\n| `float` | Symbol | One of `:left`, `:none`, or `:right`. |\n| `left` | Boolean | If `false`, sets `left: 0`. |\n| `position` | Symbol | One of `:absolute`, `:fixed`, `:relative`, `:static`, or `:sticky`. |\n| `right` | Boolean | If `false`, sets `right: 0`. |\n| `top` | Boolean | If `false`, sets `top: 0`. |\n\n## Spacing\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `m` | Integer | Margin. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, or `:auto`. |\n| `mb` | Integer | Margin bottom. One of `-12`, `-11`, `-10`, `-9`, `-8`, `-7`, `-6`, `-5`, `-4`, `-3`, `-2`, `-1`, `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, or `:auto`. |\n| `ml` | Integer | Margin left. One of `-6`, `-5`, `-4`, `-3`, `-2`, `-1`, `0`, `1`, `2`, `3`, `4`, `5`, `6`, or `:auto`. |\n| `mr` | Integer | Margin right. One of `-6`, `-5`, `-4`, `-3`, `-2`, `-1`, `0`, `1`, `2`, `3`, `4`, `5`, `6`, or `:auto`. |\n| `mt` | Integer | Margin top. One of `-12`, `-11`, `-10`, `-9`, `-8`, `-7`, `-6`, `-5`, `-4`, `-3`, `-2`, `-1`, `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, or `:auto`. |\n| `mx` | Integer | Horizontal margins. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, or `:auto`. |\n| `my` | Integer | Vertical margins. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n| `p` | Integer | Padding. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, or `:responsive`. |\n| `pb` | Integer | Padding bottom. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n| `pl` | Integer | Padding left. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n| `pr` | Integer | Padding right. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n| `pt` | Integer | Padding left. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n| `px` | Integer | Horizontal padding. One of `0`, `1`, `2`, `3`, `4`, `5`, or `6`. |\n| `py` | Integer | Vertical padding. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n\n## Typography\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `font_family` | Symbol | Font family. One of `:mono`. |\n| `font_size` | String, Integer, Symbol | One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `00`, `:normal`, or `:small`. |\n| `font_style` | Symbol | Font style. One of `:italic`. |\n| `font_weight` | Symbol | Font weight. One of `:bold`, `:emphasized`, `:light`, or `:normal`. |\n| `text_align` | Symbol | Text alignment. One of `:center`, `:left`, or `:right`. |\n| `text_transform` | Symbol | Text transformation. One of `:capitalize` or `:uppercase`. |\n| `underline` | Boolean | Whether text should be underlined. |\n| `word_break` | Symbol | Whether to break words on line breaks. One of `:break_all` or `:break_word`. |\n\n## Other\n\n| Name | Type | Description |\n| :- | :- | :- |\n| classes | String | CSS class name value to be concatenated with generated Primer CSS classes. |\n| test_selector | String | Adds `data-test-selector='given value'` in non-Production environments for testing purposes. |"
16475
+ "args_md": "## HTML attributes\n\nUse system arguments to add HTML attributes to elements. For the most part, system arguments map 1:1 to\nHTML attributes. For example, `render(Component.new(title: \"Foo\"))` will result in eg. `<div title=\"foo\">`.\nHowever, ViewComponents applies special handling to certain system arguments. See the table below for details.\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `aria` | `Hash` | Aria attributes: `aria: { label: \"foo\" }` renders `aria-label='foo'`. |\n| `data` | `Hash` | Data attributes: `data: { foo: :bar }` renders `data-foo='bar'`. |\n\n## Utility classes\n\nViewComponents provides a convenient way to add Primer CSS utility classes to HTML elements. Use the shorthand\ndocumented in the tables below instead of adding CSS classes directly.\n\n### Animation\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `animation` | Symbol | One of `:fade_down`, `:fade_in`, `:fade_out`, `:fade_up`, `:grow_x`, `:hover_grow`, `:pulse`, `:pulse_in`, `:rotate`, `:scale_in`, or `:shrink_x`. |\n\n### Border\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `border_bottom` | Integer | Set to `0` to remove the bottom border. |\n| `border_left` | Integer | Set to `0` to remove the left border. |\n| `border_radius` | Integer | One of `0`, `1`, `2`, or `3`. |\n| `border_right` | Integer | Set to `0` to remove the right border. |\n| `border_top` | Integer | Set to `0` to remove the top border. |\n| `border` | Symbol | One of `:bottom`, `:left`, `:right`, `:top`, `:x`, `:y`, or `true`. |\n| `box_shadow` | Boolean, Symbol | Box shadow. One of `:extra_large`, `:large`, `:medium`, `:none`, or `true`. |\n\n### Color\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `bg` | Symbol | Background color. One of `:accent`, `:accent_emphasis`, `:attention`, `:attention_emphasis`, `:closed`, `:closed_emphasis`, `:danger`, `:danger_emphasis`, `:default`, `:done`, `:done_emphasis`, `:emphasis`, `:inset`, `:open`, `:open_emphasis`, `:overlay`, `:severe`, `:severe_emphasis`, `:sponsors`, `:sponsors_emphasis`, `:subtle`, `:success`, `:success_emphasis`, or `:transparent`. |\n| `border_color` | Symbol | Border color. One of `:accent`, `:accent_emphasis`, `:attention`, `:attention_emphasis`, `:closed`, `:closed_emphasis`, `:danger`, `:danger_emphasis`, `:default`, `:done`, `:done_emphasis`, `:muted`, `:open`, `:open_emphasis`, `:severe`, `:severe_emphasis`, `:sponsors`, `:sponsors_emphasis`, `:subtle`, `:success`, or `:success_emphasis`. |\n| `color` | Symbol | Text color. One of `:accent`, `:attention`, `:closed`, `:danger`, `:default`, `:done`, `:inherit`, `:muted`, `:on_emphasis`, `:open`, `:severe`, `:sponsors`, `:subtle`, or `:success`. |\n\n### Flex\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `align_items` | Symbol | One of `:baseline`, `:center`, `:flex_end`, `:flex_start`, or `:stretch`. |\n| `align_self` | Symbol | One of `:auto`, `:baseline`, `:center`, `:end`, `:start`, or `:stretch`. |\n| `direction` | Symbol | One of `:column`, `:column_reverse`, `:row`, or `:row_reverse`. |\n| `flex` | Integer, Symbol | One of `1` or `:auto`. |\n| `flex_grow` | Integer | To enable, set to `0`. |\n| `flex_shrink` | Integer | To enable, set to `0`. |\n| `flex_wrap` | Symbol | One of `:nowrap`, `:reverse`, or `:wrap`. |\n| `justify_content` | Symbol | One of `:center`, `:flex_end`, `:flex_start`, `:space_around`, or `:space_between`. |\n\n### Grid\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `clearfix` | Boolean | Whether to assign the `clearfix` class. |\n| `col` | Integer | Number of columns. One of `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n| `container` | Symbol | Size of the container. One of `:lg`, `:md`, `:sm`, or `:xl`. |\n\n### Layout\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `display` | Symbol | One of `:block`, `:flex`, `:inline`, `:inline_block`, `:inline_flex`, `:none`, `:table`, or `:table_cell`. |\n| `w` | Symbol | Sets the element's width. One of `:auto`, `:fit`, or `:full`. |\n| `h` | Symbol | Sets the element's height. One of `:fit` or `:full`. |\n| `hide` | Symbol | Hide the element at a specific breakpoint. One of `:lg`, `:md`, `:sm`, `:whenNarrow`, `:whenRegular`, `:whenWide`, or `:xl`. |\n| `visibility` | Symbol | Visibility. One of `:hidden` or `:visible`. |\n| `vertical_align` | Symbol | One of `:baseline`, `:bottom`, `:middle`, `:text_bottom`, `:text_top`, or `:top`. |\n\n### Position\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `bottom` | Boolean | If `false`, sets `bottom: 0`. |\n| `float` | Symbol | One of `:left`, `:none`, or `:right`. |\n| `left` | Boolean | If `false`, sets `left: 0`. |\n| `position` | Symbol | One of `:absolute`, `:fixed`, `:relative`, `:static`, or `:sticky`. |\n| `right` | Boolean | If `false`, sets `right: 0`. |\n| `top` | Boolean | If `false`, sets `top: 0`. |\n\n### Spacing\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `m` | Integer | Margin. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, or `:auto`. |\n| `mb` | Integer | Margin bottom. One of `-12`, `-11`, `-10`, `-9`, `-8`, `-7`, `-6`, `-5`, `-4`, `-3`, `-2`, `-1`, `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, or `:auto`. |\n| `ml` | Integer | Margin left. One of `-6`, `-5`, `-4`, `-3`, `-2`, `-1`, `0`, `1`, `2`, `3`, `4`, `5`, `6`, or `:auto`. |\n| `mr` | Integer | Margin right. One of `-6`, `-5`, `-4`, `-3`, `-2`, `-1`, `0`, `1`, `2`, `3`, `4`, `5`, `6`, or `:auto`. |\n| `mt` | Integer | Margin top. One of `-12`, `-11`, `-10`, `-9`, `-8`, `-7`, `-6`, `-5`, `-4`, `-3`, `-2`, `-1`, `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, or `:auto`. |\n| `mx` | Integer | Horizontal margins. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, or `:auto`. |\n| `my` | Integer | Vertical margins. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n| `p` | Integer | Padding. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, or `:responsive`. |\n| `pb` | Integer | Padding bottom. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n| `pl` | Integer | Padding left. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n| `pr` | Integer | Padding right. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n| `pt` | Integer | Padding left. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n| `px` | Integer | Horizontal padding. One of `0`, `1`, `2`, `3`, `4`, `5`, or `6`. |\n| `py` | Integer | Vertical padding. One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, or `12`. |\n\n### Typography\n\n| Name | Type | Description |\n| :- | :- | :- |\n| `font_family` | Symbol | Font family. One of `:mono`. |\n| `font_size` | String, Integer, Symbol | One of `0`, `1`, `2`, `3`, `4`, `5`, `6`, `00`, `:normal`, or `:small`. |\n| `font_style` | Symbol | Font style. One of `:italic`. |\n| `font_weight` | Symbol | Font weight. One of `:bold`, `:emphasized`, `:light`, or `:normal`. |\n| `text_align` | Symbol | Text alignment. One of `:center`, `:left`, or `:right`. |\n| `text_transform` | Symbol | Text transformation. One of `:capitalize` or `:uppercase`. |\n| `underline` | Boolean | Whether text should be underlined. |\n| `word_break` | Symbol | Whether to break words on line breaks. One of `:break_all` or `:break_word`. |\n\n### Other\n\n| Name | Type | Description |\n| :- | :- | :- |\n| classes | String | CSS class name value to be concatenated with generated Primer CSS classes. |\n| test_selector | String | Adds `data-test-selector='given value'` in non-Production environments for testing purposes. |"
16412
16476
  }
16413
16477
  ]
data/static/previews.json CHANGED
@@ -658,6 +658,19 @@
658
658
  ]
659
659
  }
660
660
  },
661
+ {
662
+ "preview_path": "primer/alpha/action_menu/in_scroll_container",
663
+ "name": "in_scroll_container",
664
+ "snapshot": "false",
665
+ "skip_rules": {
666
+ "wont_fix": [
667
+ "region"
668
+ ],
669
+ "will_fix": [
670
+ "color-contrast"
671
+ ]
672
+ }
673
+ },
661
674
  {
662
675
  "preview_path": "primer/alpha/action_menu/align_end",
663
676
  "name": "align_end",
@@ -3134,6 +3147,45 @@
3134
3147
  "color-contrast"
3135
3148
  ]
3136
3149
  }
3150
+ },
3151
+ {
3152
+ "preview_path": "primer/alpha/dialog/left_side",
3153
+ "name": "left_side",
3154
+ "snapshot": "interactive",
3155
+ "skip_rules": {
3156
+ "wont_fix": [
3157
+ "region"
3158
+ ],
3159
+ "will_fix": [
3160
+ "color-contrast"
3161
+ ]
3162
+ }
3163
+ },
3164
+ {
3165
+ "preview_path": "primer/alpha/dialog/right_side",
3166
+ "name": "right_side",
3167
+ "snapshot": "interactive",
3168
+ "skip_rules": {
3169
+ "wont_fix": [
3170
+ "region"
3171
+ ],
3172
+ "will_fix": [
3173
+ "color-contrast"
3174
+ ]
3175
+ }
3176
+ },
3177
+ {
3178
+ "preview_path": "primer/alpha/dialog/scroll_container",
3179
+ "name": "scroll_container",
3180
+ "snapshot": "interactive",
3181
+ "skip_rules": {
3182
+ "wont_fix": [
3183
+ "region"
3184
+ ],
3185
+ "will_fix": [
3186
+ "color-contrast"
3187
+ ]
3188
+ }
3137
3189
  }
3138
3190
  ]
3139
3191
  },
@@ -4950,8 +5002,8 @@
4950
5002
  }
4951
5003
  },
4952
5004
  {
4953
- "preview_path": "primer/open_project/page_header/large_title",
4954
- "name": "large_title",
5005
+ "preview_path": "primer/open_project/page_header/medium_title",
5006
+ "name": "medium_title",
4955
5007
  "snapshot": "false",
4956
5008
  "skip_rules": {
4957
5009
  "wont_fix": [
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openproject-primer_view_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Open Source
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-12-11 00:00:00.000000000 Z
12
+ date: 2024-01-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionview
@@ -99,8 +99,10 @@ files:
99
99
  - app/assets/javascripts/app/components/primer/beta/nav_list.d.ts
100
100
  - app/assets/javascripts/app/components/primer/beta/nav_list_group_element.d.ts
101
101
  - app/assets/javascripts/app/components/primer/beta/relative_time.d.ts
102
+ - app/assets/javascripts/app/components/primer/dialog_helper.d.ts
102
103
  - app/assets/javascripts/app/components/primer/focus_group.d.ts
103
104
  - app/assets/javascripts/app/components/primer/primer.d.ts
105
+ - app/assets/javascripts/app/components/primer/scrollable_region.d.ts
104
106
  - app/assets/javascripts/lib/primer/forms/primer_multi_input.d.ts
105
107
  - app/assets/javascripts/lib/primer/forms/primer_text_field.d.ts
106
108
  - app/assets/javascripts/lib/primer/forms/toggle_switch_input.d.ts
@@ -444,6 +446,9 @@ files:
444
446
  - app/components/primer/component.rb
445
447
  - app/components/primer/conditional_wrapper.rb
446
448
  - app/components/primer/content.rb
449
+ - app/components/primer/dialog_helper.d.ts
450
+ - app/components/primer/dialog_helper.js
451
+ - app/components/primer/dialog_helper.ts
447
452
  - app/components/primer/focus_group.d.ts
448
453
  - app/components/primer/focus_group.js
449
454
  - app/components/primer/focus_group.ts
@@ -487,6 +492,9 @@ files:
487
492
  - app/components/primer/primer.js
488
493
  - app/components/primer/primer.pcss
489
494
  - app/components/primer/primer.ts
495
+ - app/components/primer/scrollable_region.d.ts
496
+ - app/components/primer/scrollable_region.js
497
+ - app/components/primer/scrollable_region.ts
490
498
  - app/components/primer/tooltip.rb
491
499
  - app/components/primer/truncate.css
492
500
  - app/components/primer/truncate.css.json
@@ -733,6 +741,7 @@ files:
733
741
  - previews/primer/alpha/action_menu_preview.rb
734
742
  - previews/primer/alpha/action_menu_preview/align_end.html.erb
735
743
  - previews/primer/alpha/action_menu_preview/content_labels.html.erb
744
+ - previews/primer/alpha/action_menu_preview/in_scroll_container.html.erb
736
745
  - previews/primer/alpha/action_menu_preview/multiple_select_form.html.erb
737
746
  - previews/primer/alpha/action_menu_preview/opens_dialog.html.erb
738
747
  - previews/primer/alpha/action_menu_preview/single_select_form.html.erb
@@ -752,6 +761,7 @@ files:
752
761
  - previews/primer/alpha/dialog_preview/body_has_scrollbar_overflow.html.erb
753
762
  - previews/primer/alpha/dialog_preview/custom_header.html.erb
754
763
  - previews/primer/alpha/dialog_preview/nested_dialog.html.erb
764
+ - previews/primer/alpha/dialog_preview/scroll_container.html.erb
755
765
  - previews/primer/alpha/dialog_preview/test.html.erb
756
766
  - previews/primer/alpha/dialog_preview/with_auto_complete.html.erb
757
767
  - previews/primer/alpha/dialog_preview/with_footer.html.erb
@@ -912,7 +922,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
912
922
  - !ruby/object:Gem::Version
913
923
  version: '0'
914
924
  requirements: []
915
- rubygems_version: 3.4.10
925
+ rubygems_version: 3.4.19
916
926
  signing_key:
917
927
  specification_version: 4
918
928
  summary: ViewComponents of the Primer Design System for OpenProject