@patternfly/react-core 4.233.0 → 4.234.1

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 (77) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/esm/components/MenuToggle/MenuToggle.js +1 -1
  3. package/dist/esm/components/MenuToggle/MenuToggle.js.map +1 -1
  4. package/dist/esm/components/TimePicker/TimePicker.d.ts +1 -0
  5. package/dist/esm/components/TimePicker/TimePicker.d.ts.map +1 -1
  6. package/dist/esm/components/TimePicker/TimePicker.js +14 -2
  7. package/dist/esm/components/TimePicker/TimePicker.js.map +1 -1
  8. package/dist/esm/components/Timestamp/Timestamp.d.ts +72 -0
  9. package/dist/esm/components/Timestamp/Timestamp.d.ts.map +1 -0
  10. package/dist/esm/components/Timestamp/Timestamp.js +33 -0
  11. package/dist/esm/components/Timestamp/Timestamp.js.map +1 -0
  12. package/dist/esm/components/Timestamp/index.d.ts +2 -0
  13. package/dist/esm/components/Timestamp/index.d.ts.map +1 -0
  14. package/dist/esm/components/Timestamp/index.js +2 -0
  15. package/dist/esm/components/Timestamp/index.js.map +1 -0
  16. package/dist/esm/components/index.d.ts +1 -0
  17. package/dist/esm/components/index.d.ts.map +1 -1
  18. package/dist/esm/components/index.js +1 -0
  19. package/dist/esm/components/index.js.map +1 -1
  20. package/dist/js/components/MenuToggle/MenuToggle.js +1 -1
  21. package/dist/js/components/MenuToggle/MenuToggle.js.map +1 -1
  22. package/dist/js/components/TimePicker/TimePicker.d.ts +1 -0
  23. package/dist/js/components/TimePicker/TimePicker.d.ts.map +1 -1
  24. package/dist/js/components/TimePicker/TimePicker.js +14 -2
  25. package/dist/js/components/TimePicker/TimePicker.js.map +1 -1
  26. package/dist/js/components/Timestamp/Timestamp.d.ts +72 -0
  27. package/dist/js/components/Timestamp/Timestamp.d.ts.map +1 -0
  28. package/dist/js/components/Timestamp/Timestamp.js +37 -0
  29. package/dist/js/components/Timestamp/Timestamp.js.map +1 -0
  30. package/dist/js/components/Timestamp/index.d.ts +2 -0
  31. package/dist/js/components/Timestamp/index.d.ts.map +1 -0
  32. package/dist/js/components/Timestamp/index.js +5 -0
  33. package/dist/js/components/Timestamp/index.js.map +1 -0
  34. package/dist/js/components/index.d.ts +1 -0
  35. package/dist/js/components/index.d.ts.map +1 -1
  36. package/dist/js/components/index.js +1 -0
  37. package/dist/js/components/index.js.map +1 -1
  38. package/dist/styles/base-no-reset.css +2 -2
  39. package/dist/styles/base.css +2 -2
  40. package/dist/umd/react-core.css +115 -69
  41. package/dist/umd/react-core.js +812 -759
  42. package/dist/umd/react-core.min.css +792 -746
  43. package/dist/umd/react-core.min.js +4 -4
  44. package/package.json +6 -6
  45. package/src/components/Button/examples/Button.md +55 -24
  46. package/src/components/ContextSelector/examples/ContextSelectorBasic.tsx +4 -4
  47. package/src/components/ContextSelector/examples/ContextSelectorPlainText.tsx +3 -3
  48. package/src/components/ContextSelector/examples/ContextSelectorWithFooter.tsx +4 -4
  49. package/src/components/DualListSelector/examples/DualListSelectorComposable.tsx +8 -2
  50. package/src/components/DualListSelector/examples/DualListSelectorComposableDragDrop.tsx +3 -9
  51. package/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx +3 -3
  52. package/src/components/DualListSelector/examples/DualListSelectorTreeExample.tsx +8 -5
  53. package/src/components/MenuToggle/MenuToggle.tsx +2 -2
  54. package/src/components/TimePicker/TimePicker.tsx +19 -11
  55. package/src/components/Timestamp/Timestamp.tsx +134 -0
  56. package/src/components/Timestamp/__tests__/Timestamp.test.tsx +177 -0
  57. package/src/components/Timestamp/__tests__/__snapshots__/Timestamp.test.tsx.snap +17 -0
  58. package/src/components/Timestamp/examples/Timestamp.md +57 -0
  59. package/src/components/Timestamp/examples/TimestampBasicFormats.tsx +26 -0
  60. package/src/components/Timestamp/examples/TimestampCustomContent.tsx +20 -0
  61. package/src/components/Timestamp/examples/TimestampCustomFormat.tsx +6 -0
  62. package/src/components/Timestamp/examples/TimestampCustomTooltip.tsx +19 -0
  63. package/src/components/Timestamp/examples/TimestampDefault.tsx +4 -0
  64. package/src/components/Timestamp/examples/TimestampDefaultTooltip.tsx +18 -0
  65. package/src/components/Timestamp/index.ts +1 -0
  66. package/src/components/index.ts +1 -0
  67. package/src/demos/Button.md +5 -1
  68. package/src/demos/ComposableMenu/examples/ComposableActionsMenu.tsx +12 -8
  69. package/src/demos/ComposableMenu/examples/ComposableApplicationLauncher.tsx +12 -11
  70. package/src/demos/ComposableMenu/examples/ComposableContextSelector.tsx +10 -12
  71. package/src/demos/ComposableMenu/examples/ComposableDrilldownMenu.tsx +7 -7
  72. package/src/demos/ComposableMenu/examples/ComposableFlyout.tsx +8 -8
  73. package/src/demos/ComposableMenu/examples/ComposableSimpleCheckboxSelect.tsx +11 -8
  74. package/src/demos/ComposableMenu/examples/ComposableSimpleDropdown.tsx +7 -7
  75. package/src/demos/ComposableMenu/examples/ComposableSimpleSelect.tsx +17 -8
  76. package/src/demos/ComposableMenu/examples/ComposableTreeViewMenu.tsx +11 -10
  77. package/src/demos/ComposableMenu/examples/ComposableTypeaheadSelect.tsx +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "4.233.0",
3
+ "version": "4.234.1",
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",
@@ -50,16 +50,16 @@
50
50
  "generate": "node scripts/copyStyles.js"
51
51
  },
52
52
  "dependencies": {
53
- "@patternfly/react-icons": "^4.84.0",
54
- "@patternfly/react-styles": "^4.83.0",
55
- "@patternfly/react-tokens": "^4.85.0",
53
+ "@patternfly/react-icons": "^4.85.1",
54
+ "@patternfly/react-styles": "^4.84.1",
55
+ "@patternfly/react-tokens": "^4.86.1",
56
56
  "focus-trap": "6.9.2",
57
57
  "react-dropzone": "9.0.0",
58
58
  "tippy.js": "5.1.2",
59
59
  "tslib": "^2.0.0"
60
60
  },
61
61
  "devDependencies": {
62
- "@patternfly/patternfly": "4.209.0",
62
+ "@patternfly/patternfly": "4.210.1",
63
63
  "@rollup/plugin-commonjs": "^22.0.2",
64
64
  "@rollup/plugin-node-resolve": "^13.3.0",
65
65
  "@rollup/plugin-replace": "^4.0.0",
@@ -76,5 +76,5 @@
76
76
  "react": "^16.8.0 || ^17.0.0",
77
77
  "react-dom": "^16.8.0 || ^17.0.0"
78
78
  },
79
- "gitHead": "816b8605d11e6374b6f6b4c5d6da80d43265fc70"
79
+ "gitHead": "5ed44df6faaf77aae464836238b7f90a0c2fa8cb"
80
80
  }
@@ -16,68 +16,99 @@ import { Link } from '@reach/router';
16
16
 
17
17
  ## Examples
18
18
 
19
- ### Variations
19
+ ### Variant examples
20
+
21
+ PatternFly supports several button styling variants to be used in different scenarios as needed. The following button variants can be assigned using the `variant` property.
20
22
 
21
23
  ```ts file="./ButtonVariations.tsx"
22
24
  ```
23
25
 
24
- ### Disabled
26
+ | Variant | Description|
27
+ | --- | ---|
28
+ | Primary | Primary buttons are the most visually prominent variant. Use for the most important call to action.|
29
+ | Secondary | Secondary buttons are less visually prominant than primary buttons. Use for general actions on a page that require less emphasis than primary buttons. |
30
+ | Tertiary | Tertiary buttons are the least visually prominent variant. Use to retain a classic button format with less emphasis than primary or secondary buttons. |
31
+ | Danger | Danger buttons may be used for actions that are potentially destructive or difficult/impossible to undo. Danger is an independent variant, but all button variants may use the the `isDanger` property to apply similar styling. |
32
+ | Warning | Warning buttons may be used for actions that change an important setting or behavior, but not in a destructive or irreversible way. |
33
+ | Link | Links are labeled, but have no background or border. Use for actions that require less emphasis, actions that navigate users to another page within the same window, and/or actions that navigate to external pages in a new window. Links may be placed inline with text using the `isInline` property.|
34
+ | Plain | Plain buttons have no styling and are intended to be labeled with icons. |
35
+ | Control | Control buttons can be labeled with text or icons. Primarily intended to be paired with other controls in an [input group](https://www.patternfly.org/v4/components/input-group). |
36
+
37
+ ### Disabled buttons
38
+
39
+ To indicate that an action is currently unavailable, all button variations can be disabled using the `isDisabled` property.
25
40
 
26
41
  ```ts file="./ButtonDisabled.tsx"
27
42
  ```
28
43
 
29
- ### Aria disabled
44
+ ### Small buttons
30
45
 
31
- ```ts file="./ButtonAriaDisabled.tsx"
46
+ To fit into tight spaces, primary, secondary, tertiary, danger, and warning button variations can be made smaller using the `isSmall` property.
47
+
48
+ ```ts file="./ButtonSmall.tsx"
32
49
  ```
33
50
 
34
- ### Aria disabled button with tooltip
51
+ ### Call to action (CTA) buttons
35
52
 
36
- ```ts file="./ButtonAriaDisabledTooltip.tsx"
53
+ CTA buttons and links direct users to complete an action. Primary, secondary, tertiary, and link button variants can be styled as CTAs using the `isLarge` property.
54
+
55
+ ```ts file="./ButtonCallToAction.tsx"
37
56
  ```
38
57
 
39
- ### Aria disabled link as button with tooltip
58
+ ### Block level buttons
40
59
 
41
- ```ts file="./ButtonAriaDisabledLinkTooltip.tsx"
60
+ Block level buttons span the full width of the parent element and can be enabled using the `isBlock` property.
61
+
62
+ ```ts file="./ButtonBlock.tsx"
63
+ ```
64
+
65
+ ### Progress indicators
66
+
67
+ Progress indicators can be added to buttons to identify that an action is in progress after a click.
68
+
69
+ ```ts file="./ButtonProgress.tsx"
42
70
  ```
43
71
 
44
72
  ### Links as buttons
45
73
 
74
+ Buttons that link to another resource may take the form of primary, secondary, tertiary, or link variants. Use `component="a"` and an `href` property to designate the button's target link.
75
+
46
76
  ```ts file="./ButtonLinks.tsx"
47
77
  ```
48
78
 
49
79
  ### Inline link as span
50
80
 
81
+ Inline links should use `component="span"` and the `isInline` property to wrap inline with surrounding text.
82
+
51
83
  ```ts file="./ButtonInlineSpanLink.tsx"
52
84
  ```
53
85
 
54
- ### Block level
55
-
56
- ```ts file="./ButtonBlock.tsx"
57
- ```
86
+ ### Router link
58
87
 
59
- ### Types
88
+ Router links can be used for in-app linking in React environments to prevent page reloading.
60
89
 
61
- ```ts file="./ButtonTypes.tsx"
90
+ ```ts file="./ButtonRouterLink.tsx"
62
91
  ```
63
92
 
64
- ### Small
93
+ ### Aria-disabled examples
65
94
 
66
- ```ts file="./ButtonSmall.tsx"
67
- ```
95
+ [Accessible Rich Internet Applications (ARIA)](https://www.w3.org/WAI/standards-guidelines/aria/) is a set of roles and attributes specified by the World Wide Web Consortium. ARIA defines ways to make web content and web applications more accessible to people with disabilities.
68
96
 
69
- ### Call to action
97
+ Buttons that are aria-disabled are similar to normal disabled buttons, except they can receive focus. Every button variant can be aria-disabled using the `isAriaDisabled` property.
70
98
 
71
- ```ts file="./ButtonCallToAction.tsx"
99
+ ```ts file="./ButtonAriaDisabled.tsx"
72
100
  ```
73
101
 
74
- ### Progress
102
+ ### Aria-disabled with tooltip
75
103
 
76
- ```ts file="./ButtonProgress.tsx"
104
+ Unlike normal disabled buttons, aria-disabled buttons can support tooltips.
105
+
106
+ ```ts file="./ButtonAriaDisabledTooltip.tsx"
77
107
  ```
78
108
 
79
- ### Router link
109
+ ### Aria-disabled link as button with tooltip
80
110
 
81
- ```ts file="./ButtonRouterLink.tsx"
82
- ```
111
+ Aria-disabled buttons can operate as links, which also support tooltips.
83
112
 
113
+ ```ts file="./ButtonAriaDisabledLinkTooltip.tsx"
114
+ ```
@@ -48,7 +48,7 @@ export const ContextSelectorBasic: React.FunctionComponent = () => {
48
48
  setSearchValue(value);
49
49
  };
50
50
 
51
- const onSearchButtonClick = (_event: React.SyntheticEvent<HTMLButtonElement>) => {
51
+ const onSearchButtonClick = () => {
52
52
  const filtered =
53
53
  searchValue === ''
54
54
  ? items
@@ -71,11 +71,11 @@ export const ContextSelectorBasic: React.FunctionComponent = () => {
71
71
  screenReaderLabel="Selected Project:"
72
72
  >
73
73
  {filteredItems.map((item, index) => {
74
- const [text = null, href = null, isDisabled] =
75
- typeof item === 'string' ? [item, null, false] : [item.text, item.href, item.isDisabled];
74
+ const [text = null, href = undefined, isDisabled] =
75
+ typeof item === 'string' ? [item, undefined, false] : [item.text, item.href, item.isDisabled];
76
76
  return (
77
77
  <ContextSelectorItem key={index} href={href} isDisabled={isDisabled}>
78
- {text || item}
78
+ {text}
79
79
  </ContextSelectorItem>
80
80
  );
81
81
  })}
@@ -47,7 +47,7 @@ export const ContextSelectorPlainText: React.FunctionComponent = () => {
47
47
  setSearchValue(value);
48
48
  };
49
49
 
50
- const onSearchButtonClick = (_event: React.SyntheticEvent<HTMLButtonElement>) => {
50
+ const onSearchButtonClick = () => {
51
51
  const filtered =
52
52
  searchValue === ''
53
53
  ? items
@@ -72,8 +72,8 @@ export const ContextSelectorPlainText: React.FunctionComponent = () => {
72
72
  isText
73
73
  >
74
74
  {filteredItems.map((item, index) => {
75
- const [text, href = null, isDisabled = false] =
76
- typeof item === 'string' ? [item, null, false] : [item.text, item.href, item.isDisabled];
75
+ const [text, href = undefined, isDisabled = false] =
76
+ typeof item === 'string' ? [item, undefined, false] : [item.text, item.href, item.isDisabled];
77
77
  return (
78
78
  <ContextSelectorItem key={index} href={href} isDisabled={isDisabled}>
79
79
  {text}
@@ -47,7 +47,7 @@ export const ContextSelectorWithFooter: React.FunctionComponent = () => {
47
47
  setSearchValue(value);
48
48
  };
49
49
 
50
- const onSearchButtonClick = (_event: React.SyntheticEvent<HTMLButtonElement>) => {
50
+ const onSearchButtonClick = () => {
51
51
  const filtered =
52
52
  searchValue === ''
53
53
  ? items
@@ -78,11 +78,11 @@ export const ContextSelectorWithFooter: React.FunctionComponent = () => {
78
78
  }
79
79
  >
80
80
  {filteredItems.map((item, index) => {
81
- const [text = null, href = null, isDisabled] =
82
- typeof item === 'string' ? [item, null, false] : [item.text, item.href, item.isDisabled];
81
+ const [text = null, href = undefined, isDisabled] =
82
+ typeof item === 'string' ? [item, undefined, false] : [item.text, item.href, item.isDisabled];
83
83
  return (
84
84
  <ContextSelectorItem key={index} href={href} isDisabled={isDisabled}>
85
- {text || item}
85
+ {text}
86
86
  </ContextSelectorItem>
87
87
  );
88
88
  })}
@@ -16,8 +16,14 @@ import AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-d
16
16
  import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
17
17
  import PficonSortCommonAscIcon from '@patternfly/react-icons/dist/esm/icons/pficon-sort-common-asc-icon';
18
18
 
19
+ interface Option {
20
+ text: string;
21
+ selected: boolean;
22
+ isVisible: boolean;
23
+ }
24
+
19
25
  export const DualListSelectorComposable: React.FunctionComponent = () => {
20
- const [availableOptions, setAvailableOptions] = React.useState([
26
+ const [availableOptions, setAvailableOptions] = React.useState<Option[]>([
21
27
  { text: 'Apple', selected: false, isVisible: true },
22
28
  { text: 'Banana', selected: false, isVisible: true },
23
29
  { text: 'Pineapple', selected: false, isVisible: true },
@@ -26,7 +32,7 @@ export const DualListSelectorComposable: React.FunctionComponent = () => {
26
32
  { text: 'Peach', selected: false, isVisible: true },
27
33
  { text: 'Strawberry', selected: false, isVisible: true }
28
34
  ]);
29
- const [chosenOptions, setChosenOptions] = React.useState([]);
35
+ const [chosenOptions, setChosenOptions] = React.useState<Option[]>([]);
30
36
  const [availableFilter, setAvailableFilter] = React.useState('');
31
37
  const [chosenFilter, setChosenFilter] = React.useState('');
32
38
 
@@ -8,20 +8,14 @@ import {
8
8
  DualListSelectorList,
9
9
  DualListSelectorListItem,
10
10
  DualListSelectorControlsWrapper,
11
- DualListSelectorControl
11
+ DualListSelectorControl,
12
+ DraggableItemPosition
12
13
  } from '@patternfly/react-core';
13
14
  import AngleDoubleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-left-icon';
14
15
  import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';
15
16
  import AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';
16
17
  import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
17
18
 
18
- interface SourceType {
19
- droppableId: string;
20
- index: number;
21
- }
22
-
23
- interface DestinationType extends SourceType {}
24
-
25
19
  export const DualListSelectorComposableDragDrop: React.FunctionComponent = () => {
26
20
  const [ignoreNextOptionSelect, setIgnoreNextOptionSelect] = React.useState(false);
27
21
  const [availableOptions, setAvailableOptions] = React.useState([
@@ -87,7 +81,7 @@ export const DualListSelectorComposableDragDrop: React.FunctionComponent = () =>
87
81
  }
88
82
  };
89
83
 
90
- const onDrop = (source: SourceType, dest: DestinationType) => {
84
+ const onDrop = (source: DraggableItemPosition, dest?: DraggableItemPosition) => {
91
85
  if (dest) {
92
86
  const newList = [...chosenOptions];
93
87
  const [removed] = newList.splice(source.index, 1);
@@ -52,7 +52,7 @@ export const DualListSelectorComposableTree: React.FunctionComponent<ExampleProp
52
52
  if (!node.children || !node.children.length) {
53
53
  return [node.id];
54
54
  } else {
55
- let childrenIds = [];
55
+ let childrenIds: string[] = [];
56
56
  node.children.forEach(child => {
57
57
  childrenIds = [...childrenIds, ...getDescendantLeafIds(child)];
58
58
  });
@@ -77,7 +77,7 @@ export const DualListSelectorComposableTree: React.FunctionComponent<ExampleProp
77
77
  // Builds a map of child leaf nodes by node id - memoized so that it only rebuilds the list if the data changes.
78
78
  const { memoizedLeavesById, memoizedAllLeaves, memoizedNodeText } = React.useMemo(() => {
79
79
  let leavesById = {};
80
- let allLeaves = [];
80
+ let allLeaves: string[] = [];
81
81
  let nodeTexts = {};
82
82
  data.forEach(foodNode => {
83
83
  nodeTexts = { ...nodeTexts, ...buildTextById(foodNode) };
@@ -129,7 +129,7 @@ export const DualListSelectorComposableTree: React.FunctionComponent<ExampleProp
129
129
  return true;
130
130
  }
131
131
  if (areSomeDescendantsSelected(node, isChosen)) {
132
- return null;
132
+ return false;
133
133
  }
134
134
  return false;
135
135
  };
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { DualListSelector } from '@patternfly/react-core';
2
+ import { DualListSelector, DualListSelectorTreeItemData } from '@patternfly/react-core';
3
3
 
4
4
  export const DualListSelectorTreeExample: React.FunctionComponent = () => {
5
- const [availableOptions, setAvailableOptions] = React.useState<React.ReactNode[]>([
5
+ const [availableOptions, setAvailableOptions] = React.useState<DualListSelectorTreeItemData[]>([
6
6
  {
7
7
  id: 'F1',
8
8
  text: 'Folder 1',
@@ -38,7 +38,7 @@ export const DualListSelectorTreeExample: React.FunctionComponent = () => {
38
38
  }
39
39
  ]);
40
40
 
41
- const [chosenOptions, setChosenOptions] = React.useState<React.ReactNode[]>([
41
+ const [chosenOptions, setChosenOptions] = React.useState<DualListSelectorTreeItemData[]>([
42
42
  {
43
43
  id: 'CF1',
44
44
  text: 'Chosen Folder 1',
@@ -73,7 +73,10 @@ export const DualListSelectorTreeExample: React.FunctionComponent = () => {
73
73
  }
74
74
  ]);
75
75
 
76
- const onListChange = (newAvailableOptions: React.ReactNode[], newChosenOptions: React.ReactNode[]) => {
76
+ const onListChange = (
77
+ newAvailableOptions: DualListSelectorTreeItemData[],
78
+ newChosenOptions: DualListSelectorTreeItemData[]
79
+ ) => {
77
80
  setAvailableOptions(newAvailableOptions.sort());
78
81
  setChosenOptions(newChosenOptions.sort());
79
82
  };
@@ -84,7 +87,7 @@ export const DualListSelectorTreeExample: React.FunctionComponent = () => {
84
87
  isTree
85
88
  availableOptions={availableOptions}
86
89
  chosenOptions={chosenOptions}
87
- onListChange={onListChange}
90
+ onListChange={onListChange as any}
88
91
  id="dual-list-selector-tree"
89
92
  />
90
93
  );
@@ -87,7 +87,7 @@ export class MenuToggleBase extends React.Component<MenuToggleProps> {
87
87
  <>
88
88
  {icon && <span className={css(styles.menuToggleIcon)}>{icon}</span>}
89
89
  {isTypeahead ? children : <span className={css(styles.menuToggleText)}>{children}</span>}
90
- {badge && <span className={css(styles.menuToggleCount)}>{badge}</span>}
90
+ {React.isValidElement(badge) && <span className={css(styles.menuToggleCount)}>{badge}</span>}
91
91
  {isTypeahead ? (
92
92
  <button
93
93
  type="button"
@@ -166,7 +166,7 @@ export class MenuToggleBase extends React.Component<MenuToggleProps> {
166
166
  }
167
167
  }
168
168
 
169
- export const MenuToggle = React.forwardRef((props: MenuToggleProps, ref: React.Ref<MenuToggleElement>) => (
169
+ export const MenuToggle = React.forwardRef<MenuToggleElement, MenuToggleProps>((props, ref) => (
170
170
  <MenuToggleBase innerRef={ref} {...props} />
171
171
  ));
172
172
 
@@ -107,6 +107,7 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
107
107
  inputProps: {},
108
108
  minTime: '',
109
109
  maxTime: '',
110
+ isOpen: false,
110
111
  setIsOpen: () => {}
111
112
  };
112
113
 
@@ -490,24 +491,31 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
490
491
  />
491
492
  );
492
493
 
494
+ let calculatedAppendTo;
495
+ switch (menuAppendTo) {
496
+ case 'inline':
497
+ calculatedAppendTo = () => this.toggleRef.current;
498
+ break;
499
+ case 'parent':
500
+ calculatedAppendTo = getParentElement;
501
+ break;
502
+ default:
503
+ calculatedAppendTo = menuAppendTo as HTMLElement;
504
+ }
505
+
493
506
  return (
494
507
  <div ref={this.baseComponentRef} className={css(datePickerStyles.datePicker, className)}>
495
508
  <div className={css(datePickerStyles.datePickerInput)} style={style} {...props}>
496
509
  <InputGroup>
497
510
  <div id={randomId}>
498
511
  <div ref={this.toggleRef} style={{ paddingLeft: '0' }}>
499
- {menuAppendTo !== 'inline' ? (
500
- <Popper
501
- appendTo={menuAppendTo === 'parent' ? getParentElement() : menuAppendTo}
502
- trigger={textInput}
503
- popper={menuContainer}
504
- isVisible={isTimeOptionsOpen}
505
- />
506
- ) : (
507
- textInput
508
- )}
512
+ <Popper
513
+ appendTo={calculatedAppendTo}
514
+ trigger={textInput}
515
+ popper={menuContainer}
516
+ isVisible={isTimeOptionsOpen}
517
+ />
509
518
  </div>
510
- {isTimeOptionsOpen && menuAppendTo === 'inline' && menuContainer}
511
519
  </div>
512
520
  </InputGroup>
513
521
  {isInvalid && (
@@ -0,0 +1,134 @@
1
+ import * as React from 'react';
2
+ import styles from '@patternfly/react-styles/css/components/Timestamp/timestamp';
3
+ import { css } from '@patternfly/react-styles';
4
+ import { Tooltip } from '../Tooltip';
5
+
6
+ export enum TimestampFormat {
7
+ full = 'full',
8
+ long = 'long',
9
+ medium = 'medium',
10
+ short = 'short'
11
+ }
12
+
13
+ export enum TimestampTooltipVariant {
14
+ default = 'default',
15
+ custom = 'custom'
16
+ }
17
+
18
+ export interface TimestampTooltip {
19
+ /** The variant of the tooltip. The "default" variant displays the date passed into
20
+ * the timestamp's date prop as a UTC string.
21
+ */
22
+ variant: 'default' | 'custom';
23
+ /** Content displayed in the tooltip when using the "custom" variant. */
24
+ content?: React.ReactNode;
25
+ /** Additional props passed to the tooltip. */
26
+ tooltipProps?: any;
27
+ /** A custom suffix to apply to a "default" variant tooltip. */
28
+ suffix?: string;
29
+ }
30
+
31
+ export interface TimestampProps extends React.HTMLProps<HTMLSpanElement> {
32
+ /** Displays custom content inside the timestamp, such as a relative time. This prop will
33
+ * override the default content that is displayed. Custom content will not be affected by
34
+ * any props that format or add to displayed content.
35
+ */
36
+ children?: React.ReactNode;
37
+ /** Class to add to the outer span. */
38
+ className?: string;
39
+ /** Applies custom formatting to the displayed date and/or time. Passing this prop in will
40
+ * override the dateFormat and timeFormat props.
41
+ */
42
+ customFormat?: { [key: string]: string };
43
+ /** Determines the default content that is displayed inside the timestamp and the value of
44
+ * the inner time element's datetime attribute. */
45
+ date?: Date;
46
+ /** Determines the format of the displayed date in the timestamp and UTC tooltip. Examples:
47
+ * "Full" => Tuesday, August 9, 2022;
48
+ * "Long" => August 9, 2022;
49
+ * "Medium" => Aug 9, 2022;
50
+ * "Short" => 8/9/22
51
+ */
52
+ dateFormat?: 'full' | 'long' | 'medium' | 'short';
53
+ /** Applies a custom suffix to the displayed content, usually after the time, such as a
54
+ * custom time zone.
55
+ */
56
+ displaySuffix?: string;
57
+ /** Flag for displaying the time in a 12 hour format. If this prop is not passed in,
58
+ * the hour format will be based on the locale prop's value.
59
+ */
60
+ is12Hour?: boolean;
61
+ /** Determines which locale to use in the displayed content. Defaults to the current locale
62
+ * when this prop is not passed in. For more information about locale's:
63
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_identification_and_negotiation
64
+ */
65
+ locale?: string;
66
+ /** Determines the format of the displayed time in the timestamp and UTC tooltip. Examples:
67
+ * "Full" => 11:25:00 AM Eastern Daylight Time
68
+ * "Long" => 11:25:00 AM EDT
69
+ * "Medium" => 11:25:00 AM
70
+ * "Short" => 11:25 AM
71
+ */
72
+ timeFormat?: 'full' | 'long' | 'medium' | 'short';
73
+ /** Adds a tooltip to the timestamp. */
74
+ tooltip?: TimestampTooltip;
75
+ }
76
+
77
+ export const Timestamp: React.FunctionComponent<TimestampProps> = ({
78
+ children,
79
+ className,
80
+ customFormat,
81
+ date: dateProp = new Date(),
82
+ dateFormat,
83
+ displaySuffix = '',
84
+ is12Hour,
85
+ locale,
86
+ timeFormat,
87
+ tooltip,
88
+ ...props
89
+ }: TimestampProps) => {
90
+ const hasTimeFormat = timeFormat && !customFormat;
91
+ const formatOptions = {
92
+ ...(dateFormat && !customFormat && { dateStyle: dateFormat }),
93
+ ...(customFormat && { ...customFormat }),
94
+ ...(is12Hour !== undefined && { hour12: is12Hour })
95
+ };
96
+
97
+ const dateAsLocaleString = new Date(dateProp).toLocaleString(locale, {
98
+ ...formatOptions,
99
+ ...(hasTimeFormat && { timeStyle: timeFormat })
100
+ });
101
+ const defaultDisplay = `${dateAsLocaleString}${displaySuffix ? ' ' + displaySuffix : ''}`;
102
+
103
+ const utcTimeFormat = timeFormat !== 'short' ? 'medium' : 'short';
104
+ const convertToUTCString = (date: Date) => new Date(date).toUTCString().slice(0, -3);
105
+ const utcDateString = new Date(convertToUTCString(dateProp)).toLocaleString(locale, {
106
+ ...formatOptions,
107
+ ...(hasTimeFormat && { timeStyle: utcTimeFormat })
108
+ });
109
+ const defaultTooltipContent = `${utcDateString}${tooltip?.suffix ? ' ' + tooltip.suffix : ' UTC'}`;
110
+
111
+ const timestamp = (
112
+ <span
113
+ className={css(styles.timestamp, tooltip && styles.modifiers.helpText, className)}
114
+ {...(tooltip && { tabIndex: 0 })}
115
+ {...props}
116
+ >
117
+ <time className="pf-c-timestamp__text" dateTime={props.dateTime || new Date(dateProp).toISOString()}>
118
+ {!children ? defaultDisplay : children}
119
+ </time>
120
+ </span>
121
+ );
122
+
123
+ return tooltip ? (
124
+ <Tooltip
125
+ content={tooltip.variant === TimestampTooltipVariant.default ? defaultTooltipContent : tooltip.content}
126
+ {...tooltip.tooltipProps}
127
+ >
128
+ {timestamp}
129
+ </Tooltip>
130
+ ) : (
131
+ timestamp
132
+ );
133
+ };
134
+ Timestamp.displayName = 'Timestamp';