playbook_ui 12.39.0 → 13.0.0.pre.alpha.PLAY966collapsiblenav41126

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_collapsible/child_kits/CollapsibleMain.tsx +1 -1
  3. data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +18 -8
  4. data/app/pb_kits/playbook/pb_date_picker/plugins/quickPick.tsx +45 -39
  5. data/app/pb_kits/playbook/pb_kit/dateTime.ts +204 -1
  6. data/app/pb_kits/playbook/pb_nav/_bold_mixin.scss +16 -14
  7. data/app/pb_kits/playbook/pb_nav/_collapsible_nav.scss +86 -115
  8. data/app/pb_kits/playbook/pb_nav/_collapsible_trail_mixin.scss +14 -0
  9. data/app/pb_kits/playbook/pb_nav/_horizontal_nav.scss +58 -57
  10. data/app/pb_kits/playbook/pb_nav/_item.tsx +234 -138
  11. data/app/pb_kits/playbook/pb_nav/_nav.scss +38 -0
  12. data/app/pb_kits/playbook/pb_nav/_nav.tsx +17 -1
  13. data/app/pb_kits/playbook/pb_nav/_subtle_mixin.scss +9 -11
  14. data/app/pb_kits/playbook/pb_nav/_vertical_nav.scss +50 -58
  15. data/app/pb_kits/playbook/pb_nav/docs/_block_no_title_nav.jsx +1 -1
  16. data/app/pb_kits/playbook/pb_nav/docs/_borderless_nav.jsx +4 -1
  17. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav.html.erb +4 -4
  18. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav.jsx +4 -10
  19. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav_custom.jsx +5 -1
  20. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav_emphasize.html.erb +12 -12
  21. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav_emphasize.jsx +13 -1
  22. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav_emphasize.md +1 -1
  23. data/app/pb_kits/playbook/pb_nav/docs/_no_highlight_nav.jsx +4 -1
  24. data/app/pb_kits/playbook/pb_nav/docs/_subtle_nav.jsx +4 -1
  25. data/app/pb_kits/playbook/pb_nav/docs/_subtle_with_icons_nav.jsx +4 -1
  26. data/app/pb_kits/playbook/pb_nav/docs/example.yml +5 -5
  27. data/app/pb_kits/playbook/pb_nav/item.html.erb +19 -12
  28. data/app/pb_kits/playbook/pb_nav/item.rb +55 -14
  29. data/app/pb_kits/playbook/pb_nav/nav.html.erb +3 -1
  30. data/app/pb_kits/playbook/pb_nav/navTypes.ts +25 -0
  31. data/dist/playbook-rails.js +7 -279
  32. data/lib/playbook/version.rb +2 -2
  33. metadata +9 -8
  34. data/app/pb_kits/playbook/pb_nav/_mixins.scss +0 -5
@@ -1,42 +1,64 @@
1
- import React from 'react'
2
- import classnames from 'classnames'
1
+ import React from "react";
2
+ import classnames from "classnames";
3
3
 
4
- import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
5
- import { globalProps, GlobalProps } from '../utilities/globalProps'
4
+ import { buildAriaProps, buildCss, buildDataProps } from "../utilities/props";
5
+ import { globalProps, GlobalProps } from "../utilities/globalProps";
6
6
 
7
- import Icon from '../pb_icon/_icon'
8
- import Image from '../pb_image/_image'
9
- import Collapsible from '../pb_collapsible/_collapsible'
7
+ import Icon from "../pb_icon/_icon";
8
+ import Image from "../pb_image/_image";
9
+ import Collapsible from "../pb_collapsible/_collapsible";
10
+ import { NavChildProps } from "./navTypes";
11
+ import { Spacing } from "../types";
10
12
 
11
13
  type NavItemProps = {
12
- active?: boolean,
13
- aria?: { [key: string]: string },
14
- fontWeight?: "regular" | "bold" | "bolder",
15
- children?: React.ReactNode[] | React.ReactNode,
16
- className?: string,
17
- collapsible?: boolean,
18
- data?: object,
19
- dark?: boolean,
20
- fontSize?: "normal" | "small",
21
- iconLeft?: string,
22
- iconRight?: string | string[],
23
- onIconRightClick?: () => void,
24
- onIconLeftClick?: () => void,
25
- id?: string,
26
- imageUrl?: string,
27
- link?: string,
28
- onClick?: () => void,
29
- target?: '_blank' | '_self' | '_parent' | '_top',
30
- text: string,
31
- collapsibleTrail?: boolean,
32
- collapsed?: boolean
33
- } & GlobalProps
14
+ active?: boolean;
15
+ aria?: { [key: string]: string };
16
+ fontWeight?: "regular" | "bold" | "bolder";
17
+ children?: React.ReactNode[] | React.ReactNode;
18
+ className?: string;
19
+ collapsible?: boolean;
20
+ data?: object;
21
+ dark?: boolean;
22
+ fontSize?: "normal" | "small";
23
+ iconLeft?: string;
24
+ iconRight?: string | string[];
25
+ onIconRightClick?: () => void;
26
+ onIconLeftClick?: () => void;
27
+ id?: string;
28
+ imageUrl?: string;
29
+ link?: string;
30
+ onClick?: () => void;
31
+ target?: "_blank" | "_self" | "_parent" | "_top";
32
+ text: string;
33
+ collapsibleTrail?: boolean;
34
+ collapsed?: boolean;
35
+ orientation?: "vertical" | "horizontal";
36
+ variant?: "normal" | "subtle";
37
+ margin?: Spacing;
38
+ marginBottom?: Spacing;
39
+ marginTop?: Spacing;
40
+ marginRight?: Spacing;
41
+ marginLeft?: Spacing;
42
+ marginX?: Spacing;
43
+ marginY?: Spacing;
44
+ } & GlobalProps;
34
45
 
35
46
  const NavItem = (props: NavItemProps) => {
47
+
48
+ const fontWeightDefault = (orientation: string, variant: string) => {
49
+ return orientation === "horizontal" && variant === "subtle"
50
+ ? "regular "
51
+ : orientation === "horizontal" && variant === "normal"
52
+ ? "bold"
53
+ : "regular";
54
+ };
55
+
36
56
  const {
37
57
  active = false,
38
58
  aria = {},
39
- fontWeight = "regular",
59
+ orientation,
60
+ variant,
61
+ fontWeight = fontWeightDefault(orientation, variant),
40
62
  children,
41
63
  className,
42
64
  collapsible,
@@ -50,129 +72,204 @@ const NavItem = (props: NavItemProps) => {
50
72
  id,
51
73
  imageUrl,
52
74
  link,
53
- onClick = () => { },
54
- target = '_self',
55
- text = '',
75
+ onClick = () => {},
76
+ target = "_self",
77
+ text = "",
56
78
  collapsibleTrail,
57
- collapsed
58
- } = props
59
-
60
- const Tag = link ? 'a' : 'div'
61
- const activeClass = active === true ? 'active' : ''
62
- const collapsibleTrailClass = collapsible && collapsibleTrail ? 'collapsible_trail' : ''
63
- const fontSizeClass = fontSize === 'small' ? "font_size_small" : "font_size_normal"
64
- const fontWeightClass = fontWeight === 'bold' ? "font_bold" : fontWeight === 'bolder' ? "font_bolder" : "font_regular"
65
- const ariaProps = buildAriaProps(aria)
66
- const dataProps = buildDataProps(data)
67
- const classes = classnames(buildCss('pb_nav_list_kit_item', activeClass),
68
- collapsible ? buildCss('pb_collapsible_nav_item', activeClass, collapsibleTrailClass) : '',
69
- fontSizeClass,
70
- fontWeightClass,
71
- globalProps(props),
72
- className)
73
-
74
-
75
- const handleIconClick = (e:any) => {
76
- if (onIconLeftClick) {
77
- e.stopPropagation();
78
- onIconLeftClick()
79
+ collapsed,
80
+ itemSpacing,
81
+ margin,
82
+ marginBottom,
83
+ marginTop,
84
+ marginRight,
85
+ marginLeft,
86
+ marginX,
87
+ marginY,
88
+ } = props;
89
+
90
+ const spacingMarginProps = {
91
+ margin,
92
+ marginBottom,
93
+ marginTop,
94
+ marginRight,
95
+ marginLeft,
96
+ marginX,
97
+ marginY,
98
+ };
99
+
100
+ //separate margin props and padding props in itemSpacing object
101
+ const filterItemSpacing = (obj: { [key: string]: string }) => {
102
+ const filteredPadding: { [key: string]: string } = {};
103
+ const filteredMargin: { [key: string]: string } = {};
104
+ for (const key in obj) {
105
+ if (key.startsWith('padding')) {
106
+ filteredPadding[key] = obj[key];
107
+ } else if (key.startsWith('margin')) {
108
+ filteredMargin[key] = obj[key];
79
109
  }
80
110
  }
111
+ return { filteredPadding, filteredMargin };
112
+ };
113
+
114
+ //deconstruct itemSpacing
115
+ const { filteredPadding, filteredMargin } = filterItemSpacing(itemSpacing);
116
+
117
+ //if itemSpacing has margin props, apply those, if margin global props passed to navItem itself, navItem props take precendence
118
+ const finalItemSpacing = {
119
+ ...(filteredMargin || {}),
120
+ ...Object.entries(spacingMarginProps).reduce((acc: any, [prop, value]) => {
121
+ if (value) {
122
+ acc[prop] = value;
123
+ }
124
+ return acc;
125
+ }, {}),
126
+ };
127
+
128
+ //custom for collapsible only, to apply margin to correct div
129
+ const filteredProps = { ...props };
130
+ delete filteredProps?.margin;
131
+ delete filteredProps?.marginX;
132
+ delete filteredProps?.marginY;
133
+ delete filteredProps?.marginBottom;
134
+ delete filteredProps?.marginTop;
135
+ delete filteredProps?.marginRight;
136
+ delete filteredProps?.marginLeft;
137
+
138
+
139
+ const Tag = link ? "a" : "div";
140
+ const activeClass = active === true ? "active" : "";
141
+ const collapsibleTrailClass =
142
+ collapsible && collapsibleTrail ? "collapsible_trail" : "";
143
+ const fontSizeClass =
144
+ fontSize === "small" ? "font_size_small" : "font_size_normal";
145
+ const fontWeightClass =
146
+ fontWeight === "bold"
147
+ ? "font_bold"
148
+ : fontWeight === "bolder"
149
+ ? "font_bolder"
150
+ : "font_regular";
151
+ const ariaProps = buildAriaProps(aria);
152
+ const dataProps = buildDataProps(data);
153
+
154
+ const tagClasses = classnames(
155
+ collapsible ? "pb_nav_list_item_link_collapsible" : "pb_nav_list_item_link",
156
+ );
157
+
158
+ const classes = classnames(
159
+ buildCss("pb_nav_list_kit_item", activeClass),
160
+ collapsible
161
+ ? buildCss("pb_collapsible_nav_item", activeClass)
162
+ : "",
163
+ fontSizeClass,
164
+ fontWeightClass,
165
+ tagClasses,
166
+ collapsible ? globalProps(filteredProps, {...filteredPadding}) : globalProps(props, {...itemSpacing}),
167
+ className
168
+ );
169
+
170
+ const handleIconClick = (e: any) => {
171
+ if (onIconLeftClick) {
172
+ e.stopPropagation();
173
+ onIconLeftClick();
174
+ }
175
+ };
176
+
177
+
178
+ // Map over the children and clone them with itemSpacing prop so nested navItems all get itemSpacing
179
+ const childrenWithProps = React.Children.map(children, (child) => {
180
+ if (React.isValidElement(child)) {
181
+ const childProps: NavChildProps = {
182
+ itemSpacing: itemSpacing,
183
+ };
184
+ return React.cloneElement(child, childProps);
185
+ }
186
+ return child;
187
+ });
81
188
 
82
189
  return (
83
- <li
84
- {...ariaProps}
85
- {...dataProps}
86
- className={classes}
87
- id={id}
88
- >
89
- {
90
- collapsible ? (
91
- <Collapsible icon={iconRight ? iconRight : ['plus','minus']}
92
- iconSize="xs"
190
+ <>
191
+ {collapsible ? (
192
+ <>
193
+ <Collapsible
194
+ className={`collapsible_nav_wrapper_${activeClass}_${collapsibleTrailClass}`}
195
+ icon={iconRight && iconRight}
196
+ iconSize="xs"
93
197
  id={id}
94
198
  collapsed={collapsed}
95
199
  onIconClick={onIconRightClick}
96
200
  onClick={onClick}
97
201
  >
98
- <Collapsible.Main dark={dark}>
99
- <Tag
100
- className="pb_nav_list_item_link_collapsible"
101
- href={link}
102
- target={target}
103
- >
104
- {imageUrl &&
105
- <div
106
- className="pb_nav_list_item_icon_section_collapsible"
107
- key={imageUrl}
108
- onClick={(e)=>handleIconClick(e)}
109
- >
110
- <Image
111
- className="pb_nav_img_wrapper"
112
- url={imageUrl}
113
- />
114
- </div>
115
- }
116
-
117
- {iconLeft &&
118
- <div
119
- className="pb_nav_list_item_icon_section_collapsible"
120
- key={iconLeft}
121
- onClick={(e)=>handleIconClick(e)}
122
- >
123
- <Icon
124
- className="pb_nav_list_item_icon_left_collapsible"
125
- fixedWidth
126
- icon={iconLeft}
127
- />
128
- </div>
129
- }
130
- <span className="pb_nav_list_item_text_collapsible">
131
- {text}
132
- </span>
133
- </Tag>
134
- </Collapsible.Main>
135
- <Collapsible.Content>
136
- {children}
137
- </Collapsible.Content>
138
- </Collapsible>
139
- ) : (
202
+ <Collapsible.Main
203
+ className={globalProps({ ...finalItemSpacing })}
204
+ dark={dark}>
205
+ <Tag
206
+ {...ariaProps}
207
+ {...dataProps}
208
+ className={classes}
209
+ id={id}
210
+ href={link}
211
+ target={target}
212
+ >
213
+ {imageUrl && (
214
+ <div
215
+ className="pb_nav_list_item_icon_section_collapsible"
216
+ key={imageUrl}
217
+ onClick={(e) => handleIconClick(e)}
218
+ >
219
+ <Image className="pb_nav_img_wrapper" url={imageUrl} />
220
+ </div>
221
+ )}
222
+
223
+ {iconLeft && (
224
+ <div
225
+ className="pb_nav_list_item_icon_section_collapsible"
226
+ key={iconLeft}
227
+ onClick={(e) => handleIconClick(e)}
228
+ >
229
+ <Icon
230
+ className="pb_nav_list_item_icon_left_collapsible"
231
+ fixedWidth
232
+ icon={iconLeft}
233
+ />
234
+ </div>
235
+ )}
236
+ <span className="pb_nav_list_item_text_collapsible">
237
+ {text}
238
+ </span>
239
+ </Tag>
240
+ </Collapsible.Main>
241
+ <Collapsible.Content>{childrenWithProps}</Collapsible.Content>
242
+ </Collapsible>
243
+ </>
244
+ ) : (
140
245
  <Tag
141
- className="pb_nav_list_item_link"
246
+ {...ariaProps}
247
+ {...dataProps}
248
+ className={classes}
249
+ id={id}
142
250
  href={link}
143
251
  onClick={onClick}
144
252
  target={target}
145
253
  >
146
- {imageUrl &&
147
- <div
148
- className="pb_nav_list_item_icon_section"
149
- key={imageUrl}
150
- >
151
- <Image
152
- className="pb_nav_img_wrapper"
153
- url={imageUrl}
154
- />
254
+ {imageUrl && (
255
+ <div className="pb_nav_list_item_icon_section" key={imageUrl}>
256
+ <Image className="pb_nav_img_wrapper" url={imageUrl} />
155
257
  </div>
156
- }
157
-
158
- {iconLeft &&
159
- <div
160
- className="pb_nav_list_item_icon_section"
161
- key={iconLeft}
162
- >
258
+ )}
259
+
260
+ {iconLeft && (
261
+ <div className="pb_nav_list_item_icon_section" key={iconLeft}>
163
262
  <Icon
164
263
  className="pb_nav_list_item_icon_left"
165
264
  fixedWidth
166
265
  icon={iconLeft}
167
266
  />
168
267
  </div>
169
- }
170
-
171
- <span className="pb_nav_list_item_text">
172
- {text || children}
173
- </span>
174
-
175
- {iconRight &&
268
+ )}
269
+
270
+ <span className="pb_nav_list_item_text">{text || children}</span>
271
+
272
+ {iconRight && (
176
273
  <div
177
274
  className="pb_nav_list_item_icon_section"
178
275
  key={iconRight as string}
@@ -183,12 +280,11 @@ const NavItem = (props: NavItemProps) => {
183
280
  icon={iconRight as string}
184
281
  />
185
282
  </div>
186
- }
283
+ )}
187
284
  </Tag>
188
- )
189
- }
190
- </li>
191
- )
192
- }
285
+ )}
286
+ </>
287
+ );
288
+ };
193
289
 
194
- export default NavItem
290
+ export default NavItem;
@@ -5,3 +5,41 @@
5
5
  @import "./vertical_nav";
6
6
  @import "./horizontal_nav";
7
7
  @import "./collapsible_nav";
8
+
9
+ [class^="pb_nav_list"] {
10
+ //classes for fontSize and fontWeight props
11
+ .font_size_small {
12
+ .pb_nav_list_item_text,
13
+ .pb_nav_list_item_text_collapsible {
14
+ font-size: $font_small !important;
15
+ }
16
+ }
17
+
18
+ .font_size_normal {
19
+ .pb_nav_list_item_text,
20
+ .pb_nav_list_item_text_collapsible {
21
+ font-size: $font_normal !important;
22
+ }
23
+ }
24
+
25
+ .font_bolder {
26
+ .pb_nav_list_item_text,
27
+ .pb_nav_list_item_text_collapsible {
28
+ font-weight: $bolder !important;
29
+ }
30
+ }
31
+
32
+ .font_bold {
33
+ .pb_nav_list_item_text,
34
+ .pb_nav_list_item_text_collapsible {
35
+ font-weight: $bold !important;
36
+ }
37
+ }
38
+
39
+ .font_regular {
40
+ .pb_nav_list_item_text,
41
+ .pb_nav_list_item_text_collapsible {
42
+ font-weight: $regular;
43
+ }
44
+ }
45
+ }
@@ -5,6 +5,7 @@ import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
5
5
  import { globalProps, GlobalProps } from '../utilities/globalProps'
6
6
 
7
7
  import Caption from '../pb_caption/_caption'
8
+ import { SpacingObject, NavChildProps } from './navTypes'
8
9
 
9
10
  type NavProps = {
10
11
  aria?: { [key: string]: string },
@@ -20,6 +21,7 @@ type NavProps = {
20
21
  link?: string,
21
22
  title?: string,
22
23
  variant?: "normal" | "subtle",
24
+ itemSpacing?: SpacingObject
23
25
  } & GlobalProps
24
26
 
25
27
  const Nav = (props: NavProps) => {
@@ -37,6 +39,7 @@ const Nav = (props: NavProps) => {
37
39
  orientation = 'vertical',
38
40
  title = '',
39
41
  variant = 'normal',
42
+ itemSpacing,
40
43
  } = props
41
44
 
42
45
  const ariaProps = buildAriaProps(aria)
@@ -50,6 +53,19 @@ const Nav = (props: NavProps) => {
50
53
  className
51
54
  )
52
55
 
56
+ // Map over the children and clone them with orientation, variant and itemSpacing props to gain access to them in navItem
57
+ const childrenWithProps = React.Children.map(children, (child) => {
58
+ if (React.isValidElement(child)) {
59
+ const childProps: NavChildProps = {
60
+ orientation: orientation,
61
+ variant: variant,
62
+ itemSpacing: itemSpacing
63
+ };
64
+ return React.cloneElement(child, childProps);
65
+ }
66
+ return child;
67
+ });
68
+
53
69
  return (
54
70
  <nav
55
71
  {...ariaProps}
@@ -72,7 +88,7 @@ const Nav = (props: NavProps) => {
72
88
  </a>
73
89
  </div>
74
90
  }
75
- <ul>{children}</ul>
91
+ <div className='pb_nav_wrapper'>{childrenWithProps}</div>
76
92
  </nav>
77
93
  )
78
94
  }
@@ -1,12 +1,11 @@
1
1
  @import "tokens/typography";
2
2
 
3
3
  @mixin subtle {
4
- [class*=pb_nav_list_kit_item] {
5
- list-style: none;
4
+ [class*="pb_nav_list_kit_item"][class*="pb_nav_list_item"] {
6
5
  border-radius: $border_rad_heavier;
7
6
  border-bottom: 0;
8
7
  margin: $space_xs ($space_sm - 2px);
9
- [class*=_link] {
8
+ &[class*="_link"] {
10
9
  text-decoration: none;
11
10
  display: flex;
12
11
  align-items: center;
@@ -17,31 +16,30 @@
17
16
  transition-timing-function: $bezier;
18
17
  border-radius: $border_rad_heavier;
19
18
  @include pb_body($text_lt_default);
20
- [class*=_icon_left] {
19
+ [class*="_icon_left"] {
21
20
  margin-right: ($space_xs + 2px);
22
21
  color: $text_lt_lighter;
23
22
  }
24
- [class*=_icon_right] {
23
+ [class*="_icon_right"] {
25
24
  margin-left: ($space_xs + 2px);
26
25
  color: $text_lt_default;
27
26
  }
28
- [class*=_text] {
27
+ [class*="_text"] {
29
28
  flex: 1;
30
29
  }
31
30
  &:hover {
32
31
  background-color: rgba($primary, 0.03);
33
- [class*=_icon] {
32
+ [class*="_icon"] {
34
33
  color: $primary;
35
34
  }
36
- [class*=_text] {
35
+ [class*="_text"] {
37
36
  color: $primary;
38
37
  }
39
38
  }
40
39
  }
41
- &[class*=_active] [class*=_link] {
42
- @include pb_title_4;
40
+ &[class*="_active"] {
43
41
  color: $primary;
44
42
  letter-spacing: normal;
45
43
  }
46
44
  }
47
- }
45
+ }