playbook_ui 17.0.0 → 17.1.0.pre.rc.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_button/docs/_button_reaction.html.erb +1 -1
  3. data/app/pb_kits/playbook/pb_button/docs/_button_reaction.jsx +1 -1
  4. data/app/pb_kits/playbook/pb_button/docs/_button_reaction.md +1 -1
  5. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_error.html.erb +1 -1
  6. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_error.jsx +1 -4
  7. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_error.html.erb +1 -1
  8. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_error.jsx +1 -4
  9. data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_error.html.erb +1 -1
  10. data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_error.jsx +1 -6
  11. data/app/pb_kits/playbook/pb_icon/docs/_icon_custom.html.erb +6 -7
  12. data/app/pb_kits/playbook/pb_icon/docs/_icon_custom.md +1 -1
  13. data/app/pb_kits/playbook/pb_icon/icon.html.erb +1 -1
  14. data/app/pb_kits/playbook/pb_icon/icon.rb +41 -17
  15. data/app/pb_kits/playbook/pb_icon_circle/docs/_icon_circle_emoji.html.erb +3 -3
  16. data/app/pb_kits/playbook/pb_icon_circle/docs/_icon_circle_emoji.jsx +2 -2
  17. data/app/pb_kits/playbook/pb_icon_circle/docs/_icon_circle_emoji.md +1 -1
  18. data/app/pb_kits/playbook/pb_icon_circle/icon_circle.test.js +1 -1
  19. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.jsx +1 -2
  20. data/app/pb_kits/playbook/pb_select/_select.scss +6 -0
  21. data/app/pb_kits/playbook/pb_select/docs/_select_error.html.erb +1 -1
  22. data/app/pb_kits/playbook/pb_select/docs/_select_error.jsx +2 -7
  23. data/app/pb_kits/playbook/pb_selectable_card_icon/docs/_selectable_card_icon_custom.html.erb +3 -3
  24. data/app/pb_kits/playbook/pb_selectable_card_icon/docs/_selectable_card_icon_custom.md +1 -1
  25. data/app/pb_kits/playbook/pb_table/_table.tsx +4 -0
  26. data/app/pb_kits/playbook/pb_table/docs/_playground.json +2 -0
  27. data/app/pb_kits/playbook/pb_table/docs/_playground.overrides.json +1 -1
  28. data/app/pb_kits/playbook/pb_table/docs/_sections.yml +1 -0
  29. data/app/pb_kits/playbook/pb_table/docs/_table_contrast_border.html.erb +71 -0
  30. data/app/pb_kits/playbook/pb_table/docs/_table_contrast_border.jsx +93 -0
  31. data/app/pb_kits/playbook/pb_table/docs/_table_contrast_border.md +1 -0
  32. data/app/pb_kits/playbook/pb_table/docs/example.yml +2 -0
  33. data/app/pb_kits/playbook/pb_table/docs/index.js +1 -0
  34. data/app/pb_kits/playbook/pb_table/kit.schema.json +8 -0
  35. data/app/pb_kits/playbook/pb_table/styles/_all.scss +2 -1
  36. data/app/pb_kits/playbook/pb_table/styles/_contrast_border.scss +87 -0
  37. data/app/pb_kits/playbook/pb_table/styles/_desktop_collapse.scss +2 -2
  38. data/app/pb_kits/playbook/pb_table/styles/_headers.scss +3 -3
  39. data/app/pb_kits/playbook/pb_table/styles/_hover.scss +2 -2
  40. data/app/pb_kits/playbook/pb_table/styles/_mobile.scss +2 -2
  41. data/app/pb_kits/playbook/pb_table/styles/_mobile_collapse.scss +2 -2
  42. data/app/pb_kits/playbook/pb_table/styles/_scroll.scss +2 -2
  43. data/app/pb_kits/playbook/pb_table/styles/_sticky_columns.scss +2 -2
  44. data/app/pb_kits/playbook/pb_table/styles/_structure.scss +1 -1
  45. data/app/pb_kits/playbook/pb_table/styles/_table-card.scss +2 -2
  46. data/app/pb_kits/playbook/pb_table/styles/_tablet_collapse.scss +2 -2
  47. data/app/pb_kits/playbook/pb_table/styles/_vertical_border.scss +9 -9
  48. data/app/pb_kits/playbook/pb_table/table.rb +7 -1
  49. data/app/pb_kits/playbook/pb_table/table.test.js +5 -0
  50. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_error.html.erb +1 -1
  51. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_error.jsx +1 -8
  52. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_error.html.erb +1 -1
  53. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_error.jsx +1 -7
  54. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_error_state.jsx +2 -6
  55. data/dist/chunks/_detail-DmbtP8Zd.js +5 -0
  56. data/dist/chunks/{_pb_line_graph-B3q4LKEz.js → _pb_line_graph-BynhzAZz.js} +1 -1
  57. data/dist/chunks/_table-BKJZFT_d.js +1 -0
  58. data/dist/chunks/_typeahead-CV5P_leb.js +1 -0
  59. data/dist/chunks/{globalProps-zGbN4oBH.js → globalProps-BmtRVofv.js} +1 -1
  60. data/dist/chunks/lib-CYZytrqW.js +29 -0
  61. data/dist/chunks/vendor.js +3 -3
  62. data/dist/playbook-rails-react-bindings.js +1 -1
  63. data/dist/playbook-rails.js +1 -1
  64. data/dist/playbook.css +1 -1
  65. data/lib/playbook/version.rb +2 -2
  66. metadata +12 -8
  67. data/dist/chunks/_detail-BKKHcGcj.js +0 -5
  68. data/dist/chunks/_table-Bkla2kcQ.js +0 -1
  69. data/dist/chunks/_typeahead-CULhE-SU.js +0 -1
  70. data/dist/chunks/lib-LNv9xZ-1.js +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a0a3a991932ffc02ce86b727b56b252fbf4bb0bc93f232fbbad6941c63e56d5
4
- data.tar.gz: 342f580343e5a39ef86663eba4d1fb8bce4ddcc699d3d7b52c9a7e1d16404f95
3
+ metadata.gz: 7b715350921a0d13054afef8c7a265256206ce8a191121ac5a553f7a6c53f81e
4
+ data.tar.gz: d2f45143b6d02498ee81758e82ce67ad49f35d821990d627bdd973ac3d944f83
5
5
  SHA512:
6
- metadata.gz: 878f0f3aa7a1930e43394ae899c2708d9d7d3596f4ebb014c588f1095921c9cb7575f77f33dfb207286bb93c2c3b2185b9b244df1addd71aa6347835c0560560
7
- data.tar.gz: 71ddab4024b085f462b341b2daf4158aac4cc81a5289025f5cf1782ba4c72560e2b75d6054a61faae3eb5d3cd1cf46ae24e8d579bcd313fe0578a5ac06692efb
6
+ metadata.gz: 060ba5b0e47cbefc0822b7f624b45344e28dea010f2b0af1d380e7d85e33b59f0b5a2df7153cbc1b4b7264b4821ee9617dc2e732ff8704b029333329119a1583
7
+ data.tar.gz: a90fecaa285e278f7d119e96f48760f8c36816ef7f8810c1177e1100d088bf34c9d4f7705319d99a1106e570b4304989536169262bc5223d2ecb505210ca5566
@@ -1,4 +1,4 @@
1
- <%= pb_rails("button", props: { count: 153, highlight: false, icon: "&#127881;", classname: "count", id: "reaction-button-highlight", variant: "reaction" }) %>
1
+ <%= pb_rails("button", props: { count: 153, highlight: false, icon: "🎉", classname: "count", id: "reaction-button-highlight", variant: "reaction" }) %>
2
2
  <%= pb_rails("button", props: { count: 5, icon: "1️⃣", variant: "reaction", margin_left: "lg" }) %>
3
3
  <%= pb_rails("button", props: { variant: "reaction", margin_left: "lg" }) %>
4
4
  <%= pb_rails("button", props: { icon: "user", variant: "reaction", margin_left: "lg" }) %>
@@ -11,7 +11,7 @@ return (
11
11
  <Button
12
12
  count={highlightActive ? (reactionCount + 1) : reactionCount}
13
13
  highlight = {highlightActive}
14
- icon="&#127881;"
14
+ icon="🎉"
15
15
  onClick={()=> setHighlightActive(!highlightActive)}
16
16
  tabIndex={0}
17
17
  variant="reaction"
@@ -1,3 +1,3 @@
1
- The `reaction` variant accepts any HTML Emoji or it's hexa/decimal ref (see [here](https://www.w3schools.com/charsets/ref_emoji.asp)) as a string within the `icon` prop. If nothing is passed to the icon prop, the default reaction button will be displayed as seen in the third example. The default reaction button will also be rendered if a Fontawesome icon (not an Emoji) is passed to the `icon` prop of a `reaction` variant, but the default "smiley +" icon will be replaced with the named icon.
1
+ The `reaction` variant accepts any emoji as a string within the `icon` prop. If nothing is passed to the icon prop, the default reaction button will be displayed as seen in the third example. The default reaction button will also be rendered if a Fontawesome icon (not an Emoji) is passed to the `icon` prop of a `reaction` variant, but the default "smiley +" icon will be replaced with the named icon.
2
2
 
3
3
  Reaction buttons also accept two additional (optional) props: `count`, which accepts a number (i.e., a count of reactions) to be displayed next to the Emoji; and `highlight`, which is a boolean that if true, displays the 'active' state for the button. Click the first reaction button to see this in action!
@@ -1,4 +1,4 @@
1
1
  <%= pb_rails("date_picker", props: {
2
- error: raw(pb_rails("icon", props: { icon: "warning" }) + " Invalid date. Please pick a valid date."),
2
+ error: "Invalid date. Please pick a valid date.",
3
3
  picker_id: "date-picker-error"
4
4
  }) %>
@@ -1,14 +1,11 @@
1
1
  import React from 'react'
2
2
 
3
3
  import DatePicker from '../_date_picker'
4
- import Icon from '../../pb_icon/_icon'
5
4
 
6
5
  const DatePickerError = (props) => (
7
6
  <div>
8
7
  <DatePicker
9
- error={<>
10
- <Icon icon="warning" /> Invalid date. Please pick a valid date.
11
- </>}
8
+ error="Invalid date. Please pick a valid date."
12
9
  pickerId="date-picker-error"
13
10
  {...props}
14
11
  />
@@ -7,6 +7,6 @@
7
7
  %>
8
8
 
9
9
  <%= pb_rails("dropdown", props: {
10
- error: raw(pb_rails("icon", props: { icon: "warning" }) + " Please make a valid selection"),
10
+ error: "Please make a valid selection",
11
11
  options: options
12
12
  }) %>
@@ -1,12 +1,9 @@
1
1
  import React, { useState } from 'react'
2
2
  import Dropdown from '../../pb_dropdown/_dropdown'
3
- import Icon from '../../pb_icon/_icon'
4
3
 
5
4
  const DropdownError = (props) => {
6
5
  const [selectedOption, setSelectedOption] = useState()
7
- const error = selectedOption?.value ? null : (<>
8
- <Icon icon="warning" /> Please make a valid selection
9
- </>)
6
+ const error = selectedOption?.value ? null : "Please make a valid selection"
10
7
  const options = [
11
8
  {
12
9
  label: "United States",
@@ -1 +1 @@
1
- <%= pb_rails("file_upload", props: {id: "error", error: raw(pb_rails("icon", props: { icon: "warning" }) + " Please upload a valid file")}) %>
1
+ <%= pb_rails("file_upload", props: {id: "error", error: "Please upload a valid file."}) %>
@@ -2,7 +2,6 @@ import React, { useState } from 'react'
2
2
  import FileUpload from '../_file_upload'
3
3
  import List from '../../pb_list/_list'
4
4
  import ListItem from '../../pb_list/_list_item'
5
- import Icon from '../../pb_icon/_icon'
6
5
 
7
6
  const AcceptedFilesList = ({ files }) => (
8
7
  <List>
@@ -19,10 +18,6 @@ const FileUploadError = (props) => {
19
18
  setFilesToUpload([...filesToUpload, ...files])
20
19
  }
21
20
 
22
- const error = (<>
23
- <Icon icon="warning" /> Please upload a valid file
24
- </>)
25
-
26
21
  return (
27
22
  <div>
28
23
  <AcceptedFilesList
@@ -30,7 +25,7 @@ const FileUploadError = (props) => {
30
25
  {...props}
31
26
  />
32
27
  <FileUpload
33
- error={error}
28
+ error="Please upload a valid file."
34
29
  onFilesAccepted={handleOnFilesAccepted}
35
30
  {...props}
36
31
  />
@@ -1,10 +1,9 @@
1
1
  <%# SVG fill color inherited from css color property %>
2
2
  <div class="icon-wrapper">
3
-
4
- <% svg_url = "https://upload.wikimedia.org/wikipedia/commons/3/3b/Wrench_font_awesome.svg" %>
5
- <p><%= pb_rails("icon", props: { icon: svg_url } ) %></p>
6
- <p><%= pb_rails("icon", props: { rotation: 90, icon: svg_url, size: "2x" } ) %></p>
7
- <p><%= pb_rails("icon", props: { spin: true, icon: svg_url, size: "3x" } ) %></p>
8
- <p><%= pb_rails("icon", props: { size: "5x", icon: svg_url } ) %></p>
9
- <p><%= pb_rails("icon", props: { flip: "horizontal", size: "5x", icon: svg_url } ) %></p>
3
+ <% svg_path = Playbook::Engine.root.join("app/pb_kits/playbook/utilities/icons/clock.svg").to_s %>
4
+ <p><%= pb_rails("icon", props: { icon: svg_path } ) %></p>
5
+ <p><%= pb_rails("icon", props: { rotation: 90, icon: svg_path, size: "2x" } ) %></p>
6
+ <p><%= pb_rails("icon", props: { spin: true, icon: svg_path, size: "3x" } ) %></p>
7
+ <p><%= pb_rails("icon", props: { size: "5x", icon: svg_path } ) %></p>
8
+ <p><%= pb_rails("icon", props: { flip: "horizontal", size: "5x", icon: svg_path } ) %></p>
10
9
  </div>
@@ -12,4 +12,4 @@ You must source _your own SVG into component/view_ you are working on. This can
12
12
 
13
13
  ### Rails
14
14
 
15
- Sending the absolute path to the `icon` prop results in an `<SVG>` tag within the working view.
15
+ Pass a local filesystem path to an SVG under your application or a mounted engine (for example `Playbook::Engine.root.join("app/pb_kits/playbook/utilities/icons/clock.svg").to_s`) via the `icon` or `custom_icon` prop. Remote `http(s)` URLs are not fetched — only local / engine paths are supported.
@@ -2,7 +2,7 @@
2
2
  <%= object.render_svg %>
3
3
  <% elsif object.valid_emoji? %>
4
4
  <span class="pb_icon_kit_emoji">
5
- <%= object.icon.html_safe %>
5
+ <%= object.icon %>
6
6
  </span>
7
7
  <% else %>
8
8
  <%= pb_content_tag(:i) do %>
@@ -46,8 +46,19 @@ module Playbook
46
46
  prop :tabindex
47
47
 
48
48
  def valid_emoji?
49
- emoji_regex = /\p{Emoji}/
50
- emoji_regex.match?(icon)
49
+ str = icon.to_s
50
+ return false if str.blank?
51
+ # Digits / # / * match \p{Emoji} as keycap bases; require real emoji forms.
52
+ # Also reject markup so we never treat HTML-like strings as emoji.
53
+ return false if str.match?(%r{[<>&"'A-Za-z/\\]})
54
+
55
+ str.match?(
56
+ /
57
+ \p{Emoji_Presentation}
58
+ | \p{Extended_Pictographic}
59
+ | [\d#*]\u{FE0F}?\u{20E3}
60
+ /x
61
+ )
51
62
  end
52
63
 
53
64
  def classname
@@ -103,7 +114,11 @@ module Playbook
103
114
  if Rails.application.config.respond_to?(:icon_path)
104
115
  resolved_icon = resolve_alias(icon)
105
116
  path = self.class.icon_path_index[resolved_icon]
106
- path if path && File.exist?(path)
117
+ # icon_path_index keys off the *.svg basename, so apply the same
118
+ # realpath / .svg / allowed-root checks as custom_icon loading —
119
+ # otherwise a foo.svg symlink to a non-svg file would be File.read
120
+ # directly in svg_content.
121
+ path && File.exist?(path) ? allowed_svg_path(path)&.to_s : nil
107
122
  end
108
123
  end
109
124
 
@@ -302,8 +317,9 @@ module Playbook
302
317
  source = source.to_s
303
318
  return "" if source.blank?
304
319
 
305
- remote = remote_svg_uri(source)
306
- return read_remote_svg(remote) if remote
320
+ # Remote http(s) fetch removed: untrusted custom_icon/icon values must not
321
+ # trigger server-side requests (SSRF). Use local / engine SVG paths only.
322
+ return "" if remote_svg_uri(source)
307
323
 
308
324
  path = allowed_svg_path(source)
309
325
  return "" unless path
@@ -318,16 +334,11 @@ module Playbook
318
334
  nil
319
335
  end
320
336
 
321
- def read_remote_svg(uri)
322
- uri.open("User-Agent" => "Playbook-Icon-Kit/1.0 (https://github.com/powerhome/playbook)", redirect: false, &:read)
323
- rescue OpenURI::HTTPError, StandardError
324
- ""
325
- end
326
-
327
337
  def allowed_svg_path(source)
328
338
  candidate = Pathname.new(source)
329
339
  candidate = Rails.root.join(candidate) unless candidate.absolute?
330
- resolved = candidate.expand_path
340
+ resolved = real_path(candidate.expand_path)
341
+ return nil unless resolved
331
342
  return nil unless svg_file?(resolved)
332
343
  return nil unless path_within_allowed_root?(resolved)
333
344
 
@@ -337,7 +348,8 @@ module Playbook
337
348
  # Only ever read real .svg files. This keeps non-svg files that happen to
338
349
  # live inside an allowed root (config/master.key, credentials, .env, ...)
339
350
  # structurally out of the read path rather than relying on the is_svg?
340
- # gate to exclude them.
351
+ # gate to exclude them. Extension is checked on the resolved real path so
352
+ # a *.svg symlink to a non-svg file cannot bypass the filter.
341
353
  def svg_file?(path)
342
354
  path.file? && path.extname.casecmp(".svg").zero?
343
355
  end
@@ -351,9 +363,12 @@ module Playbook
351
363
  false
352
364
  end
353
365
 
354
- # An SVG may live in the host application or in any mounted engine
355
- # (Playbook itself, or a host app's component engines), so every loaded
356
- # engine root is allowed in addition to Rails.root.
366
+ # An SVG may live in the host application, any mounted engine
367
+ # (Playbook itself, or a host app's component engines), or the configured
368
+ # icon_path directory. playbook-website points development icon_path at
369
+ # ../node_modules/@powerhome/playbook-icons/icons, which resolves outside
370
+ # Rails.root / Engine.root — that directory must still be allowed, while
371
+ # realpath + svg_file? keep symlink-to-non-svg blocked.
357
372
  def allowed_svg_roots
358
373
  @allowed_svg_roots ||= svg_root_candidates.filter_map { |candidate| real_path(candidate) }.uniq
359
374
  end
@@ -361,10 +376,19 @@ module Playbook
361
376
  def svg_root_candidates
362
377
  Rails::Engine.descendants
363
378
  .map { |engine| engine_root(engine) }
364
- .push(Playbook::Engine.root, Rails.root)
379
+ .push(Playbook::Engine.root, Rails.root, configured_icon_path_root)
365
380
  .compact
366
381
  end
367
382
 
383
+ def configured_icon_path_root
384
+ return unless Rails.application.config.respond_to?(:icon_path)
385
+
386
+ base = Rails.application.config.icon_path
387
+ return if base.blank?
388
+
389
+ Rails.root.join(base)
390
+ end
391
+
368
392
  def engine_root(engine)
369
393
  engine.root
370
394
  rescue
@@ -4,13 +4,13 @@
4
4
  }) %>
5
5
  <br />
6
6
  <%= pb_rails("icon_circle", props: {
7
- icon: "&#128525;",
7
+ icon: "😍",
8
8
  size:"md",
9
9
  variant: "red"
10
10
  }) %>
11
11
  <br />
12
12
  <%= pb_rails("icon_circle", props: {
13
- icon: '&#x1F389;',
13
+ icon: "🎉",
14
14
  size: "lg",
15
15
  variant: "teal"
16
- }) %>
16
+ }) %>
@@ -12,14 +12,14 @@ const IconCircleEmoji = (props) => {
12
12
  />
13
13
  <br/>
14
14
  <IconCircle
15
- icon="&#128525;"
15
+ icon="😍"
16
16
  size="md"
17
17
  variant="red"
18
18
  {...props}
19
19
  />
20
20
  <br/>
21
21
  <IconCircle
22
- icon="&#x1F389;"
22
+ icon="🎉"
23
23
  size="lg"
24
24
  variant="teal"
25
25
  {...props}
@@ -1 +1 @@
1
- The Icon Circle also allows you to pass in an HTML emoji in place of an icon if needed. To do so, pass any emoji or its hexa/decimal ref (see [here](https://www.w3schools.com/charsets/ref_emoji.asp)) as a string to the `icon` prop as shown in the code snippet below.
1
+ The Icon Circle also allows you to pass an emoji in place of an icon if needed. Pass any emoji as a string to the `icon` prop as shown in the code snippet below.
@@ -37,7 +37,7 @@ describe("IconCircle Kit", () => {
37
37
  render(
38
38
  <IconCircle
39
39
  data={{ testid: testId }}
40
- icon="&#128525;"
40
+ icon="😍"
41
41
  size="md"
42
42
  />
43
43
  )
@@ -3,7 +3,6 @@ import React, { useEffect, useState } from "react";
3
3
  import Button from '../../pb_button/_button'
4
4
  import FixedConfirmationToast from '../../pb_fixed_confirmation_toast/_fixed_confirmation_toast'
5
5
  import PhoneNumberInput from '../../pb_phone_number_input/_phone_number_input'
6
- import Icon from '../../pb_icon/_icon'
7
6
 
8
7
  const PhoneNumberInputValidation = (props) => {
9
8
  const [formErrors, setFormErrors] = useState("");
@@ -16,7 +15,7 @@ const PhoneNumberInputValidation = (props) => {
16
15
  // Start with initial error - will be cleared on blur if valid
17
16
  const initialError = (
18
17
  <>
19
- <Icon icon="warning" /> Missing phone number.
18
+ Missing phone number.
20
19
  </>
21
20
  );
22
21
 
@@ -18,6 +18,9 @@
18
18
  overflow: hidden;
19
19
  text-overflow: ellipsis;
20
20
  max-height: 45px;
21
+ &:has(option[value=""]:checked) {
22
+ color: $input_text_default;
23
+ }
21
24
  @media (hover:hover) {
22
25
  &:hover, &:active, &:focus {
23
26
  background-color: $input_background_state;
@@ -170,6 +173,9 @@
170
173
  white-space: nowrap;
171
174
  overflow: hidden;
172
175
  text-overflow: ellipsis;
176
+ &:has(option[value=""]:checked) {
177
+ color: $input_text_default_dark;
178
+ }
173
179
  @media (hover:hover) {
174
180
  &:hover, &:active, &:focus {
175
181
  background-color: $input_background_state_dark;
@@ -1,5 +1,5 @@
1
1
  <%= pb_rails("select", props: {
2
- error: raw(pb_rails("icon", props: { icon: "warning" }) + " Please make a valid selection"),
2
+ error: "Please make a valid selection",
3
3
  id: "select-food-error",
4
4
  label: "Favorite Food",
5
5
  name: "food",
@@ -1,7 +1,6 @@
1
1
  import React from 'react'
2
2
  import Body from '../../pb_body/_body'
3
3
  import Select from '../../pb_select/_select'
4
- import Icon from '../../pb_icon/_icon'
5
4
 
6
5
  const SelectError = (props) => {
7
6
  const options = [
@@ -19,14 +18,10 @@ const SelectError = (props) => {
19
18
  },
20
19
  ]
21
20
 
22
- const error = (<>
23
- <Icon icon="warning" /> Please make a valid selection
24
- </>)
25
-
26
21
  return (
27
22
  <div>
28
23
  <Select
29
- error={error}
24
+ error="Please make a valid selection"
30
25
  label="Favorite Food"
31
26
  name="food-error"
32
27
  options={options}
@@ -34,7 +29,7 @@ const SelectError = (props) => {
34
29
  {...props}
35
30
  />
36
31
  <Body
37
- error={error}
32
+ error="Please make a valid selection"
38
33
  status="negative"
39
34
  {...props}
40
35
  />
@@ -1,11 +1,11 @@
1
1
  <div class="pb--doc-demo-row">
2
- <% svg_url = "https://upload.wikimedia.org/wikipedia/commons/3/3b/Wrench_font_awesome.svg" %>
2
+ <% svg_path = Playbook::Engine.root.join("app/pb_kits/playbook/utilities/icons/clock.svg").to_s %>
3
3
 
4
4
  <%= pb_rails("selectable_card_icon", props: {
5
- custom_icon: svg_url,
5
+ custom_icon: svg_path,
6
6
  title_text: "Customization",
7
7
  body_text: "Personalize everything",
8
8
  input_id: 1,
9
9
  checked: true,
10
10
  }) %>
11
- </div>
11
+ </div>
@@ -16,4 +16,4 @@ So long as you have a valid React `<SVG>` node, you can send it as the `customIc
16
16
 
17
17
  ### Rails
18
18
 
19
- Some Rails applications use only webpack(er) which means using `image_url` will be successful over `image_path` in most cases especially development where Webpack Dev Server is serving assets over HTTP. Rails applications still using Asset Pipeline may use `image_path` or `image_url`. Of course, YMMV depending on any custom configurations in your Rails application.
19
+ Pass a local filesystem path to an SVG under your application or a mounted engine (for example `Playbook::Engine.root.join("app/pb_kits/playbook/utilities/icons/clock.svg").to_s`) via the `custom_icon` prop. Remote `http(s)` URLs are not fetched — only local / engine paths are supported.
@@ -45,6 +45,7 @@ type TableProps = {
45
45
  title?: string,
46
46
  titleProps?: { [key: string]: any },
47
47
  variant?: "default" | "withFilter",
48
+ contrastBorder?: boolean,
48
49
  verticalBorder?: boolean,
49
50
  } & GlobalProps
50
51
 
@@ -80,6 +81,7 @@ const Table = (props: TableProps): React.ReactElement => {
80
81
  striped = false,
81
82
  tag = 'table',
82
83
  title,
84
+ contrastBorder = false,
83
85
  verticalBorder = false,
84
86
  } = props
85
87
 
@@ -87,6 +89,7 @@ const Table = (props: TableProps): React.ReactElement => {
87
89
  const dataProps = buildDataProps(data)
88
90
  const htmlProps = buildHtmlProps(htmlOptions)
89
91
  const tableCollapseCss = responsive !== 'none' ? `table-collapse-${collapse}` : ''
92
+ const contrastBorderCss = contrastBorder ? 'contrast-border' : ''
90
93
  const verticalBorderCss = verticalBorder ? 'vertical-border' : ''
91
94
  const spaceCssName = outerPadding !== 'none' ? 'space_' : ''
92
95
  const outerPaddingCss = outerPadding ? `outer_padding_${spaceCssName}${outerPadding}` : ''
@@ -117,6 +120,7 @@ const Table = (props: TableProps): React.ReactElement => {
117
120
  },
118
121
  globalProps(props),
119
122
  tableCollapseCss,
123
+ contrastBorderCss,
120
124
  verticalBorderCss,
121
125
  className
122
126
  )
@@ -14,6 +14,7 @@
14
14
  "striped": false,
15
15
  "tag": "table",
16
16
  "variant": "default",
17
+ "contrastBorder": false,
17
18
  "verticalBorder": false
18
19
  },
19
20
  "groups": [
@@ -33,6 +34,7 @@
33
34
  "props": [
34
35
  "striped",
35
36
  "verticalBorder",
37
+ "contrastBorder",
36
38
  "singleLine",
37
39
  "disableHover",
38
40
  "headerStyle",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  {
41
41
  "name": "Appearance",
42
- "props": ["striped", "verticalBorder", "singleLine", "disableHover", "headerStyle", "dark", "dataTable"]
42
+ "props": ["striped", "verticalBorder", "contrastBorder", "singleLine", "disableHover", "headerStyle", "dark", "dataTable"]
43
43
  },
44
44
  {
45
45
  "name": "Sticky Columns",
@@ -42,6 +42,7 @@ sections:
42
42
  examples:
43
43
  - table_data_table
44
44
  - table_vertical_border
45
+ - table_contrast_border
45
46
  - table_striped
46
47
 
47
48
  - title: "Header Variants"
@@ -0,0 +1,71 @@
1
+ <%= pb_rails("table", props: { size: "sm", contrast_border: true }) do %>
2
+ <thead>
3
+ <tr>
4
+ <th>Column 1</th>
5
+ <th>Column 2</th>
6
+ <th>Column 3</th>
7
+ <th>Column 4</th>
8
+ <th>Column 5</th>
9
+ </tr>
10
+ </thead>
11
+ <tbody>
12
+ <tr>
13
+ <td>Value 1</td>
14
+ <td>Value 2</td>
15
+ <td>Value 3</td>
16
+ <td>Value 4</td>
17
+ <td>Value 5</td>
18
+ </tr>
19
+ <tr>
20
+ <td>Value 1</td>
21
+ <td>Value 2</td>
22
+ <td>Value 3</td>
23
+ <td>Value 4</td>
24
+ <td>Value 5</td>
25
+ </tr>
26
+ <tr>
27
+ <td>Value 1</td>
28
+ <td>Value 2</td>
29
+ <td>Value 3</td>
30
+ <td>Value 4</td>
31
+ <td>Value 5</td>
32
+ </tr>
33
+ </tbody>
34
+ <% end %>
35
+
36
+ <br/>
37
+
38
+ <%= pb_rails("table", props: { size: "sm", contrast_border: true, vertical_border: true }) do %>
39
+ <thead>
40
+ <tr>
41
+ <th>Column 1</th>
42
+ <th>Column 2</th>
43
+ <th>Column 3</th>
44
+ <th>Column 4</th>
45
+ <th>Column 5</th>
46
+ </tr>
47
+ </thead>
48
+ <tbody>
49
+ <tr>
50
+ <td>Value 1</td>
51
+ <td>Value 2</td>
52
+ <td>Value 3</td>
53
+ <td>Value 4</td>
54
+ <td>Value 5</td>
55
+ </tr>
56
+ <tr>
57
+ <td>Value 1</td>
58
+ <td>Value 2</td>
59
+ <td>Value 3</td>
60
+ <td>Value 4</td>
61
+ <td>Value 5</td>
62
+ </tr>
63
+ <tr>
64
+ <td>Value 1</td>
65
+ <td>Value 2</td>
66
+ <td>Value 3</td>
67
+ <td>Value 4</td>
68
+ <td>Value 5</td>
69
+ </tr>
70
+ </tbody>
71
+ <% end %>