playbook_ui 17.1.0 → 17.2.0.pre.rc.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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +32 -0
  3. data/app/pb_kits/playbook/pb_file_upload/_file_upload.scss +5 -0
  4. data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_custom_message.html.erb +32 -0
  5. data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_custom_message_rails.md +3 -0
  6. data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_input_options.html.erb +3 -0
  7. data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_input_options.md +1 -0
  8. data/app/pb_kits/playbook/pb_file_upload/docs/_playground.json +1 -0
  9. data/app/pb_kits/playbook/pb_file_upload/docs/example.yml +2 -0
  10. data/app/pb_kits/playbook/pb_file_upload/file_upload.html.erb +14 -5
  11. data/app/pb_kits/playbook/pb_file_upload/file_upload.rb +3 -0
  12. data/app/pb_kits/playbook/pb_file_upload/kit.schema.json +4 -2
  13. data/app/pb_kits/playbook/pb_icon/docs/_icon_css_api.html.erb +3 -0
  14. data/app/pb_kits/playbook/pb_icon/docs/_icon_css_api.jsx +13 -0
  15. data/app/pb_kits/playbook/pb_icon/docs/_icon_css_api.md +1 -0
  16. data/app/pb_kits/playbook/pb_icon/docs/example.yml +2 -0
  17. data/app/pb_kits/playbook/pb_icon/docs/index.js +1 -0
  18. data/app/pb_kits/playbook/pb_link/_link.tsx +12 -2
  19. data/app/pb_kits/playbook/pb_link/docs/_link_rel.html.erb +16 -0
  20. data/app/pb_kits/playbook/pb_link/docs/_link_rel.jsx +30 -0
  21. data/app/pb_kits/playbook/pb_link/docs/_link_rel.md +3 -0
  22. data/app/pb_kits/playbook/pb_link/docs/_playground.json +6 -2
  23. data/app/pb_kits/playbook/pb_link/docs/_playground.overrides.json +2 -1
  24. data/app/pb_kits/playbook/pb_link/docs/example.yml +2 -0
  25. data/app/pb_kits/playbook/pb_link/docs/index.js +2 -1
  26. data/app/pb_kits/playbook/pb_link/kit.schema.json +7 -0
  27. data/app/pb_kits/playbook/pb_link/link.html.erb +2 -2
  28. data/app/pb_kits/playbook/pb_link/link.rb +7 -0
  29. data/app/pb_kits/playbook/pb_link/link.test.jsx +29 -0
  30. data/app/pb_kits/playbook/pb_table/docs/_sections.yml +1 -0
  31. data/app/pb_kits/playbook/pb_table/docs/example.yml +11 -11
  32. data/dist/chunks/_detail-B4-co8rv.js +5 -0
  33. data/dist/chunks/{_pb_line_graph-CUJ7NJIr.js → _pb_line_graph-BGyCJjgG.js} +1 -1
  34. data/dist/chunks/_table-SGw4kUye.js +1 -0
  35. data/dist/chunks/_typeahead-DeIqOMbJ.js +1 -0
  36. data/dist/chunks/{globalProps-DDcpyjvz.js → globalProps-K1DCn3il.js} +1 -1
  37. data/dist/chunks/lib-7vm9BMqp.js +29 -0
  38. data/dist/chunks/{time_picker_helper-B_FN8E9Y.js → time_picker_helper-BZutIzB3.js} +1 -1
  39. data/dist/chunks/vendor.js +2 -2
  40. data/dist/playbook-rails-react-bindings.js +1 -1
  41. data/dist/playbook-rails.js +1 -1
  42. data/dist/playbook.css +1 -1
  43. data/lib/playbook/version.rb +2 -2
  44. metadata +46 -32
  45. data/dist/chunks/_detail-CyTyC7gJ.js +0 -5
  46. data/dist/chunks/_table-DDKdCER5.js +0 -1
  47. data/dist/chunks/_typeahead-DdVBwRQT.js +0 -1
  48. data/dist/chunks/lib-D57mygNT.js +0 -29
  49. /data/app/pb_kits/playbook/pb_table/docs/{_table_alignment_column_rails.html.erb → _table_alignment_column.html.erb} +0 -0
  50. /data/app/pb_kits/playbook/pb_table/docs/{_table_alignment_column.md → _table_alignment_column_react.md} +0 -0
  51. /data/app/pb_kits/playbook/pb_table/docs/{_table_alignment_row_rails.html.erb → _table_alignment_row.html.erb} +0 -0
  52. /data/app/pb_kits/playbook/pb_table/docs/{_table_alignment_row.md → _table_alignment_row_react.md} +0 -0
  53. /data/app/pb_kits/playbook/pb_table/docs/{_table_alignment_shift_data_rails.html.erb → _table_alignment_shift_data.html.erb} +0 -0
  54. /data/app/pb_kits/playbook/pb_table/docs/{_table_alignment_shift_data.md → _table_alignment_shift_data_react.md} +0 -0
  55. /data/app/pb_kits/playbook/pb_table/docs/{_table_alignment_shift_row_rails.html.erb → _table_alignment_shift_row.html.erb} +0 -0
  56. /data/app/pb_kits/playbook/pb_table/docs/{_table_alignment_shift_row.md → _table_alignment_shift_row_react.md} +0 -0
  57. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_collapsible_with_custom_content_rails.html.erb → _table_with_collapsible_with_custom_content.html.erb} +0 -0
  58. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_collapsible_with_custom_content.md → _table_with_collapsible_with_custom_content_react.md} +0 -0
  59. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_collapsible_with_nested_rows_rails.html.erb → _table_with_collapsible_with_nested_rows.html.erb} +0 -0
  60. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_collapsible_with_nested_rows.md → _table_with_collapsible_with_nested_rows_react.md} +0 -0
  61. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_collapsible_with_nested_table_rails.html.erb → _table_with_collapsible_with_nested_table.html.erb} +0 -0
  62. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_collapsible_with_nested_table.md → _table_with_collapsible_with_nested_table_react.md} +0 -0
  63. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_filter_variant_rails.html.erb → _table_with_filter_variant.html.erb} +0 -0
  64. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_filter_variant.md → _table_with_filter_variant_react.md} +0 -0
  65. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_filter_variant_with_pagination_rails.html.erb → _table_with_filter_variant_with_pagination.html.erb} +0 -0
  66. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_filter_variant_with_pagination.md → _table_with_filter_variant_with_pagination_react.md} +0 -0
  67. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_filter_with_card_title_props_rails.html.erb → _table_with_filter_with_card_title_props.html.erb} +0 -0
  68. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_filter_with_card_title_props.md → _table_with_filter_with_card_title_props_react.md} +0 -0
  69. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_subcomponents_rails.html.erb → _table_with_subcomponents.html.erb} +0 -0
  70. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_subcomponents.md → _table_with_subcomponents_react.md} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b282160685aeb7935309b54bddb2856ff20b4957a8ca3455a268ab52693bb8e7
4
- data.tar.gz: 31b055f1c7c84e23d99b25cb980a5ad370f53088e265308ccd4e72a0b74500ba
3
+ metadata.gz: b0cfdd29dff7aa690ffa80f529b880a467df847de2897b20978f15aee42493f9
4
+ data.tar.gz: efe2a6be9fa5b594a9ec8cf3dba4085b7bbc3b6e0ce181a60710b0e760165640
5
5
  SHA512:
6
- metadata.gz: ce34582a3e72b462fa81e781a464f4f1352aaad7a37ffbace981816b5ec899397a6c29d8aa487af44d0fe20bda2f2d8280574bbf5a0a31548eed4c8e576976d8
7
- data.tar.gz: 41690ad445b535cab92bb9a8c7a1e3c23e382b2e3661def036d7b85b16b7d2f6ec5c4419b22cdabdfc96f289231be50a4241d583e716a038a19efd82dfd63b3d
6
+ metadata.gz: d1000f4e35e8953cbeff95dba2a55f2373c594320e00fc58ac8436eded89a5eb9f5383c85600bf3d747d71148182841ab2a1c5f4124581b5b03596813ea0b353
7
+ data.tar.gz: '068ba2587e7d290077532454b72d6b6cddefa2d919834bc41329259a010b70535ab6ab0636a846973f3c436d6f6cc259a1fb455edade7989f352b15e76c3543d'
@@ -1076,6 +1076,30 @@
1076
1076
  }
1077
1077
  }
1078
1078
 
1079
+ // Firefox-specific fix for vertical borders disappearing upon row hover
1080
+ .pb_table.vertical-border .pb_advanced_table_body {
1081
+ tr:not(.virtualized-table-row) td:not(:last-child):not(.last-cell),
1082
+ .pb_table_tr:not(.virtualized-table-row) .pb_table_td:not(:last-child):not(.last-cell) {
1083
+ position: relative;
1084
+ border-right-color: transparent !important;
1085
+
1086
+ &::before {
1087
+ content: "";
1088
+ position: absolute;
1089
+ top: 0;
1090
+ right: -1px;
1091
+ bottom: -1px;
1092
+ width: 1px;
1093
+ background-color: var(--table-border-color);
1094
+ }
1095
+
1096
+ // The base kit gives the first column's divider the column group color
1097
+ &:first-child::before {
1098
+ background-color: var(--column-border-color);
1099
+ }
1100
+ }
1101
+ }
1102
+
1079
1103
  &[class*="column-group-border-"] {
1080
1104
  .pb_advanced_table_header {
1081
1105
  th[colspan]:not([colspan="1"]):not(:last-child),
@@ -1122,6 +1146,14 @@
1122
1146
  }
1123
1147
  }
1124
1148
 
1149
+ // --table-border-color stays light in dark mode, matches token of base kit's dark vertical border
1150
+ .pb_table.vertical-border .pb_advanced_table_body {
1151
+ tr:not(.virtualized-table-row) td:not(:last-child):not(.last-cell)::before,
1152
+ .pb_table_tr:not(.virtualized-table-row) .pb_table_td:not(:last-child):not(.last-cell)::before {
1153
+ background-color: $border_dark;
1154
+ }
1155
+ }
1156
+
1125
1157
  &[class*="column-group-border-"] {
1126
1158
  .pb_advanced_table_header {
1127
1159
  th[colspan]:not([colspan="1"]):not(:last-child),
@@ -18,6 +18,11 @@
18
18
  margin-top: $space_xs;
19
19
  }
20
20
  }
21
+ .pb_form_group_kit {
22
+ > .pb_button_kit.file_upload_custom_message_spacing {
23
+ margin-bottom: $space_xxs;
24
+ }
25
+ }
21
26
  }
22
27
 
23
28
  .pb_file_upload_kit_error {
@@ -0,0 +1,32 @@
1
+ <%= pb_rails("file_upload", props: {
2
+ accept: ".pdf,.xlsx",
3
+ custom_message: "Choose a file. The accepted file types are Adobe (.pdf) and Microsoft (.xlsx)",
4
+ id: "file-upload-custom-message-file-types",
5
+ }) %>
6
+
7
+ <%= pb_rails("file_upload", props: {
8
+ custom_message: "Max Size: 10MB.",
9
+ id: "file-upload-custom-message-max-size",
10
+ margin_top: "md",
11
+ }) %>
12
+
13
+ <%= javascript_tag defer: "defer" do %>
14
+ document.addEventListener("DOMContentLoaded", function () {
15
+ const fileInput = document.getElementById("upload-file-upload-custom-message-max-size")
16
+ const maxSize = 10 * 1000 * 1000
17
+
18
+ if (!fileInput) return
19
+
20
+ fileInput.addEventListener("change", function () {
21
+ const files = Array.from(fileInput.files || [])
22
+ const fileTooLarge = files.some((file) => file.size > maxSize)
23
+
24
+ if (fileTooLarge) {
25
+ fileInput.setCustomValidity("File size is too large. Maximum file size is 10MB.")
26
+ fileInput.reportValidity()
27
+ } else {
28
+ fileInput.setCustomValidity("")
29
+ }
30
+ })
31
+ })
32
+ <% end %>
@@ -0,0 +1,3 @@
1
+ Use the `custom_message` prop to provide additional information about the files that can be uploaded, such as accepted file types or maximum file size. The `custom_message` is informational only.
2
+
3
+ The first example demonstrates using it along with the `accept` prop and serves as a description of accepted files. The second example demonstrates using it as a max size warning with a script tag mimicking a client-side alert; however, when actually implemented file size limits should be enforced server-side when processing the uploaded file.
@@ -0,0 +1,3 @@
1
+ <%= pb_rails("file_upload", props: { input_options: { name: "my_file_upload"}}) %>
2
+
3
+
@@ -0,0 +1 @@
1
+ Use `input_options` to access the File Upload input directly. Common implemenation patterns use this to add a `name` prop directly to the input, among other props.
@@ -3,6 +3,7 @@
3
3
  "propTargets": {},
4
4
  "defaults": {
5
5
  "accept": {},
6
+ "customMessage": "",
6
7
  "dark": false
7
8
  },
8
9
  "groups": [
@@ -3,8 +3,10 @@ examples:
3
3
  rails:
4
4
  - file_upload_default: File Upload
5
5
  - file_upload_custom: Custom
6
+ - file_upload_custom_message: Add a custom message
6
7
  - file_upload_error: Error
7
8
  - file_upload_remove_replace: Remove and Replace
9
+ - file_upload_input_options: Input Options
8
10
 
9
11
  react:
10
12
  - file_upload_default: Default List of files to upload
@@ -1,8 +1,8 @@
1
1
  <%= pb_content_tag(:div) do %>
2
- <%= pb_rails("form_group", props: {cursor: "pointer", full_width: object.full_width}) do %>
2
+ <%= pb_rails("form_group", props: {cursor: "pointer", full_width: object.full_width, margin_bottom: object.custom_message.present? ? "none" : nil}) do %>
3
3
  <label
4
4
  for="upload-<%= object.id %>"
5
- class="pb_button_kit pb_button_secondary pb_button_inline pb_button_enabled <%= 'dark' if object.dark %>"
5
+ class="pb_button_kit pb_button_secondary pb_button_inline pb_button_enabled <%= 'dark' if object.dark %> <%= 'file_upload_custom_message_spacing' if object.custom_message.present? %>"
6
6
  >
7
7
  <%= "#{object.label}" %>
8
8
  </label>
@@ -10,11 +10,20 @@
10
10
  type: "file",
11
11
  dark: object.dark,
12
12
  error: object.error.presence,
13
+ margin_bottom: object.custom_message.present? ? "xxs" : nil,
13
14
  input_options: {
15
+ accept: object.accept.presence,
14
16
  id: "upload-#{object.id}",
15
- classname: "cursor_pointer",
16
- }.merge(object.input_options)
17
+ classname: "cursor_pointer ",
18
+ }.compact.merge(object.input_options)
19
+ }) %>
20
+ <% end %>
21
+ <% if object.custom_message.present? %>
22
+ <%= pb_rails("body", props: {
23
+ text: object.custom_message,
24
+ dark: object.dark,
25
+ color: "lighter",
26
+ classname: "file_upload_custom_message"
17
27
  }) %>
18
28
  <% end %>
19
- </label>
20
29
  <% end %>
@@ -23,6 +23,9 @@ module Playbook
23
23
 
24
24
  prop :error, type: Playbook::Props::String
25
25
 
26
+ prop :custom_message, type: Playbook::Props::String,
27
+ default: ""
28
+
26
29
  def classname
27
30
  file_upload_class = generate_classname("pb_file_upload_kit")
28
31
  file_upload_class + error_class + full_width_class
@@ -18,8 +18,10 @@
18
18
  "customMessage": {
19
19
  "type": "string",
20
20
  "platforms": [
21
- "react"
22
- ]
21
+ "react",
22
+ "rails"
23
+ ],
24
+ "default": ""
23
25
  },
24
26
  "dark": {
25
27
  "type": "boolean",
@@ -0,0 +1,3 @@
1
+ <div>
2
+ <span class="pb-icon-sparkles" aria-hidden="true"></span>
3
+ </div>
@@ -0,0 +1,13 @@
1
+ import React from "react"
2
+
3
+ const IconCssApi = () => {
4
+ return (
5
+ <div>
6
+ <span aria-hidden="true"
7
+ className="pb-icon-sparkles"
8
+ />
9
+ </div>
10
+ )
11
+ }
12
+
13
+ export default IconCssApi
@@ -0,0 +1 @@
1
+ Every icon is available as a CSS class enabling them to be used anywhere without importing the Icon component. The pattern needed is `pb-icon-{icon-name}` (e.g. pb-icon-asterisk). Props for size and color are not available here; size can be set with the --pb-icon-size custom property and color can be set with the CSS color property. if you need an icon to animate, a utility class should be used alongside it, e.g. `pb-icon-spin`. For a full reference, see the [Icons page](https://playbook.powerapp.cloud/icons).
@@ -9,6 +9,7 @@ examples:
9
9
  - icon_sizes: Icon Sizes
10
10
  - icon_custom: Icon Custom
11
11
  - icon_color: Icon Color
12
+ - icon_css_api: Icon CSS API
12
13
 
13
14
  react:
14
15
  - icon_default: Icon Default
@@ -20,6 +21,7 @@ examples:
20
21
  - icon_sizes: Icon Sizes
21
22
  - icon_custom: Icon Custom
22
23
  - icon_color: Icon Color
24
+ - icon_css_api: Icon CSS API
23
25
 
24
26
  swift:
25
27
  - icon_default_swift: Icon Default
@@ -7,3 +7,4 @@ export { default as IconBorder } from './_icon_border.jsx'
7
7
  export { default as IconSizes } from './_icon_sizes.jsx'
8
8
  export { default as IconCustom } from './_icon_custom.jsx'
9
9
  export { default as IconColor } from './_icon_color.jsx'
10
+ export { default as IconCssApi } from './_icon_css_api.jsx'
@@ -19,6 +19,7 @@ type LinkProps = {
19
19
  icon?: string,
20
20
  iconRight?: string,
21
21
  id?: string,
22
+ rel?: string,
22
23
  tabIndex?: number,
23
24
  tag?: 'a' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'div',
24
25
  target?: string,
@@ -39,6 +40,7 @@ const Link = (props: LinkProps): React.ReactElement => {
39
40
  icon = '',
40
41
  iconRight = '',
41
42
  id = '',
43
+ rel,
42
44
  tabIndex,
43
45
  tag = 'a',
44
46
  target = '',
@@ -64,6 +66,14 @@ const Link = (props: LinkProps): React.ReactElement => {
64
66
  return undefined
65
67
  }
66
68
 
69
+ const getRelAttribute = () => {
70
+ if (rel !== undefined) {
71
+ return rel
72
+ }
73
+
74
+ return target !== 'child' ? 'noreferrer' : undefined
75
+ }
76
+
67
77
  const renderContent = () => (
68
78
  <>
69
79
  {icon && (
@@ -99,7 +109,7 @@ const Link = (props: LinkProps): React.ReactElement => {
99
109
  <a
100
110
  {...commonProps}
101
111
  href={href}
102
- rel={target !== 'child' ? 'noreferrer' : undefined}
112
+ rel={getRelAttribute()}
103
113
  tabIndex={tabIndex}
104
114
  target={getTargetAttribute()}
105
115
  >
@@ -111,7 +121,7 @@ const Link = (props: LinkProps): React.ReactElement => {
111
121
  <a
112
122
  {...commonProps}
113
123
  href={href}
114
- rel={target !== 'child' ? 'noreferrer' : undefined}
124
+ rel={getRelAttribute()}
115
125
  tabIndex={tabIndex}
116
126
  target={getTargetAttribute()}
117
127
  >
@@ -0,0 +1,16 @@
1
+ <div>
2
+ <%= pb_rails("link", props: {
3
+ href: "https://playbook.powerapp.cloud/",
4
+ target: "playbookDocs",
5
+ text: "Named Tab (default rel)"
6
+ }) %>
7
+ </div>
8
+
9
+ <div>
10
+ <%= pb_rails("link", props: {
11
+ href: "https://playbook.powerapp.cloud/",
12
+ rel: "",
13
+ target: "playbookDocs",
14
+ text: "Named Tab (rel override, reusable)"
15
+ }) %>
16
+ </div>
@@ -0,0 +1,30 @@
1
+ import React from 'react'
2
+ import Link from '../../pb_link/_link'
3
+
4
+ const LinkRel = (props) => (
5
+ <div>
6
+ <div>
7
+ <Link
8
+ aria={{ label: 'Link to Playbook in a named tab, opens a new tab on every click' }}
9
+ href="https://playbook.powerapp.cloud/"
10
+ tabIndex={0}
11
+ target="playbookDocs"
12
+ text="Named Tab (default rel)"
13
+ {...props}
14
+ />
15
+ </div>
16
+ <div>
17
+ <Link
18
+ aria={{ label: 'Link to Playbook in a named tab, reused on repeat clicks' }}
19
+ href="https://playbook.powerapp.cloud/"
20
+ rel=""
21
+ tabIndex={0}
22
+ target="playbookDocs"
23
+ text="Named Tab (rel override, reusable)"
24
+ {...props}
25
+ />
26
+ </div>
27
+ </div>
28
+ )
29
+
30
+ export default LinkRel
@@ -0,0 +1,3 @@
1
+ By default, `Link` sets `rel="noreferrer"` on any `target` other than `'child'`. This is a secure default — `noreferrer` implies `noopener`, which blocks the opened page from accessing `window.opener` — but it also prevents the browser from reusing a previously opened named tab: a browsing context opened with `noopener` isn't discoverable by name on later clicks, so every click opens a brand-new tab instead of navigating the existing one.
2
+
3
+ Pass `rel=""` to opt out of the default and get a reusable named tab, the same way `target="child"` already behaves. Only do this for targets you control and trust (e.g. another view within the same app) — for links to third-party or untrusted destinations, leave `rel` unset and keep the secure default.
@@ -31,7 +31,8 @@
31
31
  "href",
32
32
  "target",
33
33
  "disabled",
34
- "tabIndex"
34
+ "tabIndex",
35
+ "rel"
35
36
  ]
36
37
  },
37
38
  {
@@ -167,5 +168,8 @@
167
168
  "message": "Use tag to borrow link styling on semantic headings or inline text, but keep href when the element should still navigate.",
168
169
  "type": "info"
169
170
  }
170
- }
171
+ },
172
+ "emitEmptyStringProps": [
173
+ "rel"
174
+ ]
171
175
  }
@@ -2,6 +2,7 @@
2
2
  "children": {
3
3
  "default": "Link text"
4
4
  },
5
+ "emitEmptyStringProps": ["rel"],
5
6
  "defaults": {
6
7
  "color": "default",
7
8
  "disabled": false,
@@ -17,7 +18,7 @@
17
18
  },
18
19
  {
19
20
  "name": "Behavior",
20
- "props": ["href", "target", "disabled", "tabIndex"]
21
+ "props": ["href", "target", "disabled", "tabIndex", "rel"]
21
22
  },
22
23
  {
23
24
  "name": "Appearance",
@@ -7,6 +7,7 @@ examples:
7
7
  - link_disabled: Disabled
8
8
  - link_tag: Tag
9
9
  - link_target: Target
10
+ - link_rel: Rel
10
11
 
11
12
 
12
13
  react:
@@ -16,3 +17,4 @@ examples:
16
17
  - link_disabled: Disabled
17
18
  - link_tag: Tag
18
19
  - link_target: Target
20
+ - link_rel: Rel
@@ -3,4 +3,5 @@ export { default as LinkUnderline } from './_link_underline.jsx'
3
3
  export { default as LinkIcon } from './_link_icon.jsx'
4
4
  export { default as LinkDisabled } from './_link_disabled.jsx'
5
5
  export { default as LinkTag } from './_link_tag.jsx'
6
- export { default as LinkTarget } from './_link_target.jsx'
6
+ export { default as LinkTarget } from './_link_target.jsx'
7
+ export { default as LinkRel } from './_link_rel.jsx'
@@ -56,6 +56,13 @@
56
56
  "rails"
57
57
  ]
58
58
  },
59
+ "rel": {
60
+ "type": "string",
61
+ "platforms": [
62
+ "react",
63
+ "rails"
64
+ ]
65
+ },
59
66
  "tabIndex": {
60
67
  "type": "number",
61
68
  "platforms": [
@@ -9,11 +9,11 @@
9
9
  <% end %>
10
10
 
11
11
  <% if object.tag == "a" %>
12
- <%= pb_content_tag(object.tag, { href: object.href, target: object.target }) do %>
12
+ <%= pb_content_tag(object.tag, { href: object.href, rel: object.rel_attribute, target: object.target }) do %>
13
13
  <%= link_content.call %>
14
14
  <% end %>
15
15
  <% else %>
16
- <%= pb_content_tag(:a, { href: object.href }) do %>
16
+ <%= pb_content_tag(:a, { href: object.href, rel: object.rel_attribute }) do %>
17
17
  <%= content_tag(object.tag) do %>
18
18
  <%= link_content.call %>
19
19
  <% end %>
@@ -11,6 +11,7 @@ module Playbook
11
11
  prop :href
12
12
  prop :icon
13
13
  prop :icon_right
14
+ prop :rel
14
15
  prop :tabindex
15
16
  prop :tag, type: Playbook::Props::Enum,
16
17
  values: %w[a h1 h2 h3 h4 h5 h6 p span div],
@@ -32,6 +33,12 @@ module Playbook
32
33
  target if target && href
33
34
  end
34
35
 
36
+ def rel_attribute
37
+ return rel unless rel.nil?
38
+
39
+ target == "child" ? nil : "noreferrer"
40
+ end
41
+
35
42
  private
36
43
 
37
44
  def color_class
@@ -120,3 +120,32 @@ test('should render child target prop', () => {
120
120
 
121
121
  expect(kit).toHaveAttribute('target', 'child')
122
122
  })
123
+
124
+ test('defaults to noreferrer for a named target', () => {
125
+ render(
126
+ <Link
127
+ data={{ testid: 'rel-default-test' }}
128
+ href="https://playbook.powerapp.cloud/"
129
+ target="namedTab"
130
+ />
131
+ )
132
+
133
+ const kit = screen.getByTestId('rel-default-test')
134
+
135
+ expect(kit).toHaveAttribute('rel', 'noreferrer')
136
+ })
137
+
138
+ test('rel prop overrides the default noreferrer', () => {
139
+ render(
140
+ <Link
141
+ data={{ testid: 'rel-override-test' }}
142
+ href="https://playbook.powerapp.cloud/"
143
+ rel=""
144
+ target="namedTab"
145
+ />
146
+ )
147
+
148
+ const kit = screen.getByTestId('rel-override-test')
149
+
150
+ expect(kit).toHaveAttribute('rel', '')
151
+ })
@@ -59,6 +59,7 @@ sections:
59
59
  - table_with_filter_variant_external_filter_rails
60
60
  - table_with_filter_variant_with_pagination
61
61
  - table_disable_hover
62
+ - table_with_filter_with_card_title_props
62
63
 
63
64
  - title: "Table Actions"
64
65
  examples:
@@ -8,10 +8,10 @@ examples:
8
8
  - table_sticky_right_columns: Sticky Right Column
9
9
  - table_sticky_columns: Sticky Left and Right Columns
10
10
  - table_header: Table Header
11
- - table_alignment_row_rails: Row Alignment
12
- - table_alignment_column_rails: Cell Alignment
13
- - table_alignment_shift_row_rails: Row Shift
14
- - table_alignment_shift_data_rails: Cell Shift
11
+ - table_alignment_row: Row Alignment
12
+ - table_alignment_column: Cell Alignment
13
+ - table_alignment_shift_row: Row Shift
14
+ - table_alignment_shift_data: Cell Shift
15
15
  - table_side_highlight: Side Highlight
16
16
  - table_container: Table Container Off
17
17
  - table_disable_hover: Disable Hover
@@ -28,23 +28,23 @@ examples:
28
28
  - table_vertical_border: Vertical Borders
29
29
  - table_contrast_border: Contrast Border
30
30
  - table_striped: Striped Table
31
- - table_with_subcomponents_rails: Table with Sub Components (Table Elements)
31
+ - table_with_subcomponents: Table with Sub Components (Table Elements)
32
32
  - table_with_subcomponents_as_divs: Table with Sub Components (Divs)
33
33
  - table_outer_padding: Outer Padding
34
34
  - table_with_collapsible: Table with Collapsible
35
35
  - table_with_dynamic_collapsible: Table with Dynamic Collapsible
36
36
  - table_with_collapsible_with_custom_click: Table with Collapsible with Custom Click
37
- - table_with_collapsible_with_custom_content_rails: Table with Collapsible with Custom Content
38
- - table_with_collapsible_with_nested_rows_rails: Table with Collapsible with Nested Rows
39
- - table_with_collapsible_with_nested_table_rails: Table with Collapsible with Nested Table
37
+ - table_with_collapsible_with_custom_content: Table with Collapsible with Custom Content
38
+ - table_with_collapsible_with_nested_rows: Table with Collapsible with Nested Rows
39
+ - table_with_collapsible_with_nested_table: Table with Collapsible with Nested Table
40
40
  - table_with_clickable_rows: Table with Clickable Rows
41
41
  - table_with_selectable_rows: Table with Selectable Rows
42
42
  - table_with_header_style_borderless: Header Style Borderless
43
43
  - table_with_header_style_floating: Header Style Floating
44
- - table_with_filter_variant_rails: Variant with Filter
44
+ - table_with_filter_variant: Variant with Filter
45
45
  - table_with_filter_variant_external_filter_rails: Variant with Filter (External Filter)
46
- - table_with_filter_variant_with_pagination_rails: Variant with Filter and Pagination
47
- - table_with_filter_with_card_title_props_rails: Variant with Filter (with Card and Title Props)
46
+ - table_with_filter_variant_with_pagination: Variant with Filter and Pagination
47
+ - table_with_filter_with_card_title_props: Variant with Filter (with Card and Title Props)
48
48
 
49
49
  react:
50
50
  - table_sm: Small