playbook_ui 11.12.1 → 11.13.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +50 -50
  3. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +3 -0
  4. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_time.html.erb +1 -1
  5. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_time.jsx +28 -12
  6. data/app/pb_kits/playbook/pb_date_picker/plugins/timeSelect.ts +93 -90
  7. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_time_selection_styles.scss +1 -15
  8. data/app/pb_kits/playbook/pb_dialog/_dialog.scss +48 -8
  9. data/app/pb_kits/playbook/pb_dialog/dialog.html.erb +22 -8
  10. data/app/pb_kits/playbook/pb_dialog/dialog.rb +32 -0
  11. data/app/pb_kits/playbook/pb_dialog/dialogHelper.js +18 -2
  12. data/app/pb_kits/playbook/pb_dialog/dialog_footer.html.erb +10 -6
  13. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.html.erb +6 -3
  14. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_default.html.erb +4 -4
  15. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height.html.erb +58 -0
  16. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height.jsx +1 -1
  17. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height.md +1 -2
  18. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height_placement.html.erb +58 -0
  19. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height_placement.jsx +1 -1
  20. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height_placement.md +2 -2
  21. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_scrollable.html.erb +11 -0
  22. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_should_close_on_overlay.html.erb +12 -0
  23. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_sizes.html.erb +12 -12
  24. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_sizes.jsx +1 -1
  25. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_stacked_alert.html.erb +49 -0
  26. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_stacked_alert.md +1 -1
  27. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_status.html.erb +96 -0
  28. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_status.md +3 -1
  29. data/app/pb_kits/playbook/pb_dialog/docs/example.yml +7 -0
  30. data/app/pb_kits/playbook/pb_enhanced_element/{element_observer.js → element_observer.ts} +27 -19
  31. data/app/pb_kits/playbook/pb_enhanced_element/{index.js → index.ts} +22 -15
  32. data/app/pb_kits/playbook/pb_file_upload/_file_upload.tsx +38 -6
  33. data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_max_size.jsx +71 -0
  34. data/app/pb_kits/playbook/pb_file_upload/docs/example.yml +1 -0
  35. data/app/pb_kits/playbook/pb_file_upload/docs/index.js +1 -0
  36. data/app/pb_kits/playbook/pb_file_upload/fileupload.test.js +3 -3
  37. data/app/pb_kits/playbook/pb_selectable_list/_item.jsx +15 -4
  38. data/app/pb_kits/playbook/pb_selectable_list/_selectable_list.jsx +24 -2
  39. data/app/pb_kits/playbook/pb_selectable_list/_selectable_list.scss +3 -0
  40. data/app/pb_kits/playbook/pb_selectable_list/selectable_list.html.erb +1 -1
  41. data/app/pb_kits/playbook/pb_selectable_list/selectable_list.rb +6 -0
  42. data/app/pb_kits/playbook/pb_selectable_list/selectable_list_item.html.erb +32 -0
  43. data/app/pb_kits/playbook/pb_selectable_list/selectable_list_item.rb +7 -2
  44. data/lib/playbook/version.rb +2 -2
  45. metadata +11 -4
@@ -1,4 +1,4 @@
1
- import React, { useCallback } from 'react'
1
+ import React, { useEffect, useCallback, useRef } from 'react'
2
2
  import { useDropzone } from 'react-dropzone'
3
3
  import classnames from 'classnames'
4
4
 
@@ -12,26 +12,51 @@ import Card from '../pb_card/_card'
12
12
  type FileUploadProps = {
13
13
  accept?: string[],
14
14
  className?: string,
15
- data?: object,
15
+ data?: {[key: string]: string | number},
16
16
  acceptedFilesDescription?: string,
17
+ maxSize?: number,
17
18
  onFilesAccepted: Callback<File, File>,
19
+ onFilesRejected: (error: string, files: File[]) => void,
18
20
  }
19
21
 
20
- const FileUpload = (props: FileUploadProps) => {
22
+ const getFormattedFileSize = (fileSize: number): string => {
23
+ return `${fileSize / 1e+6} MB`
24
+ }
25
+
26
+ const FileUpload = (props: FileUploadProps): React.ReactElement => {
21
27
  const {
22
28
  accept = null,
23
29
  acceptedFilesDescription = '',
24
30
  className,
25
31
  data = {},
32
+ maxSize,
26
33
  onFilesAccepted = noop,
34
+ onFilesRejected = noop,
27
35
  } = props
28
36
 
29
- const onDrop = useCallback((files) => onFilesAccepted(files), []);
30
- const { getRootProps, getInputProps, isDragActive } = useDropzone({
37
+ const onDrop = useCallback((files) => {
38
+ onFilesAccepted(files)
39
+ }, [onFilesAccepted])
40
+
41
+ const { getRootProps, getInputProps, isDragActive, rejectedFiles } = useDropzone({
31
42
  accept,
43
+ maxSize,
32
44
  onDrop,
33
45
  })
34
46
 
47
+ const prevRejected: any = useRef();
48
+
49
+ const maxFileSizeText = `Max file size is ${getFormattedFileSize(maxSize)}.`
50
+
51
+ useEffect(() => {
52
+ if (rejectedFiles === prevRejected.current) return
53
+ const isFileTooLarge = maxSize && rejectedFiles.length > 0 && rejectedFiles[0].size > maxSize;
54
+ if (isFileTooLarge) {
55
+ onFilesRejected(`File size is too large! ${maxFileSizeText}`, rejectedFiles)
56
+ }
57
+ prevRejected.current = rejectedFiles
58
+ }, [maxFileSizeText, maxSize, onFilesRejected, rejectedFiles])
59
+
35
60
  const acceptedFileTypes = () => {
36
61
  return accept.map((fileType) => {
37
62
  if (fileType.startsWith('image/')) {
@@ -44,6 +69,13 @@ const FileUpload = (props: FileUploadProps) => {
44
69
 
45
70
  const dataProps = buildDataProps(data)
46
71
 
72
+ const getDescription = () => {
73
+ let msg = ""
74
+ accept === null ? msg += 'Choose a file or drag it here.' : msg += `Choose a file or drag it here. The accepted file types are: ${acceptedFilesDescription || acceptedFileTypes()}.`
75
+ if (maxSize) msg += ` ${maxFileSizeText}`
76
+ return msg
77
+ }
78
+
47
79
  return (
48
80
  <div
49
81
  className={classnames(buildCss('pb_file_upload_kit'), globalProps(props), className)}
@@ -56,7 +88,7 @@ const FileUpload = (props: FileUploadProps) => {
56
88
  {isDragActive ?
57
89
  <p>{'Drop the files here ...'}</p>
58
90
  :
59
- <p>{accept === null ? 'Choose a file or drag it here' : `Choose a file or drag it here. The accepted file types are: ${acceptedFilesDescription || acceptedFileTypes()}`}</p>
91
+ <p>{getDescription()}</p>
60
92
  }
61
93
  </Body>
62
94
  </Card>
@@ -0,0 +1,71 @@
1
+ /* @flow */
2
+ /* eslint-disable react/no-multi-comp */
3
+
4
+ import React, { useState } from 'react'
5
+ import {
6
+ Body,
7
+ FileUpload,
8
+ List,
9
+ ListItem,
10
+ } from '../..'
11
+
12
+ const AcceptedFilesList = ({ files }: FileList) => (
13
+ <List>
14
+ {files.map((file) => (
15
+ <ListItem key={file.name}>{file.name}</ListItem>
16
+ ))}
17
+ </List>
18
+ )
19
+ const RejectedFilesList = ({ files }: FileList) => (
20
+ <List>
21
+ {files.map((file) => (
22
+ <ListItem key={file.name}><Body color="error">{`${file.name} (file too large)`}</Body></ListItem>
23
+ ))}
24
+ </List>
25
+ )
26
+
27
+ const FileUploadMaxSize = (props) => {
28
+ const [filesToUpload, setFilesToUpload] = useState([])
29
+ const [filesRejected, setFilesRejected] = useState([])
30
+ const [error, setError] = useState()
31
+
32
+ const handleOnFilesAccepted = (files) => {
33
+ if (files.length) setError()
34
+ setFilesToUpload([...filesToUpload, ...files])
35
+ }
36
+
37
+ const handleOnFilesRejected = (error, files) => {
38
+ setError(error)
39
+ setFilesRejected([...filesRejected, ...files])
40
+ }
41
+
42
+ return (
43
+ <div>
44
+ <AcceptedFilesList
45
+ files={filesToUpload}
46
+ {...props}
47
+ />
48
+ <RejectedFilesList
49
+ files={filesRejected}
50
+ {...props}
51
+ />
52
+ <FileUpload
53
+ acceptedFilesDescription="Choose a file or drag it here. 1 MB size limit."
54
+ maxSize={1000000}
55
+ onFilesAccepted={handleOnFilesAccepted}
56
+ onFilesRejected={handleOnFilesRejected}
57
+ {...props}
58
+ />
59
+ { error && (
60
+ <Body
61
+ color="error"
62
+ marginY="md"
63
+ >
64
+ {error}
65
+ </Body>
66
+ )}
67
+ </div>
68
+ )
69
+ }
70
+
71
+ export default FileUploadMaxSize
@@ -6,4 +6,5 @@ examples:
6
6
  - file_upload_default: Default List of files to upload
7
7
  - file_upload_accept: Accept only certain types of files
8
8
  - file_upload_custom_description: Add your one accepted files description
9
+ - file_upload_max_size: Set a file size limit
9
10
 
@@ -1,3 +1,4 @@
1
1
  export { default as FileUploadDefault } from './_file_upload_default.jsx'
2
2
  export { default as FileUploadAccept } from './_file_upload_accept.jsx'
3
3
  export { default as FileUploadCustomDescription } from './_file_upload_custom_description.jsx'
4
+ export { default as FileUploadMaxSize } from './_file_upload_max_size.jsx'
@@ -27,14 +27,14 @@ test('shows default drag text', () => {
27
27
  expect(kit).toHaveTextContent('Choose a file or drag it here')
28
28
  })
29
29
 
30
- test('shows type-specific drag text', () => {
30
+ test('displays max file size text', () => {
31
31
  render(
32
32
  <FileUpload
33
- accept={['image/svg+xml']}
34
33
  data={{ testid: testid }}
34
+ maxSize={1e+6}
35
35
  />
36
36
  )
37
37
 
38
38
  const kit = screen.getByTestId(testid)
39
- expect(kit).toHaveTextContent('Choose a file or drag it here. The accepted file types are: svg+xml')
39
+ expect(kit).toHaveTextContent('Choose a file or drag it here. Max file size is 1 MB.')
40
40
  })
@@ -1,6 +1,6 @@
1
1
  /* @flow */
2
2
 
3
- import React, { Node } from 'react'
3
+ import React, { Node, useState } from 'react'
4
4
  import classnames from 'classnames'
5
5
 
6
6
  import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
@@ -50,8 +50,19 @@ const SelectableListItem = ({
50
50
  className
51
51
  )
52
52
 
53
+ const initialCheckedState = checked
54
+ const [checkedState, setCheckedState] = useState(initialCheckedState)
55
+
56
+ const handleChecked = (event) => {
57
+ onChange(event)
58
+ setCheckedState(event.target.checked)
59
+ }
60
+
53
61
  return (
54
- <ListItem {...props}>
62
+ <ListItem
63
+ {...props}
64
+ className={classnames(checkedState ? "checked_item" : "", className)}
65
+ >
55
66
  <div
56
67
  {...ariaProps}
57
68
  {...dataProps}
@@ -60,10 +71,10 @@ const SelectableListItem = ({
60
71
  <Choose>
61
72
  <When condition={variant == 'checkbox'}>
62
73
  <Checkbox
63
- checked={checked}
74
+ checked={checkedState}
64
75
  id={id}
65
76
  name={name}
66
- onChange={onChange}
77
+ onChange={handleChecked}
67
78
  // eslint suppressor, text is needed to display on screen
68
79
  text={label || (text && false)}
69
80
  type="checkbox"
@@ -1,5 +1,5 @@
1
1
  /* @flow */
2
- import React from 'react'
2
+ import React, { useState } from 'react'
3
3
  import classnames from 'classnames'
4
4
 
5
5
  import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
@@ -29,7 +29,29 @@ const SelectableList = (props: SelectableListProps) => {
29
29
  const ariaProps = buildAriaProps(aria)
30
30
  const classes = classnames(buildCss('pb_selectable_list_kit'), globalProps(props), className)
31
31
  const dataProps = buildDataProps(data)
32
+ const isRadio = props.variant === "radio"
33
+ const defaultCheckedRadioValue = children.filter(item => item.props.defaultChecked)[0]?.props?.value
34
+ const [selectedRadioValue, setSelectedRadioValue] = useState(defaultCheckedRadioValue)
35
+
36
+ const onChangeRadioValue = ({ target }) => {
37
+ setSelectedRadioValue(target.value)
38
+ }
32
39
 
40
+ let selectableListItems = children
41
+
42
+ if (isRadio) {
43
+ selectableListItems = children.map(({ props }) => {
44
+ return (
45
+ <SelectableListItem
46
+ {...props}
47
+ className={classnames(selectedRadioValue === props.value ? "checked_item" : "", props.className)}
48
+ key={props.value}
49
+ onChange={evt => { onChangeRadioValue(evt); props?.onChange?.(evt); }}
50
+ />
51
+ )
52
+ })
53
+ }
54
+
33
55
  return (
34
56
  <div
35
57
  {...ariaProps}
@@ -38,7 +60,7 @@ const SelectableList = (props: SelectableListProps) => {
38
60
  id={id}
39
61
  >
40
62
  <List {...props}>
41
- {children}
63
+ {selectableListItems}
42
64
  </List>
43
65
  </div>
44
66
  )
@@ -7,6 +7,9 @@
7
7
  &:hover {
8
8
  background-color: $bg_light;
9
9
  }
10
+ &[class*=checked_item] {
11
+ background-color: $bg_light;
12
+ }
10
13
  }
11
14
  [class^=pb_radio_kit] {
12
15
  margin-left: $space_xs;
@@ -5,7 +5,7 @@
5
5
  id: object.id) do %>
6
6
  <%= pb_rails("list") do %>
7
7
  <% object.items.each do |item| %>
8
- <%= pb_rails("selectable_list/selectable_list_item", props: item.merge(variant: object.variant) )%>
8
+ <%= pb_rails("selectable_list/selectable_list_item", props: item.merge(variant: object.variant, id: object.get_id(item)) )%>
9
9
  <% end %>
10
10
  <% end %>
11
11
  <% end %>
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "securerandom"
4
+
3
5
  module Playbook
4
6
  module PbSelectableList
5
7
  class SelectableList < Playbook::KitBase
@@ -15,6 +17,10 @@ module Playbook
15
17
  def classname
16
18
  generate_classname("pb_selectable_list_kit")
17
19
  end
20
+
21
+ def get_id(item)
22
+ item[:id] || ("a".."z").to_a.sample(12).join
23
+ end
18
24
  end
19
25
  end
20
26
  end
@@ -14,4 +14,36 @@
14
14
  <%= content %>
15
15
  <% end %>
16
16
  <% end %>
17
+
18
+ <% if object.variant == "checkbox"%>
19
+ <script>
20
+ var checkboxElement = document.querySelector("#<%=object.id%> input[type=checkbox]")
21
+
22
+ checkboxElement.addEventListener("change", (evt) => {
23
+ var listItemElement = document.querySelector("#<%=object.id%>")
24
+
25
+ if (evt.target.checked) {
26
+ listItemElement.classList.add("checked_item");
27
+ } else {
28
+ listItemElement.classList.remove("checked_item")
29
+ }
30
+ })
31
+ </script>
32
+ <% else %>
33
+ <script>
34
+ var radioElement = document.querySelector("#<%=object.id%> input[type=radio]")
35
+
36
+ radioElement.addEventListener("change", () => {
37
+ var radios = radioElement.closest("ul").querySelectorAll("input[type=radio]")
38
+
39
+ radios.forEach((radio) => {
40
+ if (radio.checked) {
41
+ radio.closest("li").classList.add("checked_item");
42
+ } else {
43
+ radio.closest("li").classList.remove("checked_item")
44
+ }
45
+ });
46
+ })
47
+ </script>
48
+ <% end %>
17
49
  <% end %>
@@ -4,7 +4,6 @@ module Playbook
4
4
  module PbSelectableList
5
5
  class SelectableListItem < Playbook::KitBase
6
6
  prop :tabindex
7
-
8
7
  prop :checked, type: Playbook::Props::Boolean,
9
8
  default: false
10
9
  prop :name, type: Playbook::Props::String
@@ -17,7 +16,13 @@ module Playbook
17
16
  default: {}
18
17
 
19
18
  def classname
20
- generate_classname("pb_item_kit")
19
+ generate_classname("pb_item_kit") + checked_class
20
+ end
21
+
22
+ private
23
+
24
+ def checked_class
25
+ checked ? " checked_item" : ""
21
26
  end
22
27
  end
23
28
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Playbook
4
- PREVIOUS_VERSION = "11.12.0"
5
- VERSION = "11.12.1"
4
+ PREVIOUS_VERSION = "11.12.1"
5
+ VERSION = "11.13.0"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: playbook_ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 11.12.1
4
+ version: 11.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Power UX
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-11-22 00:00:00.000000000 Z
12
+ date: 2022-12-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -836,20 +836,26 @@ files:
836
836
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_default.html.erb
837
837
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_default.jsx
838
838
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_default.md
839
+ - app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height.html.erb
839
840
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height.jsx
840
841
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height.md
842
+ - app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height_placement.html.erb
841
843
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height_placement.jsx
842
844
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height_placement.md
845
+ - app/pb_kits/playbook/pb_dialog/docs/_dialog_scrollable.html.erb
843
846
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_scrollable.jsx
844
847
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_scrollable.md
845
848
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_separators.jsx
846
849
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_separators.md
850
+ - app/pb_kits/playbook/pb_dialog/docs/_dialog_should_close_on_overlay.html.erb
847
851
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_should_close_on_overlay.jsx
848
852
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_should_close_on_overlay.md
849
853
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_sizes.html.erb
850
854
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_sizes.jsx
855
+ - app/pb_kits/playbook/pb_dialog/docs/_dialog_stacked_alert.html.erb
851
856
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_stacked_alert.jsx
852
857
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_stacked_alert.md
858
+ - app/pb_kits/playbook/pb_dialog/docs/_dialog_status.html.erb
853
859
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_status.jsx
854
860
  - app/pb_kits/playbook/pb_dialog/docs/_dialog_status.md
855
861
  - app/pb_kits/playbook/pb_dialog/docs/example.yml
@@ -870,8 +876,8 @@ files:
870
876
  - app/pb_kits/playbook/pb_docs/kit_api.rb
871
877
  - app/pb_kits/playbook/pb_docs/kit_example.html.erb
872
878
  - app/pb_kits/playbook/pb_docs/kit_example.rb
873
- - app/pb_kits/playbook/pb_enhanced_element/element_observer.js
874
- - app/pb_kits/playbook/pb_enhanced_element/index.js
879
+ - app/pb_kits/playbook/pb_enhanced_element/element_observer.ts
880
+ - app/pb_kits/playbook/pb_enhanced_element/index.ts
875
881
  - app/pb_kits/playbook/pb_file_upload/_file_upload.scss
876
882
  - app/pb_kits/playbook/pb_file_upload/_file_upload.tsx
877
883
  - app/pb_kits/playbook/pb_file_upload/docs/_description.md
@@ -879,6 +885,7 @@ files:
879
885
  - app/pb_kits/playbook/pb_file_upload/docs/_file_upload_custom_description.jsx
880
886
  - app/pb_kits/playbook/pb_file_upload/docs/_file_upload_custom_description.md
881
887
  - app/pb_kits/playbook/pb_file_upload/docs/_file_upload_default.jsx
888
+ - app/pb_kits/playbook/pb_file_upload/docs/_file_upload_max_size.jsx
882
889
  - app/pb_kits/playbook/pb_file_upload/docs/example.yml
883
890
  - app/pb_kits/playbook/pb_file_upload/docs/index.js
884
891
  - app/pb_kits/playbook/pb_file_upload/fileupload.test.js