openproject-primer_view_components 0.84.5 → 0.86.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/app/assets/javascripts/components/primer/open_project/sub_header_element.d.ts +1 -1
  4. data/app/assets/javascripts/primer_view_components.js +1 -1
  5. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  6. data/app/assets/styles/primer_view_components.css +1 -1
  7. data/app/assets/styles/primer_view_components.css.map +1 -1
  8. data/app/components/primer/alpha/select_panel_element.js +2 -2
  9. data/app/components/primer/alpha/select_panel_element.ts +2 -2
  10. data/app/components/primer/alpha/tree_view/leaf_node.html.erb +5 -0
  11. data/app/components/primer/alpha/tree_view/leaf_node.rb +18 -0
  12. data/app/components/primer/alpha/tree_view/node.html.erb +3 -0
  13. data/app/components/primer/alpha/tree_view/node.rb +10 -0
  14. data/app/components/primer/alpha/tree_view/sub_tree_node.html.erb +5 -0
  15. data/app/components/primer/alpha/tree_view/sub_tree_node.rb +18 -0
  16. data/app/components/primer/alpha/tree_view/trailing_action.html.erb +3 -0
  17. data/app/components/primer/alpha/tree_view/trailing_action.rb +18 -0
  18. data/app/components/primer/alpha/tree_view.css +1 -1
  19. data/app/components/primer/alpha/tree_view.css.json +4 -1
  20. data/app/components/primer/alpha/tree_view.css.map +1 -1
  21. data/app/components/primer/alpha/tree_view.pcss +22 -6
  22. data/app/components/primer/open_project/filterable_tree_view/sub_tree.rb +6 -6
  23. data/app/components/primer/open_project/filterable_tree_view.css +1 -0
  24. data/app/components/primer/open_project/filterable_tree_view.css.json +14 -0
  25. data/app/components/primer/open_project/filterable_tree_view.css.map +1 -0
  26. data/app/components/primer/open_project/filterable_tree_view.html.erb +26 -14
  27. data/app/components/primer/open_project/filterable_tree_view.js +294 -5
  28. data/app/components/primer/open_project/filterable_tree_view.pcss +57 -0
  29. data/app/components/primer/open_project/filterable_tree_view.rb +58 -10
  30. data/app/components/primer/open_project/filterable_tree_view.ts +316 -4
  31. data/app/components/primer/open_project/sub_header.css +1 -1
  32. data/app/components/primer/open_project/sub_header.css.json +2 -1
  33. data/app/components/primer/open_project/sub_header.css.map +1 -1
  34. data/app/components/primer/open_project/sub_header.html.erb +11 -8
  35. data/app/components/primer/open_project/sub_header.pcss +14 -7
  36. data/app/components/primer/open_project/sub_header.rb +46 -25
  37. data/app/components/primer/open_project/sub_header_element.d.ts +1 -1
  38. data/app/components/primer/open_project/sub_header_element.js +6 -6
  39. data/app/components/primer/open_project/sub_header_element.ts +5 -10
  40. data/app/components/primer/primer.pcss +1 -0
  41. data/app/controllers/primer/view_components/filterable_tree_view_items_controller.rb +192 -0
  42. data/app/views/primer/view_components/filterable_tree_view_items/_node.html.erb +38 -0
  43. data/app/views/primer/view_components/filterable_tree_view_items/async_form_tree.html.erb +9 -0
  44. data/app/views/primer/view_components/filterable_tree_view_items/index.html.erb +6 -0
  45. data/config/routes.rb +4 -0
  46. data/lib/primer/view_components/version.rb +2 -2
  47. data/previews/primer/alpha/select_panel_preview.rb +0 -27
  48. data/previews/primer/alpha/text_area_preview.rb +0 -1
  49. data/previews/primer/alpha/text_field_preview.rb +0 -1
  50. data/previews/primer/alpha/tree_view_preview/leaf_node_playground.html.erb +4 -0
  51. data/previews/primer/alpha/tree_view_preview.rb +3 -0
  52. data/previews/primer/open_project/filterable_tree_view_preview/async.html.erb +3 -0
  53. data/previews/primer/open_project/filterable_tree_view_preview/async_form_input.html.erb +9 -0
  54. data/previews/primer/open_project/filterable_tree_view_preview/link_nodes.html.erb +18 -0
  55. data/previews/primer/open_project/filterable_tree_view_preview.rb +23 -2
  56. data/previews/primer/open_project/sub_header_preview/quick_filters.html.erb +47 -0
  57. data/previews/primer/open_project/sub_header_preview.rb +23 -1
  58. data/static/arguments.json +28 -0
  59. data/static/audited_at.json +1 -0
  60. data/static/classes.json +9 -0
  61. data/static/constants.json +10 -1
  62. data/static/info_arch.json +189 -30
  63. data/static/previews.json +94 -29
  64. data/static/statuses.json +1 -0
  65. metadata +18 -10
@@ -4610,6 +4610,11 @@
4610
4610
  "description": null,
4611
4611
  "parameters": []
4612
4612
  },
4613
+ {
4614
+ "name": "trailing_action",
4615
+ "description": null,
4616
+ "parameters": []
4617
+ },
4613
4618
  {
4614
4619
  "name": "trailing_visual",
4615
4620
  "description": null,
@@ -4645,6 +4650,11 @@
4645
4650
  "description": null,
4646
4651
  "parameters": []
4647
4652
  },
4653
+ {
4654
+ "name": "trailing_action",
4655
+ "description": null,
4656
+ "parameters": []
4657
+ },
4648
4658
  {
4649
4659
  "name": "trailing_visual",
4650
4660
  "description": null,
@@ -8102,7 +8112,7 @@
8102
8112
  {
8103
8113
  "preview_path": "primer/alpha/select_panel/default",
8104
8114
  "name": "default",
8105
- "snapshot": "interactive",
8115
+ "snapshot": "false",
8106
8116
  "skip_rules": {
8107
8117
  "wont_fix": [
8108
8118
  "region"
@@ -8115,7 +8125,7 @@
8115
8125
  {
8116
8126
  "preview_path": "primer/alpha/select_panel/local_fetch",
8117
8127
  "name": "local_fetch",
8118
- "snapshot": "interactive",
8128
+ "snapshot": "false",
8119
8129
  "skip_rules": {
8120
8130
  "wont_fix": [
8121
8131
  "region"
@@ -8128,7 +8138,7 @@
8128
8138
  {
8129
8139
  "preview_path": "primer/alpha/select_panel/eventually_local_fetch",
8130
8140
  "name": "eventually_local_fetch",
8131
- "snapshot": "interactive",
8141
+ "snapshot": "false",
8132
8142
  "skip_rules": {
8133
8143
  "wont_fix": [
8134
8144
  "region"
@@ -8141,7 +8151,7 @@
8141
8151
  {
8142
8152
  "preview_path": "primer/alpha/select_panel/remote_fetch",
8143
8153
  "name": "remote_fetch",
8144
- "snapshot": "interactive",
8154
+ "snapshot": "false",
8145
8155
  "skip_rules": {
8146
8156
  "wont_fix": [
8147
8157
  "region"
@@ -8154,7 +8164,7 @@
8154
8164
  {
8155
8165
  "preview_path": "primer/alpha/select_panel/custom_loading_label",
8156
8166
  "name": "custom_loading_label",
8157
- "snapshot": "interactive",
8167
+ "snapshot": "false",
8158
8168
  "skip_rules": {
8159
8169
  "wont_fix": [
8160
8170
  "region"
@@ -8167,7 +8177,7 @@
8167
8177
  {
8168
8178
  "preview_path": "primer/alpha/select_panel/custom_loading_description",
8169
8179
  "name": "custom_loading_description",
8170
- "snapshot": "interactive",
8180
+ "snapshot": "false",
8171
8181
  "skip_rules": {
8172
8182
  "wont_fix": [
8173
8183
  "region"
@@ -8180,7 +8190,7 @@
8180
8190
  {
8181
8191
  "preview_path": "primer/alpha/select_panel/local_fetch_no_results",
8182
8192
  "name": "local_fetch_no_results",
8183
- "snapshot": "interactive",
8193
+ "snapshot": "false",
8184
8194
  "skip_rules": {
8185
8195
  "wont_fix": [
8186
8196
  "region"
@@ -8193,7 +8203,7 @@
8193
8203
  {
8194
8204
  "preview_path": "primer/alpha/select_panel/eventually_local_fetch_no_results",
8195
8205
  "name": "eventually_local_fetch_no_results",
8196
- "snapshot": "interactive",
8206
+ "snapshot": "false",
8197
8207
  "skip_rules": {
8198
8208
  "wont_fix": [
8199
8209
  "region"
@@ -8206,7 +8216,7 @@
8206
8216
  {
8207
8217
  "preview_path": "primer/alpha/select_panel/remote_fetch_no_results",
8208
8218
  "name": "remote_fetch_no_results",
8209
- "snapshot": "interactive",
8219
+ "snapshot": "false",
8210
8220
  "skip_rules": {
8211
8221
  "wont_fix": [
8212
8222
  "region"
@@ -8219,7 +8229,7 @@
8219
8229
  {
8220
8230
  "preview_path": "primer/alpha/select_panel/single_select",
8221
8231
  "name": "single_select",
8222
- "snapshot": "interactive",
8232
+ "snapshot": "false",
8223
8233
  "skip_rules": {
8224
8234
  "wont_fix": [
8225
8235
  "region"
@@ -8232,7 +8242,7 @@
8232
8242
  {
8233
8243
  "preview_path": "primer/alpha/select_panel/multiselect",
8234
8244
  "name": "multiselect",
8235
- "snapshot": "interactive",
8245
+ "snapshot": "false",
8236
8246
  "skip_rules": {
8237
8247
  "wont_fix": [
8238
8248
  "region"
@@ -8245,7 +8255,7 @@
8245
8255
  {
8246
8256
  "preview_path": "primer/alpha/select_panel/with_dynamic_label",
8247
8257
  "name": "with_dynamic_label",
8248
- "snapshot": "interactive",
8258
+ "snapshot": "false",
8249
8259
  "skip_rules": {
8250
8260
  "wont_fix": [
8251
8261
  "region"
@@ -8258,7 +8268,7 @@
8258
8268
  {
8259
8269
  "preview_path": "primer/alpha/select_panel/with_dynamic_label_and_aria_prefix",
8260
8270
  "name": "with_dynamic_label_and_aria_prefix",
8261
- "snapshot": "interactive",
8271
+ "snapshot": "false",
8262
8272
  "skip_rules": {
8263
8273
  "wont_fix": [
8264
8274
  "region"
@@ -8271,7 +8281,7 @@
8271
8281
  {
8272
8282
  "preview_path": "primer/alpha/select_panel/footer_buttons",
8273
8283
  "name": "footer_buttons",
8274
- "snapshot": "interactive",
8284
+ "snapshot": "false",
8275
8285
  "skip_rules": {
8276
8286
  "wont_fix": [
8277
8287
  "region"
@@ -8284,7 +8294,7 @@
8284
8294
  {
8285
8295
  "preview_path": "primer/alpha/select_panel/with_avatar_items",
8286
8296
  "name": "with_avatar_items",
8287
- "snapshot": "interactive",
8297
+ "snapshot": "false",
8288
8298
  "skip_rules": {
8289
8299
  "wont_fix": [
8290
8300
  "region"
@@ -8297,7 +8307,7 @@
8297
8307
  {
8298
8308
  "preview_path": "primer/alpha/select_panel/select_panel_with_icon_button",
8299
8309
  "name": "select_panel_with_icon_button",
8300
- "snapshot": "interactive",
8310
+ "snapshot": "false",
8301
8311
  "skip_rules": {
8302
8312
  "wont_fix": [
8303
8313
  "region"
@@ -8310,7 +8320,7 @@
8310
8320
  {
8311
8321
  "preview_path": "primer/alpha/select_panel/with_leading_icons",
8312
8322
  "name": "with_leading_icons",
8313
- "snapshot": "interactive",
8323
+ "snapshot": "false",
8314
8324
  "skip_rules": {
8315
8325
  "wont_fix": [
8316
8326
  "region"
@@ -8323,7 +8333,7 @@
8323
8333
  {
8324
8334
  "preview_path": "primer/alpha/select_panel/with_trailing_icons",
8325
8335
  "name": "with_trailing_icons",
8326
- "snapshot": "interactive",
8336
+ "snapshot": "false",
8327
8337
  "skip_rules": {
8328
8338
  "wont_fix": [
8329
8339
  "region"
@@ -8336,7 +8346,7 @@
8336
8346
  {
8337
8347
  "preview_path": "primer/alpha/select_panel/with_subtitle",
8338
8348
  "name": "with_subtitle",
8339
- "snapshot": "interactive",
8349
+ "snapshot": "false",
8340
8350
  "skip_rules": {
8341
8351
  "wont_fix": [
8342
8352
  "region"
@@ -8349,7 +8359,7 @@
8349
8359
  {
8350
8360
  "preview_path": "primer/alpha/select_panel/remote_fetch_initial_failure",
8351
8361
  "name": "remote_fetch_initial_failure",
8352
- "snapshot": "interactive",
8362
+ "snapshot": "false",
8353
8363
  "skip_rules": {
8354
8364
  "wont_fix": [
8355
8365
  "region"
@@ -8362,7 +8372,7 @@
8362
8372
  {
8363
8373
  "preview_path": "primer/alpha/select_panel/remote_fetch_filter_failure",
8364
8374
  "name": "remote_fetch_filter_failure",
8365
- "snapshot": "interactive",
8375
+ "snapshot": "false",
8366
8376
  "skip_rules": {
8367
8377
  "wont_fix": [
8368
8378
  "region"
@@ -8375,7 +8385,7 @@
8375
8385
  {
8376
8386
  "preview_path": "primer/alpha/select_panel/eventually_local_fetch_initial_failure",
8377
8387
  "name": "eventually_local_fetch_initial_failure",
8378
- "snapshot": "interactive",
8388
+ "snapshot": "false",
8379
8389
  "skip_rules": {
8380
8390
  "wont_fix": [
8381
8391
  "region"
@@ -8388,7 +8398,7 @@
8388
8398
  {
8389
8399
  "preview_path": "primer/alpha/select_panel/single_select_form",
8390
8400
  "name": "single_select_form",
8391
- "snapshot": "interactive",
8401
+ "snapshot": "false",
8392
8402
  "skip_rules": {
8393
8403
  "wont_fix": [
8394
8404
  "region"
@@ -8401,7 +8411,7 @@
8401
8411
  {
8402
8412
  "preview_path": "primer/alpha/select_panel/remote_fetch_form",
8403
8413
  "name": "remote_fetch_form",
8404
- "snapshot": "interactive",
8414
+ "snapshot": "false",
8405
8415
  "skip_rules": {
8406
8416
  "wont_fix": [
8407
8417
  "region"
@@ -8414,7 +8424,7 @@
8414
8424
  {
8415
8425
  "preview_path": "primer/alpha/select_panel/multiselect_form",
8416
8426
  "name": "multiselect_form",
8417
- "snapshot": "interactive",
8427
+ "snapshot": "false",
8418
8428
  "skip_rules": {
8419
8429
  "wont_fix": [
8420
8430
  "region"
@@ -8427,7 +8437,7 @@
8427
8437
  {
8428
8438
  "preview_path": "primer/alpha/select_panel/list_of_links",
8429
8439
  "name": "list_of_links",
8430
- "snapshot": "interactive",
8440
+ "snapshot": "false",
8431
8441
  "skip_rules": {
8432
8442
  "wont_fix": [
8433
8443
  "region"
@@ -8440,7 +8450,7 @@
8440
8450
  {
8441
8451
  "preview_path": "primer/alpha/select_panel/no_values",
8442
8452
  "name": "no_values",
8443
- "snapshot": "interactive",
8453
+ "snapshot": "false",
8444
8454
  "skip_rules": {
8445
8455
  "wont_fix": [
8446
8456
  "region"
@@ -9368,7 +9378,7 @@
9368
9378
  {
9369
9379
  "preview_path": "primer/alpha/text_area/with_character_limit_over_limit",
9370
9380
  "name": "with_character_limit_over_limit",
9371
- "snapshot": "interactive",
9381
+ "snapshot": "false",
9372
9382
  "skip_rules": {
9373
9383
  "wont_fix": [
9374
9384
  "region"
@@ -9907,7 +9917,7 @@
9907
9917
  {
9908
9918
  "preview_path": "primer/alpha/text_field/with_character_limit_over_limit",
9909
9919
  "name": "with_character_limit_over_limit",
9910
- "snapshot": "interactive",
9920
+ "snapshot": "false",
9911
9921
  "skip_rules": {
9912
9922
  "wont_fix": [
9913
9923
  "region"
@@ -10900,6 +10910,32 @@
10900
10910
  "previews": [],
10901
10911
  "subcomponents": []
10902
10912
  },
10913
+ {
10914
+ "fully_qualified_name": "Primer::Alpha::TreeView::TrailingAction",
10915
+ "description": "The trailing action for `TreeView` nodes.\n\nThis component is part of the {{#link_to_component}}Primer::Alpha::TreeView{{/link_to_component}} component and should\nnot be used directly.",
10916
+ "accessibility_docs": null,
10917
+ "is_form_component": false,
10918
+ "is_published": true,
10919
+ "requires_js": false,
10920
+ "component": "TreeView::TrailingAction",
10921
+ "status": "alpha",
10922
+ "a11y_reviewed": false,
10923
+ "short_name": "TreeViewTrailingAction",
10924
+ "source": "https://github.com/primer/view_components/tree/main/app/components/primer/alpha/tree_view/trailing_action.rb",
10925
+ "lookbook": "https://primer.style/view-components/lookbook/inspect/primer/alpha/tree_view/trailing_action/default/",
10926
+ "parameters": [
10927
+ {
10928
+ "name": "action",
10929
+ "type": "ViewComponent::Base",
10930
+ "default": "N/A",
10931
+ "description": "A component or other renderable to use as the action button etc."
10932
+ }
10933
+ ],
10934
+ "slots": [],
10935
+ "methods": [],
10936
+ "previews": [],
10937
+ "subcomponents": []
10938
+ },
10903
10939
  {
10904
10940
  "fully_qualified_name": "Primer::Alpha::TreeView::SubTreeContainer",
10905
10941
  "description": "This component is part of the {{#link_to_component}}Primer::Alpha::TreeView{{/link_to_component}} component and should\nnot be used directly.",
@@ -11240,6 +11276,11 @@
11240
11276
  "description": "Generic leading action slot",
11241
11277
  "parameters": []
11242
11278
  },
11279
+ {
11280
+ "name": "trailing_action",
11281
+ "description": "Generic trailing action slot",
11282
+ "parameters": []
11283
+ },
11243
11284
  {
11244
11285
  "name": "leading_visual",
11245
11286
  "description": "Generic leading visual slot",
@@ -11545,6 +11586,11 @@
11545
11586
  "description": null,
11546
11587
  "parameters": []
11547
11588
  },
11589
+ {
11590
+ "name": "trailing_action",
11591
+ "description": null,
11592
+ "parameters": []
11593
+ },
11548
11594
  {
11549
11595
  "name": "trailing_visual",
11550
11596
  "description": null,
@@ -11584,6 +11630,19 @@
11584
11630
  ],
11585
11631
  "return_types": []
11586
11632
  },
11633
+ {
11634
+ "name": "with_trailing_action_button",
11635
+ "description": "Adds a trailing action rendered to the right of the node's content.",
11636
+ "parameters": [
11637
+ {
11638
+ "name": "system_arguments",
11639
+ "type": "Hash",
11640
+ "default": "N/A",
11641
+ "description": "The arguments accepted by {{#link_to_component}}Primer::Beta::IconButton{{/link_to_component}}."
11642
+ }
11643
+ ],
11644
+ "return_types": []
11645
+ },
11587
11646
  {
11588
11647
  "name": "with_trailing_visual_icon",
11589
11648
  "description": "Adds a trailing visual icon rendered to the right of the node's label.",
@@ -11675,6 +11734,11 @@
11675
11734
  "description": null,
11676
11735
  "parameters": []
11677
11736
  },
11737
+ {
11738
+ "name": "trailing_action",
11739
+ "description": null,
11740
+ "parameters": []
11741
+ },
11678
11742
  {
11679
11743
  "name": "trailing_visual",
11680
11744
  "description": null,
@@ -11733,6 +11797,19 @@
11733
11797
  ],
11734
11798
  "return_types": []
11735
11799
  },
11800
+ {
11801
+ "name": "with_trailing_action_button",
11802
+ "description": "Adds a trailing action rendered to the right of the node's content.",
11803
+ "parameters": [
11804
+ {
11805
+ "name": "system_arguments",
11806
+ "type": "Hash",
11807
+ "default": "N/A",
11808
+ "description": "The arguments accepted by {{#link_to_component}}Primer::Beta::IconButton{{/link_to_component}}."
11809
+ }
11810
+ ],
11811
+ "return_types": []
11812
+ },
11736
11813
  {
11737
11814
  "name": "with_trailing_visual_icon",
11738
11815
  "description": "Adds a trailing visual icon rendered to the right of the node's label.",
@@ -20273,7 +20350,7 @@
20273
20350
  },
20274
20351
  {
20275
20352
  "fully_qualified_name": "Primer::OpenProject::FilterableTreeView",
20276
- "description": "A TreeView and associated filter controls for searching nested hierarchies.\n\n## Filter controls\n\n`FilterableTreeView`s can be filtered using two controls, both present in the toolbar above the tree:\n\n1. A free-form query string from a text input field.\n2. A `SegmentedControl` with two options (by default):\n 1. The \"Selected\" option causes the component to only show checked nodes, provided they also satisfy the other\n filter criteria described here.\n 2. The \"All\" option causes the component to show all nodes, provided they also satisfy the other filter\n criteria described here.\n\n## Custom filter modes\n\nIn addition to the default filter modes of `'all'` and `'selected'` described above, `FilterableTreeView` supports\nadding custom filter modes. Adding a filter mode will cause its label to appear in the `SegmentedControl` in the\ntoolbar, and will be passed as the third argument to the filter function (see below).\n\nHere's how to add a custom filter mode in addition to the default ones:\n\n```erb\n<%= render(Primer::OpenProject::FilterableTreeView.new) do |tree_view| %>\n <%# remove this line to prevent adding the default modes %>\n <% tree_view.with_default_filter_modes %>\n <% tree_view.with_filter_mode(name: \"Custom\", system_arguments)\n<% end %>\n```\n\n## Filter behavior\n\nBy default, matching node text is identified by looking for an exact substring match, operating on a lowercased\nversion of both the query string and the node text. For more information, and to provide a customized filter\nfunction, please see the section titled \"Customizing the filter function\" below.\n\nNodes that match the filter appear as normal; nodes that do not match are presented as follows:\n\n1. Leaf nodes are hidden.\n2. Sub-tree nodes with no matching children are hidden.\n3. Sub-tree nodes with at least one matching child are disabled but still visible.\n\n## Checking behavior\n\nBy default, checking a node in a `FilterableTreeView` checks only that node (i.e. no child nodes are checked).\nTo aide in checking children in deeply nested or highly populated hierarchies, a third control exists in the\ntoolbar: the \"Include sub-items\" check box. If this feature is turned on, checking sub-tree nodes causes all\nchildren, both leaf and sub-tree nodes, to also be checked recursively. Moreover, turning this feature on will\ncause the children of any previously checked nodes to be checked recursively. Unchecking a node while in\n\"Include sub-items\" mode will restore that sub-tree and all its children to their previously checked state, so as\nnot to permanently override a user's selections. Unchecking the \"Include sub-items\" check box has a similar effect,\ni.e. restores all previous user selections under currently checked sub-trees.\n\n## JavaScript API\n\n`FilterableTreeView` does not yet have an extensive JavaScript API, but this may change in the future as the\ncomponent is further developed to fit additional use-cases.\n\n### Customizing the filter function\n\nThe filter function can be customized by setting the value of the `filterFn` property to a function with the\nfollowing signature:\n\n```typescript\nexport type FilterFn = (node: HTMLElement, query: string, filterMode?: string) => Range[] | null\n```\n\nThis function will be called once for each node in the tree every time filter controls change (i.e. when the\nfilter mode or query string are altered). The function is called with the following arguments:\n\n|Argument |Description |\n|:-----------|:----------------------------------------------------------------|\n|`node` |The HTML node element, i.e. the element with `role=treeitem` set.|\n|`query` |The query string. |\n|`filterMode`|The filter mode, either `'all'` or `'selected'`. |\n\nThe component expects the filter function to return specific values depending on the type of match:\n\n1. No match - return `null`\n2. Match but no highlights (eg. when the query string is empty) - return an empty array\n3. Match with highlights - return a non-empty array of `Range` objects\n\nExample:\n\n```javascript\nconst filterableTreeView = document.querySelector('filterable-tree-view')\nfilterableTreeView.filterFn = (node, query, filterMode) => {\n // custom filter implementation here\n}\n```\n\nYou can read about `Range` objects here: https://developer.mozilla.org/en-US/docs/Web/API/Range.\n\nFor a complete example demonstrating how to implement a working filter function complete with range highlighting,\nsee the default filter function available in the `FilterableTreeViewElement` JavaScript class, which is part of\nthe Primer source code.\n\n### Events\n\nCurrently `FilterableTreeView` does not emit any events aside from the events already emitted by the `TreeView`\ncomponent.",
20353
+ "description": "A TreeView and associated filter controls for searching nested hierarchies.\n\n## Filter controls\n\n`FilterableTreeView`s can be filtered using two controls, both present in the toolbar above the tree:\n\n1. A free-form query string from a text input field.\n2. A `SegmentedControl` with two options (by default):\n 1. The \"Selected\" option causes the component to only show checked nodes, provided they also satisfy the other\n filter criteria described here.\n 2. The \"All\" option causes the component to show all nodes, provided they also satisfy the other filter\n criteria described here.\n\n## Custom filter modes\n\nIn addition to the default filter modes of `'all'` and `'selected'` described above, `FilterableTreeView` supports\nadding custom filter modes. Adding a filter mode will cause its label to appear in the `SegmentedControl` in the\ntoolbar, and will be passed as the third argument to the filter function (see below).\n\nHere's how to add a custom filter mode in addition to the default ones:\n\n```erb\n<%= render(Primer::OpenProject::FilterableTreeView.new) do |tree_view| %>\n <%# remove this line to prevent adding the default modes %>\n <% tree_view.with_default_filter_modes %>\n <% tree_view.with_filter_mode(name: \"Custom\", system_arguments)\n<% end %>\n```\n\n## Filter behavior\n\nBy default, matching node text is identified by looking for an exact substring match, operating on a lowercased\nversion of both the query string and the node text. For more information, and to provide a customized filter\nfunction, please see the section titled \"Customizing the filter function\" below.\n\nNodes that match the filter appear as normal; nodes that do not match are presented as follows:\n\n1. Leaf nodes are hidden.\n2. Sub-tree nodes with no matching children are hidden.\n3. Sub-tree nodes with at least one matching child are disabled but still visible.\n\n## Checking behavior\n\nBy default, checking a node in a `FilterableTreeView` checks only that node (i.e. no child nodes are checked).\nTo aide in checking children in deeply nested or highly populated hierarchies, a third control exists in the\ntoolbar: the \"Include sub-items\" check box. If this feature is turned on, checking sub-tree nodes causes all\nchildren, both leaf and sub-tree nodes, to also be checked recursively. Moreover, turning this feature on will\ncause the children of any previously checked nodes to be checked recursively. Unchecking a node while in\n\"Include sub-items\" mode will restore that sub-tree and all its children to their previously checked state, so as\nnot to permanently override a user's selections. Unchecking the \"Include sub-items\" check box has a similar effect,\ni.e. restores all previous user selections under currently checked sub-trees.\n\n## JavaScript API\n\n`FilterableTreeView` does not yet have an extensive JavaScript API, but this may change in the future as the\ncomponent is further developed to fit additional use-cases.\n\n### Customizing the filter function\n\nThe filter function can be customized by setting the value of the `filterFn` property to a function with the\nfollowing signature:\n\n```typescript\nexport type FilterFn = (node: HTMLElement, query: string, filterMode?: string) => Range[] | null\n```\n\nThis function will be called once for each node in the tree every time filter controls change (i.e. when the\nfilter mode or query string are altered). The function is called with the following arguments:\n\n|Argument |Description |\n|:-----------|:----------------------------------------------------------------|\n|`node` |The HTML node element, i.e. the element with `role=treeitem` set.|\n|`query` |The query string. |\n|`filterMode`|The filter mode, either `'all'` or `'selected'`. |\n\nThe component expects the filter function to return specific values depending on the type of match:\n\n1. No match - return `null`\n2. Match but no highlights (eg. when the query string is empty) - return an empty array\n3. Match with highlights - return a non-empty array of `Range` objects\n\nExample:\n\n```javascript\nconst filterableTreeView = document.querySelector('filterable-tree-view')\nfilterableTreeView.filterFn = (node, query, filterMode) => {\n // custom filter implementation here\n}\n```\n\nYou can read about `Range` objects here: https://developer.mozilla.org/en-US/docs/Web/API/Range.\n\nFor a complete example demonstrating how to implement a working filter function complete with range highlighting,\nsee the default filter function available in the `FilterableTreeViewElement` JavaScript class, which is part of\nthe Primer source code.\n\n### Events\n\nCurrently `FilterableTreeView` does not emit any events aside from the events already emitted by the `TreeView`\ncomponent.\n\n## Async loading strategy\n\nWhen `src` is set on the component, all filter interactions (text input, filter mode changes) trigger a debounced\nserver request instead of client-side filtering. The server is responsible for returning a filtered `<tree-view>`\nHTML fragment that replaces the current tree.\n\n### Behavior\n\n- The full tree is loaded initially from the server via `src`.\n- Each filter input event triggers a debounced (300 ms) request to the server.\n- The server returns a filtered `<tree-view>` element which replaces the existing one.\n- All matching results and their full ancestor hierarchy are expanded automatically.\n- Matching text is highlighted using the CSS Custom Highlight API (or `<mark>` fallback).\n- When the filter is cleared, the tree is replaced with the full (unfiltered) result from\n the server and the expansion state from before the search is restored.\n- Checked nodes are preserved across tree replacements using `data-node-id` attributes.\n- When \"include sub-items\" is active and the tree is filtered, clicking a parent node\n selects ALL its descendants (not just the visible filtered ones). Therefore, \"include_sub_items\" is passed\n to the server, since it holds the only truth about the data.\n\n### Server endpoint\n\nThe server endpoint must return a `<tree-view>` HTML fragment. Each node must have a stable\n`data-node-id` on its `[role=treeitem]` element.\n\n### Usage\n\n```erb\n<%= render(Primer::OpenProject::FilterableTreeView.new(\n src: my_path\n)) %>\n```",
20277
20354
  "accessibility_docs": null,
20278
20355
  "is_form_component": false,
20279
20356
  "is_published": true,
@@ -20285,6 +20362,12 @@
20285
20362
  "source": "https://github.com/primer/view_components/tree/main/app/components/primer/open_project/filterable_tree_view.rb",
20286
20363
  "lookbook": "https://primer.style/view-components/lookbook/inspect/primer/open_project/filterable_tree_view/default/",
20287
20364
  "parameters": [
20365
+ {
20366
+ "name": "src",
20367
+ "type": "String",
20368
+ "default": "`nil`",
20369
+ "description": "URL of the server endpoint that returns a filtered `<tree-view>` HTML fragment. When set, activates async (server-side) filtering mode. See \"Async loading strategy\" above."
20370
+ },
20288
20371
  {
20289
20372
  "name": "tree_view_arguments",
20290
20373
  "type": "Hash",
@@ -20403,6 +20486,45 @@
20403
20486
  ]
20404
20487
  }
20405
20488
  },
20489
+ {
20490
+ "preview_path": "primer/open_project/filterable_tree_view/async",
20491
+ "name": "async",
20492
+ "snapshot": "false",
20493
+ "skip_rules": {
20494
+ "wont_fix": [
20495
+ "region"
20496
+ ],
20497
+ "will_fix": [
20498
+ "color-contrast"
20499
+ ]
20500
+ }
20501
+ },
20502
+ {
20503
+ "preview_path": "primer/open_project/filterable_tree_view/async_form_input",
20504
+ "name": "async_form_input",
20505
+ "snapshot": "false",
20506
+ "skip_rules": {
20507
+ "wont_fix": [
20508
+ "region"
20509
+ ],
20510
+ "will_fix": [
20511
+ "color-contrast"
20512
+ ]
20513
+ }
20514
+ },
20515
+ {
20516
+ "preview_path": "primer/open_project/filterable_tree_view/link_nodes",
20517
+ "name": "link_nodes",
20518
+ "snapshot": "false",
20519
+ "skip_rules": {
20520
+ "wont_fix": [
20521
+ "region"
20522
+ ],
20523
+ "will_fix": [
20524
+ "color-contrast"
20525
+ ]
20526
+ }
20527
+ },
20406
20528
  {
20407
20529
  "preview_path": "primer/open_project/filterable_tree_view/hide_checkbox",
20408
20530
  "name": "hide_checkbox",
@@ -21649,6 +21771,12 @@
21649
21771
  "source": "https://github.com/primer/view_components/tree/main/app/components/primer/open_project/sub_header.rb",
21650
21772
  "lookbook": "https://primer.style/view-components/lookbook/inspect/primer/open_project/sub_header/default/",
21651
21773
  "parameters": [
21774
+ {
21775
+ "name": "collapsed_search",
21776
+ "type": "Boolean",
21777
+ "default": "`false`",
21778
+ "description": "When true, the search bar starts collapsed as an icon button on all screen sizes. Clicking expands it."
21779
+ },
21652
21780
  {
21653
21781
  "name": "system_arguments",
21654
21782
  "type": "Hash",
@@ -21672,6 +21800,11 @@
21672
21800
  "description": "A button or custom content that will render on the left-hand side of the component, next to the filter input.\n\nTo render a button, call the `with_filter_button` method, which accepts the arguments accepted by {{#link_to_component}}Primer::Beta::Button{{/link_to_component}}.\n\nTo render custom content, call the `with_filter_component` method and pass a block that returns HTML.",
21673
21801
  "parameters": []
21674
21802
  },
21803
+ {
21804
+ "name": "quick_filters",
21805
+ "description": "Quick filters shown in the left pane next to the search bar (0–5 items).\nHidden on mobile. Requires all_filters_button to be set when used.\nSupports ActionMenus, Buttons, IconButtons, SelectPanels, and SegmentedControls inside the block.",
21806
+ "parameters": []
21807
+ },
21675
21808
  {
21676
21809
  "name": "segmented_control",
21677
21810
  "description": null,
@@ -21781,6 +21914,32 @@
21781
21914
  ]
21782
21915
  }
21783
21916
  },
21917
+ {
21918
+ "preview_path": "primer/open_project/sub_header/collapsed_search",
21919
+ "name": "collapsed_search",
21920
+ "snapshot": "false",
21921
+ "skip_rules": {
21922
+ "wont_fix": [
21923
+ "region"
21924
+ ],
21925
+ "will_fix": [
21926
+ "color-contrast"
21927
+ ]
21928
+ }
21929
+ },
21930
+ {
21931
+ "preview_path": "primer/open_project/sub_header/quick_filters",
21932
+ "name": "quick_filters",
21933
+ "snapshot": "false",
21934
+ "skip_rules": {
21935
+ "wont_fix": [
21936
+ "region"
21937
+ ],
21938
+ "will_fix": [
21939
+ "color-contrast"
21940
+ ]
21941
+ }
21942
+ },
21784
21943
  {
21785
21944
  "preview_path": "primer/open_project/sub_header/bottom_pane",
21786
21945
  "name": "bottom_pane",