playbook_ui 12.39.0 → 13.0.0.pre.alpha.PLAY966collapsiblenav41128

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