playbook_ui 11.12.1 → 11.13.0

Sign up to get free protection for your applications and to get access to all the features.
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