@patternfly/react-core 4.267.2 → 4.267.4

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 (36) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/esm/components/CalendarMonth/CalendarMonth.d.ts +14 -1
  3. package/dist/esm/components/CalendarMonth/CalendarMonth.d.ts.map +1 -1
  4. package/dist/esm/components/CalendarMonth/CalendarMonth.js +10 -2
  5. package/dist/esm/components/CalendarMonth/CalendarMonth.js.map +1 -1
  6. package/dist/esm/components/Menu/MenuItem.d.ts +2 -2
  7. package/dist/esm/components/Menu/MenuItem.d.ts.map +1 -1
  8. package/dist/esm/components/Menu/MenuItem.js +1 -0
  9. package/dist/esm/components/Menu/MenuItem.js.map +1 -1
  10. package/dist/esm/components/Nav/NavItem.d.ts +2 -2
  11. package/dist/esm/components/Nav/NavItem.d.ts.map +1 -1
  12. package/dist/esm/components/Nav/NavItem.js +13 -8
  13. package/dist/esm/components/Nav/NavItem.js.map +1 -1
  14. package/dist/js/components/CalendarMonth/CalendarMonth.d.ts +14 -1
  15. package/dist/js/components/CalendarMonth/CalendarMonth.d.ts.map +1 -1
  16. package/dist/js/components/CalendarMonth/CalendarMonth.js +10 -2
  17. package/dist/js/components/CalendarMonth/CalendarMonth.js.map +1 -1
  18. package/dist/js/components/Menu/MenuItem.d.ts +2 -2
  19. package/dist/js/components/Menu/MenuItem.d.ts.map +1 -1
  20. package/dist/js/components/Menu/MenuItem.js +1 -0
  21. package/dist/js/components/Menu/MenuItem.js.map +1 -1
  22. package/dist/js/components/Nav/NavItem.d.ts +2 -2
  23. package/dist/js/components/Nav/NavItem.d.ts.map +1 -1
  24. package/dist/js/components/Nav/NavItem.js +13 -8
  25. package/dist/js/components/Nav/NavItem.js.map +1 -1
  26. package/dist/umd/react-core.min.js +2 -2
  27. package/package.json +2 -2
  28. package/src/components/CalendarMonth/CalendarMonth.tsx +26 -1
  29. package/src/components/CalendarMonth/__tests__/CalendarMonth.test.tsx +9 -0
  30. package/src/components/CalendarMonth/examples/CalendarMonth.md +1 -1
  31. package/src/components/CalendarMonth/examples/CalendarMonthDateRange.tsx +19 -2
  32. package/src/components/CalendarMonth/examples/CalendarMonthSelectableDate.tsx +14 -4
  33. package/src/components/Menu/MenuItem.tsx +3 -2
  34. package/src/components/Nav/NavItem.tsx +17 -11
  35. package/src/components/Nav/examples/NavFlyout.tsx +2 -13
  36. package/src/demos/Nav.md +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "4.267.2",
3
+ "version": "4.267.4",
4
4
  "description": "This library provides a set of common React components for use with the PatternFly reference implementation.",
5
5
  "main": "dist/js/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -77,5 +77,5 @@
77
77
  "react": "^16.8 || ^17 || ^18",
78
78
  "react-dom": "^16.8 || ^17 || ^18"
79
79
  },
80
- "gitHead": "d0a31d9e7658776d5a4fd38beb13584fb90df4b8"
80
+ "gitHead": "e1bb5650695acd2a2de69b29c8a9857161e4a8a1"
81
81
  }
@@ -20,6 +20,15 @@ export enum Weekday {
20
20
  Saturday
21
21
  }
22
22
 
23
+ export interface CalendarMonthInlineProps {
24
+ /** Component wrapping the calendar month when used inline. Recommended to be 'article'. */
25
+ component?: keyof JSX.IntrinsicElements;
26
+ /** Title of the calendar rendered above the inline calendar month. Recommended to be a 'title' component. */
27
+ title?: React.ReactNode;
28
+ /** Id of the accessible label of the calendar month. Recommended to map to the title. */
29
+ ariaLabelledby?: string;
30
+ }
31
+
23
32
  /** Additional properties that extend from and can be passed to the main component. These
24
33
  * properties allow customizing the calendar formatting and aria-labels.
25
34
  */
@@ -49,6 +58,8 @@ export interface CalendarFormat {
49
58
  weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | Weekday;
50
59
  /** Accessible label for the year input. */
51
60
  yearInputAriaLabel?: string;
61
+ /** Props used to ensure accessibility when displaying the calendar month inline. */
62
+ inlineProps?: CalendarMonthInlineProps;
52
63
  }
53
64
 
54
65
  export interface CalendarProps extends CalendarFormat, Omit<React.HTMLProps<HTMLDivElement>, 'onChange'> {
@@ -133,6 +144,7 @@ export const CalendarMonth = ({
133
144
  yearInputAriaLabel = 'Select year',
134
145
  cellAriaLabel,
135
146
  isDateFocused = false,
147
+ inlineProps,
136
148
  ...props
137
149
  }: CalendarProps) => {
138
150
  const longMonths = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map(monthNum => new Date(1990, monthNum)).map(monthFormat);
@@ -232,7 +244,8 @@ export const CalendarMonth = ({
232
244
  const isHoveredDateValid = isValidated(hoveredDate);
233
245
  const monthFormatted = monthFormat(focusedDate);
234
246
  const yearFormatted = yearFormat(focusedDate);
235
- return (
247
+
248
+ const calendarToRender = (
236
249
  <div className={css(styles.calendarMonth, className)} {...props}>
237
250
  <div className={styles.calendarMonthHeader}>
238
251
  <div className={css(styles.calendarMonthHeaderNavControl, styles.modifiers.prevMonth)}>
@@ -386,4 +399,16 @@ export const CalendarMonth = ({
386
399
  </table>
387
400
  </div>
388
401
  );
402
+
403
+ if (inlineProps !== undefined) {
404
+ const Component = (inlineProps.component ? inlineProps.component : 'article') as any;
405
+ return (
406
+ <Component {...(inlineProps.ariaLabelledby && { 'aria-labelledby': inlineProps.ariaLabelledby })}>
407
+ {inlineProps.title}
408
+ {calendarToRender}
409
+ </Component>
410
+ );
411
+ }
412
+ return calendarToRender;
389
413
  };
414
+ CalendarMonth.displayName = 'CalendarMonth';
@@ -55,3 +55,12 @@ test('Next year dates have correct year in aria label', () => {
55
55
  const nextYearDate = screen.getByRole('button', { name: '1 January 2025' });
56
56
  expect(nextYearDate).toBeVisible();
57
57
  });
58
+
59
+ test('InlineProps render correct wrapper component and attributes', () => {
60
+ render(<CalendarMonth inlineProps={{component: 'article', title: <div id="hi">Title</div>, ariaLabelledby: "hi"}} />);
61
+
62
+ const article = screen.getByRole('article');
63
+ expect(article).toHaveAttribute('aria-labelledby', 'hi');
64
+ const title = screen.getByText('Title');
65
+ expect(title).toBeVisible();
66
+ });
@@ -2,7 +2,7 @@
2
2
  id: Calendar month
3
3
  section: components
4
4
  cssPrefix: pf-c-calendar-month
5
- propComponents: ['CalendarMonth', 'CalendarFormat']
5
+ propComponents: ['CalendarMonth', 'CalendarFormat', 'CalendarMonthInlineProps']
6
6
  ---
7
7
 
8
8
  ## Examples
@@ -1,10 +1,27 @@
1
1
  import React from 'react';
2
- import { CalendarMonth } from '@patternfly/react-core';
2
+ import { CalendarMonth, Title, CalendarMonthInlineProps } from '@patternfly/react-core';
3
3
 
4
4
  export const CalendarMonthDateRange: React.FunctionComponent = () => {
5
5
  const startDate = new Date(2020, 10, 11);
6
6
  const endDate = new Date(2020, 10, 24);
7
7
  const disablePreStartDates = (date: Date) => date >= startDate;
8
8
 
9
- return <CalendarMonth validators={[disablePreStartDates]} date={endDate} rangeStart={startDate} />;
9
+ const inlineProps: CalendarMonthInlineProps = {
10
+ component: 'article',
11
+ title: (
12
+ <Title id="display-range" headingLevel="h4">
13
+ Calendar month displaying a range
14
+ </Title>
15
+ ),
16
+ ariaLabelledby: 'display-range'
17
+ };
18
+
19
+ return (
20
+ <CalendarMonth
21
+ validators={[disablePreStartDates]}
22
+ date={endDate}
23
+ rangeStart={startDate}
24
+ inlineProps={inlineProps}
25
+ />
26
+ );
10
27
  };
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { CalendarMonth } from '@patternfly/react-core';
2
+ import { CalendarMonth, Title, CalendarMonthInlineProps } from '@patternfly/react-core';
3
3
 
4
4
  export const CalendarMonthSelectableDate: React.FunctionComponent = () => {
5
5
  const [date, setDate] = React.useState(new Date(2020, 10, 24));
@@ -9,10 +9,20 @@ export const CalendarMonthSelectableDate: React.FunctionComponent = () => {
9
9
  console.log(`updated month: ${newDate.getMonth()}, updated year: ${newDate.getFullYear()}`);
10
10
  };
11
11
 
12
+ const inlineProps: CalendarMonthInlineProps = {
13
+ component: 'article',
14
+ title: (
15
+ <Title headingLevel="h4" id="favorite-date">
16
+ Select your favorite date
17
+ </Title>
18
+ ),
19
+ ariaLabelledby: 'favorite-date'
20
+ };
21
+
12
22
  return (
13
- <React.Fragment>
23
+ <>
24
+ <CalendarMonth date={date} onChange={setDate} onMonthChange={onMonthChange} inlineProps={inlineProps} />
14
25
  <pre>Selected date: {date.toString()}</pre>
15
- <CalendarMonth date={date} onChange={setDate} onMonthChange={onMonthChange} />
16
- </React.Fragment>
26
+ </>
17
27
  );
18
28
  };
@@ -22,7 +22,7 @@ export interface MenuItemProps extends Omit<React.HTMLProps<HTMLLIElement>, 'onC
22
22
  className?: string;
23
23
  /** Identifies the component in the Menu onSelect or onActionClick callback */
24
24
  itemId?: any;
25
- /** Target navigation link */
25
+ /** Target navigation link. Should not be used if the flyout prop is defined. */
26
26
  to?: string;
27
27
  /** @beta Flag indicating the item has a checkbox */
28
28
  hasCheck?: boolean;
@@ -54,7 +54,7 @@ export interface MenuItemProps extends Omit<React.HTMLProps<HTMLLIElement>, 'onC
54
54
  isFocused?: boolean;
55
55
  /** Flag indicating the item is in danger state */
56
56
  isDanger?: boolean;
57
- /** @beta Flyout menu */
57
+ /** @beta Flyout menu. Should not be used if the to prop is defined. */
58
58
  flyoutMenu?: React.ReactElement;
59
59
  /** @beta Callback function when mouse leaves trigger */
60
60
  onShowFlyout?: (event?: any) => void;
@@ -202,6 +202,7 @@ const MenuItemBase: React.FunctionComponent<MenuItemProps> = ({
202
202
 
203
203
  if (key === ' ' || key === 'Enter' || key === 'ArrowRight' || type === 'click') {
204
204
  event.stopPropagation();
205
+ event.preventDefault();
205
206
  if (!flyoutVisible) {
206
207
  showFlyout(true);
207
208
  setFlyoutTarget(target as HTMLElement);
@@ -14,7 +14,7 @@ export interface NavItemProps extends Omit<React.HTMLProps<HTMLAnchorElement>, '
14
14
  styleChildren?: boolean;
15
15
  /** Additional classes added to the nav item */
16
16
  className?: string;
17
- /** Target navigation link */
17
+ /** Target navigation link. Should not be used if the flyout prop is defined. */
18
18
  to?: string;
19
19
  /** Flag indicating whether the item is active */
20
20
  isActive?: boolean;
@@ -28,7 +28,7 @@ export interface NavItemProps extends Omit<React.HTMLProps<HTMLAnchorElement>, '
28
28
  onClick?: NavSelectClickHandler;
29
29
  /** Component used to render NavItems if React.isValidElement(children) is false */
30
30
  component?: React.ReactNode;
31
- /** Flyout of a nav item. This should be a Menu component. */
31
+ /** Flyout of a nav item. This should be a Menu component. Should not be used if the to prop is defined. */
32
32
  flyout?: React.ReactElement;
33
33
  /** Callback when flyout is opened or closed */
34
34
  onShowFlyout?: () => void;
@@ -68,8 +68,14 @@ export const NavItem: React.FunctionComponent<NavItemProps> = ({
68
68
  const ref = React.useRef<HTMLLIElement>();
69
69
  const flyoutVisible = ref === flyoutRef;
70
70
  const popperRef = React.useRef<HTMLDivElement>();
71
- const Component = component as any;
72
71
  const hasFlyout = flyout !== undefined;
72
+ const Component = hasFlyout ? 'button' : (component as any);
73
+
74
+ // A NavItem should not be both a link and a flyout
75
+ if (to && hasFlyout) {
76
+ // eslint-disable-next-line no-console
77
+ console.error('NavItem cannot have both "to" and "flyout" props.');
78
+ }
73
79
 
74
80
  const showFlyout = (show: boolean, override?: boolean) => {
75
81
  if ((!flyoutVisible || override) && show) {
@@ -106,11 +112,7 @@ export const NavItem: React.FunctionComponent<NavItemProps> = ({
106
112
  const key = event.key;
107
113
  const target = event.target as HTMLElement;
108
114
 
109
- if (!(popperRef?.current?.contains(target) || (hasFlyout && ref?.current?.contains(target)))) {
110
- return;
111
- }
112
-
113
- if (key === ' ' || key === 'ArrowRight') {
115
+ if ((key === ' ' || key === 'Enter' || key === 'ArrowRight') && hasFlyout && ref?.current?.contains(target)) {
114
116
  event.stopPropagation();
115
117
  event.preventDefault();
116
118
  if (!flyoutVisible) {
@@ -119,7 +121,9 @@ export const NavItem: React.FunctionComponent<NavItemProps> = ({
119
121
  }
120
122
  }
121
123
 
122
- if (key === 'Escape' || key === 'ArrowLeft') {
124
+ // We only want the NavItem to handle closing a flyout menu if only the first level flyout is open.
125
+ // Otherwise, MenuItem should handle closing its flyouts
126
+ if ((key === 'Escape' || key === 'ArrowLeft') && popperRef?.current?.querySelectorAll('.pf-c-menu').length === 1) {
123
127
  if (flyoutVisible) {
124
128
  event.stopPropagation();
125
129
  event.preventDefault();
@@ -165,6 +169,8 @@ export const NavItem: React.FunctionComponent<NavItemProps> = ({
165
169
  'aria-expanded': flyoutVisible
166
170
  };
167
171
 
172
+ const tabIndex = isNavOpen ? null : -1;
173
+
168
174
  const renderDefaultLink = (context: any): React.ReactNode => {
169
175
  const preventLinkDefault = preventDefault || !to;
170
176
  return (
@@ -178,7 +184,7 @@ export const NavItem: React.FunctionComponent<NavItemProps> = ({
178
184
  className
179
185
  )}
180
186
  aria-current={isActive ? 'page' : null}
181
- tabIndex={isNavOpen ? null : '-1'}
187
+ tabIndex={tabIndex}
182
188
  {...(hasFlyout && { ...ariaFlyoutProps })}
183
189
  {...props}
184
190
  >
@@ -195,7 +201,7 @@ export const NavItem: React.FunctionComponent<NavItemProps> = ({
195
201
  ...(styleChildren && {
196
202
  className: css(styles.navLink, isActive && styles.modifiers.current, child.props && child.props.className)
197
203
  }),
198
- tabIndex: child.props.tabIndex || isNavOpen ? null : -1,
204
+ tabIndex: child.props.tabIndex || tabIndex,
199
205
  children: hasFlyout ? (
200
206
  <React.Fragment>
201
207
  {child.props.children}
@@ -19,12 +19,7 @@ export const NavFlyout: React.FunctionComponent = () => {
19
19
  <Menu key={depth} containsFlyout isNavFlyout id={`nav-flyout-menu-${depth}`} onSelect={onMenuSelect}>
20
20
  <MenuContent>
21
21
  <MenuList>
22
- <MenuItem
23
- onClick={onMenuItemClick}
24
- flyoutMenu={children}
25
- itemId={`nav-flyout-next-menu-${depth}`}
26
- to={`#next-menu-link-${depth}`}
27
- >
22
+ <MenuItem onClick={onMenuItemClick} flyoutMenu={children} itemId={`nav-flyout-next-menu-${depth}`}>
28
23
  Next menu
29
24
  </MenuItem>
30
25
  {Array.apply(0, Array(numFlyouts - depth)).map((_item, index: number) => (
@@ -38,12 +33,7 @@ export const NavFlyout: React.FunctionComponent = () => {
38
33
  Menu {depth} item {index}
39
34
  </MenuItem>
40
35
  ))}
41
- <MenuItem
42
- onClick={onMenuItemClick}
43
- flyoutMenu={children}
44
- itemId={`nav-flyout-next-menu-2-${depth}`}
45
- to={`#next-menu-2-link-${depth}`}
46
- >
36
+ <MenuItem onClick={onMenuItemClick} flyoutMenu={children} itemId={`nav-flyout-next-menu-2-${depth}`}>
47
37
  Next menu
48
38
  </MenuItem>
49
39
  </MenuList>
@@ -81,7 +71,6 @@ export const NavFlyout: React.FunctionComponent = () => {
81
71
  preventDefault
82
72
  flyout={curFlyout}
83
73
  id="nav-flyout-default-link-3"
84
- to="#nav-flyout-default-link-3"
85
74
  itemId="nav-flyout-default-link-3"
86
75
  isActive={activeItem === 'nav-flyout-default-link-3'}
87
76
  >
package/src/demos/Nav.md CHANGED
@@ -1463,7 +1463,7 @@ class VerticalPage extends React.Component {
1463
1463
  <Menu key={depth} containsFlyout isNavFlyout id={`menu-${depth}`} onSelect={this.onMenuSelect}>
1464
1464
  <MenuContent>
1465
1465
  <MenuList>
1466
- <MenuItem flyoutMenu={children} itemId={`next-menu-${depth}`} to={`#menu-link-${depth}`}>
1466
+ <MenuItem flyoutMenu={children} itemId={`next-menu-${depth}`}>
1467
1467
  Additional settings
1468
1468
  </MenuItem>
1469
1469
  {[...Array(numFlyouts - depth).keys()].map(j => (
@@ -1471,7 +1471,7 @@ class VerticalPage extends React.Component {
1471
1471
  Settings menu {depth} item {j}
1472
1472
  </MenuItem>
1473
1473
  ))}
1474
- <MenuItem flyoutMenu={children} itemId={`next-menu-2-${depth}`} to={`#second-menu-link-${depth}`}>
1474
+ <MenuItem flyoutMenu={children} itemId={`next-menu-2-${depth}`}>
1475
1475
  Additional settings
1476
1476
  </MenuItem>
1477
1477
  </MenuList>