playbook_ui 14.16.0.pre.rc.6 → 14.16.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_bar_graph/_bar_graph.tsx +1 -1
  3. data/app/pb_kits/playbook/pb_circle_chart/_circle_chart.tsx +1 -1
  4. data/app/pb_kits/playbook/pb_draggable/_draggable.scss +115 -5
  5. data/app/pb_kits/playbook/pb_draggable/_draggable.tsx +6 -4
  6. data/app/pb_kits/playbook/pb_draggable/context/index.tsx +35 -9
  7. data/app/pb_kits/playbook/pb_draggable/context/types.ts +35 -28
  8. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones.jsx +184 -0
  9. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones.md +5 -0
  10. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones_colors.jsx +97 -0
  11. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones_colors.md +1 -0
  12. data/app/pb_kits/playbook/pb_draggable/subcomponents/DraggableContainer.tsx +11 -2
  13. data/app/pb_kits/playbook/pb_draggable/subcomponents/DraggableItem.tsx +65 -6
  14. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_breakpoints.html.erb +3 -0
  15. data/app/pb_kits/playbook/pb_drawer/docs/example.yml +1 -0
  16. data/app/pb_kits/playbook/pb_filter/Filter/SortMenu.tsx +2 -1
  17. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.scss +2 -2
  18. data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +67 -0
  19. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +67 -0
  20. data/app/pb_kits/playbook/pb_gauge/_gauge.tsx +1 -1
  21. data/app/pb_kits/playbook/pb_icon/_icon.scss +8 -1
  22. data/app/pb_kits/playbook/pb_icon/docs/_icon_color.html.erb +10 -4
  23. data/app/pb_kits/playbook/pb_icon/docs/_icon_color.jsx +49 -24
  24. data/app/pb_kits/playbook/pb_icon_button/_icon_button.tsx +4 -1
  25. data/app/pb_kits/playbook/pb_icon_button/docs/_icon_button_click.jsx +13 -0
  26. data/app/pb_kits/playbook/pb_icon_button/docs/example.yml +1 -0
  27. data/app/pb_kits/playbook/pb_icon_button/docs/index.js +1 -0
  28. data/app/pb_kits/playbook/pb_lightbox/Carousel/Slide.tsx +1 -1
  29. data/app/pb_kits/playbook/pb_lightbox/Carousel/Slides.tsx +1 -1
  30. data/app/pb_kits/playbook/pb_lightbox/Carousel/Thumbnails.tsx +1 -1
  31. data/app/pb_kits/playbook/pb_line_graph/_line_graph.tsx +1 -1
  32. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +23 -0
  33. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +26 -0
  34. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_error.html.erb +72 -0
  35. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_error.jsx +97 -0
  36. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.html.erb +71 -0
  37. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.jsx +91 -0
  38. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +4 -0
  39. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +3 -1
  40. data/app/pb_kits/playbook/pb_multi_level_select/index.js +105 -0
  41. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +10 -0
  42. data/app/pb_kits/playbook/pb_nav/_nav.scss +5 -0
  43. data/app/pb_kits/playbook/pb_treemap_chart/_treemap_chart.tsx +1 -1
  44. data/app/pb_kits/playbook/pb_typeahead/_typeahead.scss +4 -0
  45. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +3 -0
  46. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_disabled.html.erb +19 -0
  47. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_disabled.jsx +23 -0
  48. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
  49. data/app/pb_kits/playbook/pb_typeahead/docs/index.js +1 -0
  50. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +3 -0
  51. data/app/pb_kits/playbook/pb_user/_user.tsx +78 -13
  52. data/app/pb_kits/playbook/pb_user/docs/_user_font_options.html.erb +22 -0
  53. data/app/pb_kits/playbook/pb_user/docs/_user_font_options.jsx +40 -0
  54. data/app/pb_kits/playbook/pb_user/docs/_user_font_options_rails.md +5 -0
  55. data/app/pb_kits/playbook/pb_user/docs/_user_font_options_react.md +5 -0
  56. data/app/pb_kits/playbook/pb_user/docs/example.yml +2 -0
  57. data/app/pb_kits/playbook/pb_user/docs/index.js +1 -0
  58. data/app/pb_kits/playbook/pb_user/user.html.erb +27 -6
  59. data/app/pb_kits/playbook/pb_user/user.rb +17 -1
  60. data/app/pb_kits/playbook/pb_user/user.test.js +182 -1
  61. data/app/pb_kits/playbook/tokens/_colors.scss +1 -4
  62. data/app/pb_kits/playbook/utilities/object.test.js +139 -1
  63. data/app/pb_kits/playbook/utilities/object.ts +86 -0
  64. data/app/pb_kits/playbook/utilities/text.ts +1 -1
  65. data/dist/chunks/_typeahead-BuTZG1Jn.js +22 -0
  66. data/dist/chunks/_weekday_stacked-oT22q75-.js +45 -0
  67. data/dist/chunks/lazysizes-DHz07jlL.js +1 -0
  68. data/dist/chunks/{lib-BeKPJYlk.js → lib-Co5y3V4K.js} +2 -2
  69. data/dist/chunks/{pb_form_validation-BvDxpfs-.js → pb_form_validation-DMajaRt3.js} +1 -1
  70. data/dist/chunks/vendor.js +1 -1
  71. data/dist/playbook-doc.js +1 -1
  72. data/dist/playbook-rails-react-bindings.js +1 -1
  73. data/dist/playbook-rails.js +1 -1
  74. data/dist/playbook.css +1 -1
  75. data/lib/playbook/forms/builder/multi_level_select_field.rb +2 -0
  76. data/lib/playbook/version.rb +1 -1
  77. metadata +23 -7
  78. data/dist/chunks/_typeahead-CRAPc8k-.js +0 -22
  79. data/dist/chunks/_weekday_stacked-T0kFfioG.js +0 -45
  80. data/dist/chunks/lazysizes-B7xYodB-.js +0 -1
@@ -7,6 +7,8 @@ import { GlobalProps, globalProps } from '../utilities/globalProps'
7
7
  import Avatar from '../pb_avatar/_avatar'
8
8
  import Body from '../pb_body/_body'
9
9
  import Title from '../pb_title/_title'
10
+ import Caption from '../pb_caption/_caption'
11
+ import Detail from '../pb_detail/_detail'
10
12
 
11
13
  type UserProps = {
12
14
  align?: "left" | "center" | "right",
@@ -20,11 +22,13 @@ type UserProps = {
20
22
  htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
21
23
  id?: string,
22
24
  name?: string,
25
+ nameStyle?: "title" | "body" | "caption" | "detail"
23
26
  orientation?: "horizontal" | "vertical",
24
27
  size?: "sm" | "md" | "lg",
25
28
  subtitle?: string | Array<Node> | Node,
26
29
  territory?: string,
27
30
  title?: string,
31
+ titleStyle?: "title" | "body" | "caption" | "detail",
28
32
  } & GlobalProps
29
33
 
30
34
  const User = (props: UserProps): React.ReactElement => {
@@ -40,11 +44,13 @@ const User = (props: UserProps): React.ReactElement => {
40
44
  htmlOptions = {},
41
45
  id,
42
46
  name,
47
+ nameStyle = 'title',
43
48
  orientation = 'horizontal',
44
49
  size = 'sm',
45
50
  subtitle,
46
51
  territory = '',
47
52
  title = '',
53
+ titleStyle = 'body',
48
54
  } = props
49
55
 
50
56
  const dataProps: {[key: string]: string} = buildDataProps(data)
@@ -58,6 +64,76 @@ const User = (props: UserProps): React.ReactElement => {
58
64
  )
59
65
 
60
66
  const avatarPresent = avatar || avatarUrl
67
+ const titleText = territory === '' ? title : `${territory} • ${title}`
68
+
69
+ const renderNameComponent = () => {
70
+ switch (nameStyle) {
71
+ case "title":
72
+ return (
73
+ <Title
74
+ bold={bold}
75
+ dark={dark}
76
+ size={size === "lg" ? 3 : 4}
77
+ text={name}
78
+ />
79
+ );
80
+ case "body":
81
+ return (
82
+ <Body
83
+ dark={dark}
84
+ text={name}
85
+ />
86
+ );
87
+ case "caption":
88
+ return (
89
+ <Caption
90
+ dark={dark}
91
+ size={size === "sm" ? "xs" : size}
92
+ text={name}
93
+ />
94
+ );
95
+ case "detail":
96
+ return (
97
+ <Detail
98
+ dark={dark}
99
+ text={name}
100
+ />
101
+ );
102
+ default:
103
+ return null;
104
+ }
105
+ };
106
+
107
+ const renderTitleComponent = () => {
108
+ switch (titleStyle) {
109
+ case "body":
110
+ return (
111
+ <Body
112
+ color="light"
113
+ dark={dark}
114
+ text={titleText}
115
+ variant={null}
116
+ />
117
+ );
118
+ case "caption":
119
+ return (
120
+ <Caption
121
+ dark={dark}
122
+ size={size === "sm" ? "xs" : size}
123
+ text={titleText}
124
+ />
125
+ );
126
+ case "detail":
127
+ return (
128
+ <Detail
129
+ dark={dark}
130
+ text={titleText}
131
+ />
132
+ );
133
+ default:
134
+ return null;
135
+ }
136
+ };
61
137
 
62
138
  return (
63
139
  <div
@@ -76,19 +152,8 @@ const User = (props: UserProps): React.ReactElement => {
76
152
  />
77
153
  }
78
154
  <div className="content_wrapper">
79
- <Title
80
- bold={bold}
81
- dark={dark}
82
- size={size == 'lg' ? 3 : 4}
83
- text={name}
84
- />
85
- <Body
86
- color="light"
87
- dark={dark}
88
- variant={null}
89
- >
90
- {territory === '' ? title : `${territory} • ${title}`}
91
- </Body>
155
+ {renderNameComponent()}
156
+ {renderTitleComponent()}
92
157
  { typeof(subtitle) === 'string' &&
93
158
  <Body
94
159
  color="light"
@@ -0,0 +1,22 @@
1
+ <div class="pb--doc-demo-row">
2
+ <%= pb_rails("user", props: {
3
+ align: "left",
4
+ avatar_url: "https://randomuser.me/api/portraits/women/44.jpg",
5
+ name: "Anna Black",
6
+ name_style: "body",
7
+ orientation: "horizontal",
8
+ size: "md",
9
+ territory: "PHL",
10
+ title: "Remodeling Consultant"
11
+ }) %>
12
+ <%= pb_rails("user", props: {
13
+ align: "left",
14
+ avatar_url: "https://randomuser.me/api/portraits/women/44.jpg",
15
+ name: "Anna Black",
16
+ name_style: "detail",
17
+ orientation: "horizontal",
18
+ size: "md",
19
+ territory: "PHL",
20
+ title: "Remodeling Consultant"
21
+ }) %>
22
+ </div>
@@ -0,0 +1,40 @@
1
+ import React from 'react'
2
+ import { User } from 'playbook-ui'
3
+
4
+ const UserFontOptions = (props) => {
5
+ return (
6
+ <div>
7
+ <div className="pb--doc-demo-row">
8
+ <div>
9
+ <User
10
+ align="center"
11
+ avatarUrl="https://randomuser.me/api/portraits/women/44.jpg"
12
+ name="Anna Black"
13
+ nameStyle= "body"
14
+ orientation="horizontal"
15
+ size= "md"
16
+ territory= "PHL"
17
+ title="Remodeling Consultant"
18
+ {...props}
19
+ />
20
+ </div>
21
+
22
+ <div>
23
+ <User
24
+ align="left"
25
+ avatarUrl="https://randomuser.me/api/portraits/women/44.jpg"
26
+ name="Anna Black"
27
+ nameStyle= "detail"
28
+ orientation="horizontal"
29
+ size= "md"
30
+ territory= "PHL"
31
+ title= "Remodeling Consultant"
32
+ {...props}
33
+ />
34
+ </div>
35
+ </div>
36
+ </div>
37
+ )
38
+ }
39
+
40
+ export default UserFontOptions
@@ -0,0 +1,5 @@
1
+ Passing a `name_style` prop changes the type kit used for the user name. You can choose between `title`, `body`, `caption`, and `detail`. `title` is the default.
2
+
3
+ Passing a `title_style` prop changes the type kit used for the user title. You can choose between `body`, `caption`, and `detail`. `body` is the default.
4
+
5
+ The size of the `caption` is determined by the `size` prop.
@@ -0,0 +1,5 @@
1
+ Passing a `nameStyle` prop changes the type kit used for the user name. You can choose between `title`, `body`, `caption`, and `detail`. `title` is the default.
2
+
3
+ Passing a `titleStyle` prop changes the type kit used for the user title. You can choose between `body`, `caption`, and `detail`. `body` is the default.
4
+
5
+ The size of the `caption` is determined by the `size` prop.
@@ -9,6 +9,7 @@ examples:
9
9
  - user_vertical_size: Vertical Size
10
10
  - user_subtitle: Subtitle
11
11
  - user_block_content_subtitle_rails: Block Content Subtitle
12
+ - user_font_options: Font Options
12
13
 
13
14
  react:
14
15
  - user_default: Default
@@ -19,6 +20,7 @@ examples:
19
20
  - user_vertical_size: Vertical Size
20
21
  - user_subtitle: Subtitle
21
22
  - user_block_content_subtitle_react: Block Content Subtitle
23
+ - user_font_options: Font Options
22
24
 
23
25
  swift:
24
26
  - user_horizontal_swift: Horizontal
@@ -6,3 +6,4 @@ export { default as UserSize } from './_user_size.jsx'
6
6
  export { default as UserVerticalSize } from './_user_vertical_size.jsx'
7
7
  export { default as UserSubtitle } from './_user_subtitle.jsx'
8
8
  export { default as UserBlockContentSubtitleReact } from './_user_block_content_subtitle_react.jsx'
9
+ export { default as UserFontOptions } from './_user_font_options.jsx'
@@ -12,12 +12,33 @@
12
12
  }) %>
13
13
  <% end %>
14
14
  <%= content_tag(:div, class: "content_wrapper") do %>
15
- <%= pb_rails("title", props: { text: object.name, size: object.title_size, dark: object.dark, bold: object.bold }) %>
16
- <%= pb_rails("body", props: {
17
- text: "#{object.details}",
18
- dark: object.dark,
19
- color: "light"
20
- }) %>
15
+ <% case object.name_style %>
16
+ <% when "title" %>
17
+ <%= pb_rails("title", props: { text: object.name, size: object.name_size, dark: object.dark, bold: object.bold }) %>
18
+ <% when "body" %>
19
+ <%= pb_rails("body", props: { text: object.name, dark: object.dark }) %>
20
+ <% when "caption" %>
21
+ <%= pb_rails("caption", props: { text: object.name, dark: object.dark, size: object.name_size }) %>
22
+ <% when "detail" %>
23
+ <%= pb_rails("detail", props: { text: object.name, dark: object.dark }) %>
24
+ <% end %>
25
+ <% case object.title_style %>
26
+ <% when "body" %>
27
+ <%= pb_rails("body", props: {
28
+ text: "#{object.details}",
29
+ dark: object.dark,
30
+ color: "light"
31
+ }) %>
32
+ <% when "caption" %>
33
+ <%= pb_rails("caption", props: {
34
+ text: "#{object.details}",
35
+ dark: object.dark,
36
+ color: "light",
37
+ size: object.title_size
38
+ }) %>
39
+ <% when "detail" %>
40
+ <%= pb_rails("detail", props: { text: "#{object.details}", dark: object.dark }) %>
41
+ <% end %>
21
42
  <% if content %>
22
43
  <%= content.presence %>
23
44
  <% else %>
@@ -11,6 +11,9 @@ module Playbook
11
11
  prop :avatar_url
12
12
  prop :bold, type: Playbook::Props::Boolean, default: true
13
13
  prop :name
14
+ prop :name_style, type: Playbook::Props::Enum,
15
+ values: %w[title body caption detail],
16
+ default: "title"
14
17
  prop :orientation, type: Playbook::Props::Enum,
15
18
  values: %w[vertical horizontal],
16
19
  default: "horizontal"
@@ -19,6 +22,9 @@ module Playbook
19
22
  default: "sm"
20
23
  prop :subtitle
21
24
  prop :title
25
+ prop :title_style, type: Playbook::Props::Enum,
26
+ values: %w[body caption detail],
27
+ default: "body"
22
28
  prop :territory
23
29
 
24
30
  def classname
@@ -36,8 +42,18 @@ module Playbook
36
42
  end
37
43
  end
38
44
 
45
+ def name_size
46
+ if name_style == "caption"
47
+ size == "sm" ? "xs" : size
48
+ else
49
+ size == "lg" ? 3 : 4
50
+ end
51
+ end
52
+
39
53
  def title_size
40
- size == "lg" ? 3 : 4
54
+ if title_style == "caption"
55
+ size == "sm" ? "xs" : size
56
+ end
41
57
  end
42
58
 
43
59
  def details
@@ -1,5 +1,5 @@
1
1
  import React from 'react'
2
- import { render, screen } from '../utilities/test-utils'
2
+ import { render, screen , cleanup} from '../utilities/test-utils'
3
3
  import User from './_user'
4
4
  import Caption from "../pb_caption/_caption"
5
5
 
@@ -40,3 +40,184 @@ test('bold prop applies correct styling when false', () => {
40
40
 
41
41
  expect(titleElement).toHaveClass('pb_title_kit_size_4_thin')
42
42
  })
43
+
44
+ test('align prop adds desired class', () => {
45
+ [
46
+ "left",
47
+ "center",
48
+ "right"
49
+ ].forEach((alignProp) => {
50
+ render(
51
+ <User
52
+ align={alignProp}
53
+ data={{ testid: 'test-user-kit' }}
54
+ name="Anna Black"
55
+ />
56
+ )
57
+ const kit = screen.getByTestId('test-user-kit')
58
+ expect(kit.className).toContain(`pb_user_kit_${alignProp}`)
59
+
60
+ cleanup()
61
+ })
62
+ })
63
+
64
+ test('avatar prop adds default avatar img', () => {
65
+ render(
66
+ <User
67
+ avatar
68
+ data={{ testid: 'test-user-kit' }}
69
+ />
70
+ )
71
+
72
+ const container = screen.getByTestId('test-user-kit')
73
+ const avatarWrapper = container.querySelector('.avatar_wrapper')
74
+
75
+ expect(avatarWrapper).toBeInTheDocument()
76
+ expect(avatarWrapper).toHaveClass('avatar_wrapper')
77
+ })
78
+
79
+ test('avatarUrl prop adds avatar img', () => {
80
+ const avatarUrl = 'https://example.com/avatar.jpg'
81
+ render(
82
+ <User
83
+ avatarUrl={avatarUrl}
84
+ data={{ testid: 'test-user-kit' }}
85
+ />
86
+ )
87
+
88
+ const container = screen.getByTestId('test-user-kit')
89
+ const avatarImg = container.querySelector('img')
90
+
91
+ expect(avatarImg).toBeInTheDocument()
92
+ expect(avatarImg).toHaveAttribute('src', avatarUrl)
93
+ })
94
+
95
+ test('name prop adds name text', () => {
96
+ const name = 'Anna Black'
97
+ render(
98
+ <User
99
+ data={{ testid: 'test-name' }}
100
+ name={name}
101
+ />
102
+ )
103
+
104
+ const titleElement = screen.getByText(name)
105
+ expect(titleElement).toBeInTheDocument()
106
+ })
107
+
108
+ test('nameStyle prop changes the typography kit', () => {
109
+ const name = 'Anna Black'
110
+ const kitTypes = ['body', 'caption', 'detail']
111
+
112
+ kitTypes.forEach((typeKit) => {
113
+ render(
114
+ <User
115
+ data={{ testid: 'test-user-kit' }}
116
+ name={name}
117
+ nameStyle={typeKit}
118
+ />
119
+ )
120
+
121
+ const container = screen.getByTestId('test-user-kit')
122
+ const subcomponent = container.querySelector(`[class*="pb_${typeKit}_kit"]`)
123
+
124
+ expect(subcomponent).toBeInTheDocument()
125
+ expect(subcomponent).toHaveTextContent(name)
126
+
127
+ cleanup()
128
+ })
129
+ })
130
+
131
+ test('orientation prop adds desired class', () => {
132
+ [
133
+ "horizontal",
134
+ "vertical"
135
+ ].forEach((orientation) => {
136
+ render(
137
+ <User
138
+ data={{ testid: 'test-user-kit' }}
139
+ name="Anna Black"
140
+ orientation={orientation}
141
+ />
142
+ )
143
+
144
+ const container = screen.getByTestId('test-user-kit')
145
+ expect(container.className).toContain(orientation)
146
+
147
+ cleanup()
148
+ })
149
+ })
150
+
151
+ test('size prop adds desired class', () => {
152
+ [
153
+ "sm",
154
+ "md",
155
+ "lg"
156
+ ].forEach((size) => {
157
+ render(
158
+ <User
159
+ data={{ testid: 'test-user-kit' }}
160
+ name="Anna Black"
161
+ size={size}
162
+ />
163
+ )
164
+
165
+ const container = screen.getByTestId('test-user-kit')
166
+ expect(container.className).toContain(size)
167
+
168
+ cleanup()
169
+ })
170
+ })
171
+
172
+ test('title prop adds title text', () => {
173
+ const title = 'Remodeling Consultant'
174
+ render(
175
+ <User
176
+ data={{ testid: 'test-title' }}
177
+ name="Anna Black"
178
+ title={title}
179
+ />
180
+ )
181
+
182
+ const titleElement = screen.getByText(title)
183
+ expect(titleElement).toBeInTheDocument()
184
+ })
185
+
186
+ test('titleStyle prop changes the typography kit', () => {
187
+ const title = 'Remodeling Consultant'
188
+ const kitTypes = ['caption', 'detail']
189
+
190
+ kitTypes.forEach((typeKit) => {
191
+ render(
192
+ <User
193
+ data={{ testid: 'test-user-kit' }}
194
+ name="Anna Black"
195
+ title={title}
196
+ titleStyle={typeKit}
197
+ />
198
+ )
199
+
200
+ const container = screen.getByTestId('test-user-kit')
201
+ const subcomponent = container.querySelector(`[class*="pb_${typeKit}_kit"]`)
202
+
203
+ expect(subcomponent).toBeInTheDocument()
204
+ expect(subcomponent).toHaveTextContent(title)
205
+
206
+ cleanup()
207
+ })
208
+ })
209
+
210
+ test('territory prop adds territory text', () => {
211
+ const territory = 'PHI'
212
+ render(
213
+ <User
214
+ data={{ testid: 'test-territory' }}
215
+ name="Anna Black"
216
+ territory={territory}
217
+ title="Remodeling Consultant"
218
+ />
219
+ )
220
+
221
+ const titleElement = screen.getByText(`${territory} • Remodeling Consultant`)
222
+ expect(titleElement).toBeInTheDocument()
223
+ })
@@ -41,8 +41,7 @@ $interface_colors: (
41
41
  silver: $silver,
42
42
  slate: $slate,
43
43
  charcoal: $charcoal,
44
- black: $black,
45
-
44
+ black: $black
46
45
  );
47
46
 
48
47
  /* Main colors ------------------------*/
@@ -205,8 +204,6 @@ $status_colors: (
205
204
  primary_secondary: $primary_secondary
206
205
  );
207
206
 
208
-
209
-
210
207
  $status_color_text: (
211
208
  success: $success,
212
209
  success_sm: $text_lt_success_sm,
@@ -1,4 +1,4 @@
1
- import { isEmpty, get, isString, uniqueId, omitBy } from './object';
1
+ import { isEmpty, get, isString, uniqueId, omitBy, noop, merge, filter, find, partial } from './object';
2
2
 
3
3
  describe('Lodash functions', () => {
4
4
  describe('isEmpty', () => {
@@ -96,4 +96,142 @@ describe('Lodash functions', () => {
96
96
  expect(objWithSmallValues).toEqual(obj);
97
97
  });
98
98
  });
99
+
100
+ describe('noop', () => {
101
+ test('should do nothing and return undefined', () => {
102
+ expect(noop()).toBeUndefined();
103
+ });
104
+ });
105
+
106
+ describe('merge', () => {
107
+ test('merges two objects correctly', () => {
108
+ const obj1 = { a: 1, b: { x: 10 } };
109
+ const obj2 = { b: { y: 20 }, c: 3 };
110
+ expect(merge(obj1, obj2)).toEqual({ a: 1, b: { x: 10, y: 20 }, c: 3 });
111
+ });
112
+
113
+ test('when keys repeat use last occurrence value', () => {
114
+ const obj1 = { a: 1 };
115
+ const obj2 = { b: 2 };
116
+ const obj3 = { a: 3, c: 4 };
117
+ expect(merge(obj1, obj2, obj3)).toEqual({ a: 3, b: 2, c: 4 });
118
+ });
119
+
120
+ test('ignores non-object arguments', () => {
121
+ expect(merge(null, { a: 1 })).toEqual({ a: 1 });
122
+ expect(merge(undefined, { a: 1 })).toEqual({ a: 1 });
123
+ });
124
+ });
125
+
126
+ describe('filter', () => {
127
+ test('filters an array using a function predicate', () => {
128
+ const arr = [1, 2, 3, 4, 5];
129
+ const isEven = (n) => n % 2 === 0;
130
+ expect(filter(arr, isEven)).toEqual([2, 4]);
131
+ });
132
+
133
+ test('filters an array using a string predicate', () => {
134
+ const arr = [
135
+ { active: true, name: 'John' },
136
+ { active: false, name: 'Jane' },
137
+ { active: true, name: 'Doe' }
138
+ ];
139
+ expect(filter(arr, 'active')).toEqual([
140
+ { active: true, name: 'John' },
141
+ { active: true, name: 'Doe' }
142
+ ]);
143
+ });
144
+
145
+ test('filters an array using an array predicate', () => {
146
+ const arr = [
147
+ { type: 'fruit', name: 'apple' },
148
+ { type: 'vegetable', name: 'carrot' },
149
+ { type: 'fruit', name: 'banana' }
150
+ ];
151
+ expect(filter(arr, ['type', 'fruit'])).toEqual([
152
+ { type: 'fruit', name: 'apple' },
153
+ { type: 'fruit', name: 'banana' }
154
+ ]);
155
+ });
156
+
157
+ test('filters an array using an object predicate', () => {
158
+ const arr = [
159
+ { type: 'fruit', name: 'apple', color: 'red' },
160
+ { type: 'fruit', name: 'banana', color: 'yellow' },
161
+ { type: 'vegetable', name: 'carrot', color: 'orange' }
162
+ ];
163
+ expect(filter(arr, { type: 'fruit', color: 'red' })).toEqual([
164
+ { type: 'fruit', name: 'apple', color: 'red' }
165
+ ]);
166
+ });
167
+ });
168
+
169
+ describe('find', () => {
170
+ test('finds an element using a function predicate', () => {
171
+ const arr = [1, 2, 3, 4, 5];
172
+ const greaterThanThree = (n) => n > 3;
173
+ expect(find(arr, greaterThanThree)).toBe(4);
174
+ });
175
+
176
+ test('finds an element using a string predicate', () => {
177
+ const arr = [
178
+ { active: false, name: 'John' },
179
+ { active: true, name: 'Jane' },
180
+ { active: true, name: 'Doe' }
181
+ ];
182
+ expect(find(arr, 'active')).toEqual({ active: true, name: 'Jane' });
183
+ });
184
+
185
+ test('finds an element using an array predicate', () => {
186
+ const arr = [
187
+ { type: 'fruit', name: 'apple' },
188
+ { type: 'vegetable', name: 'carrot' },
189
+ { type: 'fruit', name: 'banana' }
190
+ ];
191
+ expect(find(arr, ['type', 'vegetable'])).toEqual({ type: 'vegetable', name: 'carrot' });
192
+ });
193
+
194
+ test('finds an element using an object predicate', () => {
195
+ const arr = [
196
+ { type: 'fruit', name: 'apple', color: 'red' },
197
+ { type: 'fruit', name: 'banana', color: 'yellow' },
198
+ { type: 'vegetable', name: 'carrot', color: 'orange' }
199
+ ];
200
+ expect(find(arr, { name: 'banana', color: 'yellow' })).toEqual({ type: 'fruit', name: 'banana', color: 'yellow' });
201
+ });
202
+
203
+ test('returns undefined if no element matches', () => {
204
+ const arr = [{ id: 1 }, { id: 2 }];
205
+ expect(find(arr, { id: 3 })).toBeUndefined();
206
+ });
207
+ });
208
+
209
+ describe('partial', () => {
210
+ function add(a, b, c) {
211
+ return a + b + c;
212
+ }
213
+
214
+ test('partials arguments without placeholders', () => {
215
+ const add5 = partial(add, 2, 3);
216
+ expect(add5(4)).toBe(9);
217
+ });
218
+
219
+ test('partials arguments with placeholders', () => {
220
+ const addWithPlaceholder = partial(add, partial.placeholder, 3, partial.placeholder);
221
+ expect(addWithPlaceholder(2, 4)).toBe(9);
222
+ });
223
+
224
+ test('returns correct result when all arguments are pre-filled', () => {
225
+ const addAll = partial(add, 1, 2, 3);
226
+ expect(addAll()).toBe(6);
227
+ });
228
+
229
+ test('appends extra arguments when provided', () => {
230
+ function join(...args) {
231
+ return args.join('_');
232
+ }
233
+ const joinPartial = partial(join, 'a');
234
+ expect(joinPartial('b', 'c')).toBe('a_b_c');
235
+ });
236
+ });
99
237
  });