@patternfly/react-core 4.262.0 → 4.264.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 (102) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/esm/components/Button/Button.d.ts.map +1 -1
  3. package/dist/esm/components/Button/Button.js +1 -1
  4. package/dist/esm/components/Button/Button.js.map +1 -1
  5. package/dist/esm/components/NumberInput/NumberInput.d.ts +1 -1
  6. package/dist/esm/components/NumberInput/NumberInput.d.ts.map +1 -1
  7. package/dist/esm/components/NumberInput/NumberInput.js +0 -1
  8. package/dist/esm/components/NumberInput/NumberInput.js.map +1 -1
  9. package/dist/esm/components/Spinner/Spinner.d.ts +2 -0
  10. package/dist/esm/components/Spinner/Spinner.d.ts.map +1 -1
  11. package/dist/esm/components/Spinner/Spinner.js +2 -2
  12. package/dist/esm/components/Spinner/Spinner.js.map +1 -1
  13. package/dist/esm/components/Tabs/Tab.d.ts +4 -2
  14. package/dist/esm/components/Tabs/Tab.d.ts.map +1 -1
  15. package/dist/esm/components/Tabs/Tab.js +6 -7
  16. package/dist/esm/components/Tabs/Tab.js.map +1 -1
  17. package/dist/esm/components/Tabs/TabAction.d.ts +20 -0
  18. package/dist/esm/components/Tabs/TabAction.d.ts.map +1 -0
  19. package/dist/esm/components/Tabs/TabAction.js +17 -0
  20. package/dist/esm/components/Tabs/TabAction.js.map +1 -0
  21. package/dist/esm/components/Tabs/Tabs.d.ts +1 -1
  22. package/dist/esm/components/Tabs/Tabs.d.ts.map +1 -1
  23. package/dist/esm/components/Tabs/index.d.ts +1 -0
  24. package/dist/esm/components/Tabs/index.d.ts.map +1 -1
  25. package/dist/esm/components/Tabs/index.js +1 -0
  26. package/dist/esm/components/Tabs/index.js.map +1 -1
  27. package/dist/esm/helpers/Popper/Popper.d.ts.map +1 -1
  28. package/dist/esm/helpers/Popper/Popper.js +6 -3
  29. package/dist/esm/helpers/Popper/Popper.js.map +1 -1
  30. package/dist/js/components/Button/Button.d.ts.map +1 -1
  31. package/dist/js/components/Button/Button.js +1 -1
  32. package/dist/js/components/Button/Button.js.map +1 -1
  33. package/dist/js/components/NumberInput/NumberInput.d.ts +1 -1
  34. package/dist/js/components/NumberInput/NumberInput.d.ts.map +1 -1
  35. package/dist/js/components/NumberInput/NumberInput.js +0 -1
  36. package/dist/js/components/NumberInput/NumberInput.js.map +1 -1
  37. package/dist/js/components/Spinner/Spinner.d.ts +2 -0
  38. package/dist/js/components/Spinner/Spinner.d.ts.map +1 -1
  39. package/dist/js/components/Spinner/Spinner.js +2 -2
  40. package/dist/js/components/Spinner/Spinner.js.map +1 -1
  41. package/dist/js/components/Tabs/Tab.d.ts +4 -2
  42. package/dist/js/components/Tabs/Tab.d.ts.map +1 -1
  43. package/dist/js/components/Tabs/Tab.js +6 -7
  44. package/dist/js/components/Tabs/Tab.js.map +1 -1
  45. package/dist/js/components/Tabs/TabAction.d.ts +20 -0
  46. package/dist/js/components/Tabs/TabAction.d.ts.map +1 -0
  47. package/dist/js/components/Tabs/TabAction.js +20 -0
  48. package/dist/js/components/Tabs/TabAction.js.map +1 -0
  49. package/dist/js/components/Tabs/Tabs.d.ts +1 -1
  50. package/dist/js/components/Tabs/Tabs.d.ts.map +1 -1
  51. package/dist/js/components/Tabs/index.d.ts +1 -0
  52. package/dist/js/components/Tabs/index.d.ts.map +1 -1
  53. package/dist/js/components/Tabs/index.js +1 -0
  54. package/dist/js/components/Tabs/index.js.map +1 -1
  55. package/dist/js/helpers/Popper/Popper.d.ts.map +1 -1
  56. package/dist/js/helpers/Popper/Popper.js +6 -3
  57. package/dist/js/helpers/Popper/Popper.js.map +1 -1
  58. package/dist/styles/assets/pficon/pficon.scss +12 -0
  59. package/dist/styles/assets/pficon/pficon.woff +0 -0
  60. package/dist/styles/assets/pficon/pficon.woff2 +0 -0
  61. package/dist/styles/base-no-reset.css +10 -0
  62. package/dist/styles/base.css +10 -0
  63. package/dist/umd/react-core.min.css +1331 -1162
  64. package/dist/umd/react-core.min.js +2 -2
  65. package/package.json +6 -6
  66. package/src/components/Button/Button.tsx +1 -0
  67. package/src/components/Button/__tests__/__snapshots__/Button.test.tsx.snap +1 -1
  68. package/src/components/NumberInput/NumberInput.tsx +1 -2
  69. package/src/components/NumberInput/__tests__/NumberInput.test.tsx +4 -4
  70. package/src/components/NumberInput/examples/NumberInput.md +2 -0
  71. package/src/components/NumberInput/examples/NumberInputCustomStep.tsx +4 -4
  72. package/src/components/NumberInput/examples/NumberInputCustomStepAndThreshold.tsx +4 -5
  73. package/src/components/NumberInput/examples/NumberInputDefault.tsx +5 -5
  74. package/src/components/NumberInput/examples/NumberInputDisabled.tsx +5 -5
  75. package/src/components/NumberInput/examples/NumberInputUnit.tsx +10 -10
  76. package/src/components/NumberInput/examples/NumberInputUnitThreshold.tsx +16 -6
  77. package/src/components/NumberInput/examples/NumberInputVaryingSizes.tsx +11 -11
  78. package/src/components/NumberInput/examples/NumberInputWithStatus.tsx +5 -6
  79. package/src/components/Popover/__tests__/__snapshots__/Popover.test.tsx.snap +3 -1
  80. package/src/components/SearchInput/__tests__/__snapshots__/SearchInput.test.tsx.snap +3 -1
  81. package/src/components/Spinner/Spinner.tsx +4 -1
  82. package/src/components/Spinner/examples/Spinner.md +4 -0
  83. package/src/components/Spinner/examples/SpinnerInline.tsx +29 -0
  84. package/src/components/Tabs/Tab.tsx +17 -16
  85. package/src/components/Tabs/TabAction.tsx +57 -0
  86. package/src/components/Tabs/Tabs.tsx +1 -1
  87. package/src/components/Tabs/__tests__/Tab.test.tsx +10 -0
  88. package/src/components/Tabs/__tests__/__snapshots__/Tab.test.tsx.snap +45 -0
  89. package/src/components/Tabs/examples/Tabs.md +64 -20
  90. package/src/components/Tabs/examples/TabsHelp.tsx +61 -0
  91. package/src/components/Tabs/examples/TabsHelpAndClose.tsx +84 -0
  92. package/src/components/Tabs/index.ts +1 -0
  93. package/src/components/Tooltip/__tests__/__snapshots__/Tooltip.test.tsx.snap +3 -1
  94. package/src/demos/Tabs.md +0 -10
  95. package/src/demos/examples/DashboardWrapper.js +1 -0
  96. package/src/demos/examples/Tabs/ModalTabs.tsx +3 -3
  97. package/src/demos/examples/Tabs/NestedTabs.tsx +11 -3
  98. package/src/demos/examples/Tabs/NestedUnindentedTabs.tsx +25 -40
  99. package/src/demos/examples/Tabs/TabsAndTable.tsx +9 -52
  100. package/src/helpers/Popper/Popper.tsx +13 -3
  101. package/src/demos/examples/Tabs/GrayTabs.tsx +0 -152
  102. package/src/demos/examples/Tabs/TabsAndTablesAutoWidthTabs.tsx +0 -442
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "4.262.0",
3
+ "version": "4.264.0",
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",
@@ -44,16 +44,16 @@
44
44
  "generate": "node scripts/copyStyles.js"
45
45
  },
46
46
  "dependencies": {
47
- "@patternfly/react-icons": "^4.92.10",
48
- "@patternfly/react-styles": "^4.91.10",
49
- "@patternfly/react-tokens": "^4.93.10",
47
+ "@patternfly/react-icons": "^4.93.0",
48
+ "@patternfly/react-styles": "^4.92.0",
49
+ "@patternfly/react-tokens": "^4.94.0",
50
50
  "focus-trap": "6.9.2",
51
51
  "react-dropzone": "9.0.0",
52
52
  "tippy.js": "5.1.2",
53
53
  "tslib": "^2.0.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@patternfly/patternfly": "4.219.2",
56
+ "@patternfly/patternfly": "4.221.2",
57
57
  "@rollup/plugin-commonjs": "^21.0.0",
58
58
  "@rollup/plugin-node-resolve": "^13.0.0",
59
59
  "@rollup/plugin-replace": "^3.0.0",
@@ -70,5 +70,5 @@
70
70
  "react": "^16.8 || ^17 || ^18",
71
71
  "react-dom": "^16.8 || ^17 || ^18"
72
72
  },
73
- "gitHead": "441c5d2ff327a5c9b97eaa299c1c8b9b929f38e1"
73
+ "gitHead": "e1f95ce96852165dd4d102cd87c6b49288f01eff"
74
74
  }
@@ -172,6 +172,7 @@ const ButtonBase: React.FunctionComponent<ButtonProps> = ({
172
172
  <span className={css(styles.buttonProgress)}>
173
173
  <Spinner
174
174
  size={spinnerSize.md}
175
+ isInline={isInline}
175
176
  aria-valuetext={spinnerAriaValueText}
176
177
  aria-label={spinnerAriaLabel}
177
178
  aria-labelledby={spinnerAriaLabelledBy}
@@ -257,7 +257,7 @@ exports[`Button isLoading inline link 1`] = `
257
257
  <span
258
258
  aria-label="Contents"
259
259
  aria-valuetext="Loading"
260
- class="pf-c-spinner pf-m-md"
260
+ class="pf-c-spinner pf-m-inline"
261
261
  role="progressbar"
262
262
  >
263
263
  <span
@@ -10,7 +10,7 @@ import { TextInput } from '../TextInput';
10
10
 
11
11
  export interface NumberInputProps extends React.HTMLProps<HTMLDivElement> {
12
12
  /** Value of the number input */
13
- value?: number | null;
13
+ value?: number | '';
14
14
  /** Additional classes added to the number input */
15
15
  className?: string;
16
16
  /** Sets the width of the number input to a number of characters */
@@ -89,7 +89,6 @@ export const NumberInput: React.FunctionComponent<NumberInputProps> = ({
89
89
  plusBtnProps,
90
90
  ...props
91
91
  }: NumberInputProps) => {
92
- value = value || 0;
93
92
  const numberInputUnit = <div className={css(styles.numberInputUnit)}>{unit}</div>;
94
93
  const keyDownHandler =
95
94
  inputProps && inputProps.onKeyDown ? inputProps.onKeyDown : defaultKeyDownHandler({ inputName, onMinus, onPlus });
@@ -195,22 +195,22 @@ describe('numberInput', () => {
195
195
  expect(input).toHaveDisplayValue('47.01');
196
196
  });
197
197
 
198
- test('renders 0 if no value passed', () => {
198
+ test('renders 0 (default value) if no value passed', () => {
199
199
  render(<NumberInput />);
200
200
  const input = screen.getByRole('spinbutton');
201
201
  expect(input).toHaveDisplayValue('0');
202
202
  });
203
203
 
204
- test('renders 0 if undefined value passed', () => {
204
+ test('renders 0 (default value) if undefined value passed', () => {
205
205
  render(<NumberInput value={undefined} />);
206
206
  const input = screen.getByRole('spinbutton');
207
207
  expect(input).toHaveDisplayValue('0');
208
208
  });
209
209
 
210
- test('renders 0 if null value passed', () => {
210
+ test('renders nothing if null value passed', () => {
211
211
  render(<NumberInput value={null} />);
212
212
  const input = screen.getByRole('spinbutton');
213
- expect(input).toHaveDisplayValue('0');
213
+ expect(input).toHaveDisplayValue('');
214
214
  });
215
215
 
216
216
  test('does not throw an error if onChange is passed via inputProps as well as the onChange prop', () => {
@@ -19,6 +19,8 @@ propComponents: ['NumberInput']
19
19
 
20
20
  ### With unit and thresholds
21
21
 
22
+ To enable a user entered value to snap to the nearest threshold if the entered input is out of bounds, define the blur event handler.
23
+
22
24
  ```ts file="./NumberInputUnitThreshold.tsx"
23
25
  ```
24
26
 
@@ -2,16 +2,16 @@ import React from 'react';
2
2
  import { NumberInput } from '@patternfly/react-core';
3
3
 
4
4
  export const NumberInputCustomStep: React.FunctionComponent = () => {
5
- const [value, setValue] = React.useState(90);
5
+ const [value, setValue] = React.useState<number | ''>(90);
6
6
  const step = 3;
7
7
 
8
8
  const stepper = (stepValue: number) => {
9
- setValue(value + stepValue);
9
+ setValue((value || 0) + stepValue);
10
10
  };
11
11
 
12
12
  const onChange = (event: React.FormEvent<HTMLInputElement>) => {
13
- const target = event.target as HTMLInputElement;
14
- setValue(Number(target.value));
13
+ const value = (event.target as HTMLInputElement).value;
14
+ setValue(value === '' ? value : +value);
15
15
  };
16
16
 
17
17
  return (
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { NumberInput } from '@patternfly/react-core';
3
3
 
4
4
  export const NumberInputCustomStepAndThreshold: React.FunctionComponent = () => {
5
- const [value, setValue] = React.useState(90);
5
+ const [value, setValue] = React.useState<number | ''>(90);
6
6
  const minValue = 90;
7
7
  const maxValue = 100;
8
8
  const step = 3;
@@ -19,13 +19,12 @@ export const NumberInputCustomStepAndThreshold: React.FunctionComponent = () =>
19
19
  };
20
20
 
21
21
  const stepper = (stepValue: number) => {
22
- setValue(value + stepValue);
22
+ setValue((value || 0) + stepValue);
23
23
  };
24
24
 
25
25
  const onChange = (event: React.FormEvent<HTMLInputElement>) => {
26
- const target = event.target as HTMLInputElement;
27
- const newValue = normalizeBetween(isNaN(+target.value) ? 0 : Number(target.value), minValue, maxValue);
28
- setValue(newValue);
26
+ const value = (event.target as HTMLInputElement).value;
27
+ setValue(value === '' ? value : +value);
29
28
  };
30
29
 
31
30
  const onBlur = (event: React.FormEvent<HTMLInputElement>) => {
@@ -2,20 +2,20 @@ import React from 'react';
2
2
  import { NumberInput } from '@patternfly/react-core';
3
3
 
4
4
  export const NumberInputDefault: React.FunctionComponent = () => {
5
- const [value, setValue] = React.useState(90);
5
+ const [value, setValue] = React.useState<number | ''>(90);
6
6
 
7
7
  const onMinus = () => {
8
- const newValue = value - 1;
8
+ const newValue = (value || 0) - 1;
9
9
  setValue(newValue);
10
10
  };
11
11
 
12
12
  const onChange = (event: React.FormEvent<HTMLInputElement>) => {
13
- const target = event.target as HTMLInputElement;
14
- setValue(Number(target.value));
13
+ const value = (event.target as HTMLInputElement).value;
14
+ setValue(value === '' ? value : +value);
15
15
  };
16
16
 
17
17
  const onPlus = () => {
18
- const newValue = value + 1;
18
+ const newValue = (value || 0) + 1;
19
19
  setValue(newValue);
20
20
  };
21
21
 
@@ -2,22 +2,22 @@ import React from 'react';
2
2
  import { NumberInput } from '@patternfly/react-core';
3
3
 
4
4
  export const NumberInputDisabled: React.FunctionComponent = () => {
5
- const [value, setValue] = React.useState(100);
5
+ const [value, setValue] = React.useState<number | ''>(100);
6
6
  const minValue = 0;
7
7
  const maxValue = 100;
8
8
 
9
9
  const onMinus = () => {
10
- const newValue = value - 1;
10
+ const newValue = (value || 0) - 1;
11
11
  setValue(newValue);
12
12
  };
13
13
 
14
14
  const onChange = (event: React.FormEvent<HTMLInputElement>) => {
15
- const target = event.target as HTMLInputElement;
16
- setValue(Number(target.value));
15
+ const value = (event.target as HTMLInputElement).value;
16
+ setValue(value === '' ? value : +value);
17
17
  };
18
18
 
19
19
  const onPlus = () => {
20
- const newValue = value + 1;
20
+ const newValue = (value || 0) + 1;
21
21
  setValue(newValue);
22
22
  };
23
23
 
@@ -2,26 +2,26 @@ import React from 'react';
2
2
  import { NumberInput } from '@patternfly/react-core';
3
3
 
4
4
  export const NumberInputUnit: React.FunctionComponent = () => {
5
- const [value1, setValue1] = React.useState(90);
6
- const [value2, setValue2] = React.useState(Number((1.0).toFixed(2)));
5
+ const [value1, setValue1] = React.useState<number | ''>(90);
6
+ const [value2, setValue2] = React.useState<number | ''>(Number((1.0).toFixed(2)));
7
7
 
8
- const onMinus1 = () => setValue1(value1 - 1);
8
+ const onMinus1 = () => setValue1((value1 || 0) - 1);
9
9
  const onChange1 = (event: React.FormEvent<HTMLInputElement>) => {
10
- const target = event.target as HTMLInputElement;
11
- setValue1(Number(target.value));
10
+ const value = (event.target as HTMLInputElement).value;
11
+ setValue1(value === '' ? value : +value);
12
12
  };
13
- const onPlus1 = () => setValue1(value1 + 1);
13
+ const onPlus1 = () => setValue1((value1 || 0) + 1);
14
14
 
15
15
  const onMinus2 = () => {
16
- const newValue = Number((value2 - 0.01).toFixed(2));
16
+ const newValue = Number(((value2 || 0) - 0.01).toFixed(2));
17
17
  setValue2(newValue);
18
18
  };
19
19
  const onChange2 = (event: React.FormEvent<HTMLInputElement>) => {
20
- const target = event.target as HTMLInputElement;
21
- setValue2(Number(target.value));
20
+ const value = (event.target as HTMLInputElement).value;
21
+ setValue2(value === '' ? value : +value);
22
22
  };
23
23
  const onPlus2 = () => {
24
- const newValue = Number((value2 + 0.01).toFixed(2));
24
+ const newValue = Number(((value2 || 0) + 0.01).toFixed(2));
25
25
  setValue2(newValue);
26
26
  };
27
27
 
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { NumberInput } from '@patternfly/react-core';
3
3
 
4
4
  export const NumberInputUnitThreshold: React.FunctionComponent = () => {
5
- const [value, setValue] = React.useState(0);
5
+ const [value, setValue] = React.useState<number | ''>(0);
6
6
  const minValue = 0;
7
7
  const maxValue = 10;
8
8
 
@@ -18,18 +18,27 @@ export const NumberInputUnitThreshold: React.FunctionComponent = () => {
18
18
  };
19
19
 
20
20
  const onMinus = () => {
21
- const newValue = normalizeBetween(value - 1, minValue, maxValue);
21
+ const newValue = normalizeBetween((value as number) - 1, minValue, maxValue);
22
22
  setValue(newValue);
23
23
  };
24
24
 
25
25
  const onChange = (event: React.FormEvent<HTMLInputElement>) => {
26
- const target = event.target as HTMLInputElement;
27
- const newValue = normalizeBetween(isNaN(+target.value) ? 0 : Number(target.value), minValue, maxValue);
28
- setValue(newValue);
26
+ const value = (event.target as HTMLInputElement).value;
27
+ setValue(value === '' ? value : +value);
28
+ };
29
+
30
+ const onBlur = (event: React.FocusEvent<HTMLInputElement>) => {
31
+ const blurVal = +event.target.value;
32
+
33
+ if (blurVal < minValue) {
34
+ setValue(minValue);
35
+ } else if (blurVal > maxValue) {
36
+ setValue(maxValue);
37
+ }
29
38
  };
30
39
 
31
40
  const onPlus = () => {
32
- const newValue = normalizeBetween(value + 1, minValue, maxValue);
41
+ const newValue = normalizeBetween((value as number) + 1, minValue, maxValue);
33
42
  setValue(newValue);
34
43
  };
35
44
 
@@ -43,6 +52,7 @@ export const NumberInputUnitThreshold: React.FunctionComponent = () => {
43
52
  max={maxValue}
44
53
  onMinus={onMinus}
45
54
  onChange={onChange}
55
+ onBlur={onBlur}
46
56
  onPlus={onPlus}
47
57
  inputName="input"
48
58
  inputAriaLabel="number input"
@@ -2,25 +2,25 @@ import React from 'react';
2
2
  import { NumberInput } from '@patternfly/react-core';
3
3
 
4
4
  export const NumberInputVaryingSizes: React.FunctionComponent = () => {
5
- const [input1Value, setInput1Value] = React.useState(1);
6
- const [input2Value, setInput2Value] = React.useState(1234567890);
7
- const [input3Value, setInput3Value] = React.useState(5);
8
- const [input4Value, setInput4Value] = React.useState(12345);
5
+ const [input1Value, setInput1Value] = React.useState<number | ''>(1);
6
+ const [input2Value, setInput2Value] = React.useState<number | ''>(1234567890);
7
+ const [input3Value, setInput3Value] = React.useState<number | ''>(5);
8
+ const [input4Value, setInput4Value] = React.useState<number | ''>(12345);
9
9
 
10
10
  const onChange = (
11
11
  event: React.FormEvent<HTMLInputElement>,
12
- updateFunction: React.Dispatch<React.SetStateAction<number>>
12
+ updateFunction: React.Dispatch<React.SetStateAction<number | ''>>
13
13
  ) => {
14
- const target = event.target as HTMLInputElement;
15
- updateFunction(Number(target.value));
14
+ const value = (event.target as HTMLInputElement).value;
15
+ updateFunction(value === '' ? value : +value);
16
16
  };
17
17
 
18
- const onMinus = (value, updateFunction: React.Dispatch<React.SetStateAction<number>>) => {
19
- updateFunction(value - 1);
18
+ const onMinus = (value: number | '', updateFunction: React.Dispatch<React.SetStateAction<number | ''>>) => {
19
+ updateFunction((value || 0) - 1);
20
20
  };
21
21
 
22
- const onPlus = (value, updateFunction: React.Dispatch<React.SetStateAction<number>>) => {
23
- updateFunction(value + 1);
22
+ const onPlus = (value: number | '', updateFunction: React.Dispatch<React.SetStateAction<number | ''>>) => {
23
+ updateFunction((value || 0) + 1);
24
24
  };
25
25
 
26
26
  return (
@@ -9,22 +9,21 @@ export const NumberInputWithStatus: React.FunctionComponent = () => {
9
9
  const [value, setValue] = React.useReducer((state, newVal) => Math.max(min, Math.min(max, Number(newVal))), 5);
10
10
 
11
11
  const onPlus = () => {
12
- const newVal = value + 1;
12
+ const newVal = (value || 0) + 1;
13
13
  setValue(newVal);
14
14
  validate(newVal);
15
15
  };
16
16
 
17
17
  const onMinus = () => {
18
- const newVal = value - 1;
18
+ const newVal = (value || 0) - 1;
19
19
  setValue(newVal);
20
20
  validate(newVal);
21
21
  };
22
22
 
23
23
  const onChange = (event: React.FormEvent<HTMLInputElement>) => {
24
- const target = event.target as HTMLInputElement;
25
- const newVal = isNaN(+target.value) ? 5 : Number(target.value);
26
- setValue(newVal);
27
- validate(newVal);
24
+ const value = (event.target as HTMLInputElement).value;
25
+ setValue(value === '' ? value : +value);
26
+ validate(value);
28
27
  };
29
28
 
30
29
  const validate = newVal => {
@@ -36,7 +36,9 @@ exports[`popover renders close-button, header and body 1`] = `
36
36
 
37
37
  exports[`popover renders in strict mode 1`] = `
38
38
  <DocumentFragment>
39
- <div>
39
+ <div
40
+ style="display: contents;"
41
+ >
40
42
  <div>
41
43
  Toggle Popover
42
44
  </div>
@@ -441,7 +441,9 @@ exports[`SearchInput renders search input in strict mode 1`] = `
441
441
  <div
442
442
  class=""
443
443
  >
444
- <div>
444
+ <div
445
+ style="display: contents;"
446
+ >
445
447
  <div
446
448
  class="pf-c-input-group"
447
449
  >
@@ -20,6 +20,8 @@ export interface SpinnerProps extends Omit<React.HTMLProps<HTMLSpanElement>, 'si
20
20
  isSVG?: boolean;
21
21
  /** Diameter of spinner set as CSS variable */
22
22
  diameter?: string;
23
+ /** @beta Indicates the spinner is inline and the size should inherit the text font size. This will override the size prop. */
24
+ isInline?: boolean;
23
25
  /** Accessible label to describe what is loading */
24
26
  'aria-label'?: string;
25
27
  /** Id of element which describes what is being loaded */
@@ -33,6 +35,7 @@ export const Spinner: React.FunctionComponent<SpinnerProps> = ({
33
35
  'aria-valuetext': ariaValueText = 'Loading...',
34
36
  isSVG = false,
35
37
  diameter,
38
+ isInline = false,
36
39
  'aria-label': ariaLabel,
37
40
  'aria-labelledBy': ariaLabelledBy,
38
41
  ...props
@@ -41,7 +44,7 @@ export const Spinner: React.FunctionComponent<SpinnerProps> = ({
41
44
 
42
45
  return (
43
46
  <Component
44
- className={css(styles.spinner, styles.modifiers[size], className)}
47
+ className={css(styles.spinner, isInline ? styles.modifiers.inline : styles.modifiers[size], className)}
45
48
  role="progressbar"
46
49
  aria-valuetext={ariaValueText}
47
50
  {...(isSVG && { viewBox: '0 0 100 100' })}
@@ -17,3 +17,7 @@ propComponents: ['Spinner']
17
17
  ### Custom size
18
18
  ```ts file="./SpinnerCustomSize.tsx"
19
19
  ```
20
+
21
+ ### Inline size
22
+ ```ts file="./SpinnerInline.tsx" isBeta
23
+ ```
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import { Spinner, Text, TextContent } from '@patternfly/react-core';
3
+
4
+ export const SpinnerInline: React.FunctionComponent = () => (
5
+ <React.Fragment>
6
+ <TextContent>
7
+ <Text component="h1">
8
+ Heading
9
+ <Spinner isInline isSVG aria-label="Spinner in a heading" />
10
+ </Text>
11
+ <Text component="p">
12
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit Sed hendrerit nisi in cursus maximus.
13
+ </Text>
14
+ <Text component="h2">
15
+ Second level
16
+ <Spinner isInline isSVG aria-label="spinner in a subheading" />
17
+ </Text>
18
+ <Text component="p">
19
+ Curabitur accumsan turpis pharetra blandit. Quisque condimentum maximus mi,{' '}
20
+ <Spinner isInline isSVG aria-label="Spinner in a paragraph" /> sit amet commodo arcu rutrum id. Proin pretium
21
+ urna vel cursus venenatis. Suspendisse potenti.
22
+ </Text>
23
+ <small>
24
+ Sometimes you need small text
25
+ <Spinner isInline isSVG aria-label="Spinner in a small element" />
26
+ </small>
27
+ </TextContent>
28
+ </React.Fragment>
29
+ );
@@ -5,10 +5,12 @@ import { TabButton } from './TabButton';
5
5
  import { TabsContext } from './TabsContext';
6
6
  import { css } from '@patternfly/react-styles';
7
7
  import { Tooltip } from '../Tooltip';
8
- import { Button } from '../Button';
9
8
  import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
9
+ import { TabAction } from './TabAction';
10
10
 
11
- export interface TabProps extends Omit<React.HTMLProps<HTMLAnchorElement | HTMLButtonElement>, 'title'>, OUIAProps {
11
+ export interface TabProps
12
+ extends Omit<React.HTMLProps<HTMLAnchorElement | HTMLButtonElement>, 'title' | 'action'>,
13
+ OUIAProps {
12
14
  /** content rendered inside the Tab content area. */
13
15
  children?: React.ReactNode;
14
16
  /** additional classes added to the Tab */
@@ -39,6 +41,8 @@ export interface TabProps extends Omit<React.HTMLProps<HTMLAnchorElement | HTMLB
39
41
  closeButtonAriaLabel?: string;
40
42
  /** @beta Flag indicating the close button should be disabled */
41
43
  isCloseDisabled?: boolean;
44
+ /** @beta Actions rendered beside the tab content */
45
+ actions?: React.ReactNode;
42
46
  /** Value to set the data-ouia-component-id for the tab button.*/
43
47
  ouiaId?: number | string;
44
48
  }
@@ -59,6 +63,7 @@ const TabBase: React.FunctionComponent<TabProps> = ({
59
63
  tooltip,
60
64
  closeButtonAriaLabel,
61
65
  isCloseDisabled = false,
66
+ actions,
62
67
  ...props
63
68
  }: TabProps) => {
64
69
  const preventedEvents = inoperableEvents.reduce(
@@ -117,26 +122,22 @@ const TabBase: React.FunctionComponent<TabProps> = ({
117
122
  className={css(
118
123
  styles.tabsItem,
119
124
  eventKey === localActiveKey && styles.modifiers.current,
120
- handleTabClose && styles.modifiers.action,
121
- handleTabClose && (isDisabled || isAriaDisabled) && styles.modifiers.disabled,
125
+ (handleTabClose || actions) && styles.modifiers.action,
126
+ (isDisabled || isAriaDisabled) && styles.modifiers.disabled,
122
127
  childClassName
123
128
  )}
124
129
  role="presentation"
125
130
  >
126
131
  {tooltip ? <Tooltip {...tooltip.props}>{tabButton}</Tooltip> : tabButton}
132
+ {actions && actions}
127
133
  {handleTabClose !== undefined && (
128
- <span className={css(styles.tabsItemClose)}>
129
- <Button
130
- variant="plain"
131
- aria-label={closeButtonAriaLabel || 'Close tab'}
132
- onClick={(event: any) => handleTabClose(event, eventKey, tabContentRef)}
133
- isDisabled={isCloseDisabled}
134
- >
135
- <span className={css(styles.tabsItemCloseIcon)}>
136
- <TimesIcon />
137
- </span>
138
- </Button>
139
- </span>
134
+ <TabAction
135
+ aria-label={closeButtonAriaLabel || 'Close tab'}
136
+ onClick={(event: any) => handleTabClose(event, eventKey, tabContentRef)}
137
+ isDisabled={isCloseDisabled}
138
+ >
139
+ <TimesIcon />
140
+ </TabAction>
140
141
  )}
141
142
  </li>
142
143
  );
@@ -0,0 +1,57 @@
1
+ import * as React from 'react';
2
+ import { css } from '@patternfly/react-styles';
3
+ import styles from '@patternfly/react-styles/css/components/Tabs/tabs';
4
+ import { Button } from '../Button';
5
+ import { getOUIAProps, OUIAProps } from '../../helpers';
6
+
7
+ export interface TabActionProps extends Omit<React.HTMLProps<HTMLButtonElement>, 'ref' | 'type'>, OUIAProps {
8
+ /** Content rendered in the tab action */
9
+ children?: React.ReactNode;
10
+ /** Additional classes added to the tab action span */
11
+ className?: string;
12
+ /** Click callback for tab action button */
13
+ onClick?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
14
+ /** Flag indicating if the tab action is a help action */
15
+ isHelpAction?: boolean;
16
+ /** Flag indicating if the tab action is disabled */
17
+ isDisabled?: boolean;
18
+ /** Accessible label for the tab action */
19
+ 'aria-label'?: string;
20
+ /** @hide Callback for the section ref */
21
+ innerRef?: React.Ref<any>;
22
+ }
23
+
24
+ const TabActionBase: React.FunctionComponent<TabActionProps> = ({
25
+ children,
26
+ className,
27
+ onClick,
28
+ isHelpAction,
29
+ isDisabled,
30
+ 'aria-label': ariaLabel = 'Tab action',
31
+ innerRef,
32
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
33
+ ouiaId,
34
+ ouiaSafe,
35
+ ...props
36
+ }: TabActionProps) => (
37
+ <span className={css(styles.tabsItemAction, isHelpAction && styles.modifiers.help, className)}>
38
+ <Button
39
+ ref={innerRef}
40
+ type="button"
41
+ variant="plain"
42
+ aria-label={ariaLabel}
43
+ onClick={onClick}
44
+ isDisabled={isDisabled}
45
+ {...getOUIAProps(TabAction.displayName, ouiaId, ouiaSafe)}
46
+ {...props}
47
+ >
48
+ <span className={css(styles.tabsItemActionIcon)}>{children}</span>
49
+ </Button>
50
+ </span>
51
+ );
52
+
53
+ export const TabAction = React.forwardRef((props: TabActionProps, ref: React.Ref<HTMLElement>) => (
54
+ <TabActionBase {...props} innerRef={ref} />
55
+ ));
56
+
57
+ TabAction.displayName = 'TabAction';
@@ -42,7 +42,7 @@ export interface TabsProps extends Omit<React.HTMLProps<HTMLElement | HTMLDivEle
42
42
  defaultActiveKey?: number | string;
43
43
  /** Callback to handle tab selection */
44
44
  onSelect?: (event: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: number | string) => void;
45
- /** @beta Callback to handle tab closing */
45
+ /** @beta Callback to handle tab closing and adds a basic close button to all tabs. This is overridden by the tab actions property. */
46
46
  onClose?: (event: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: number | string) => void;
47
47
  /** @beta Callback for the add button. Passing this property inserts the add button */
48
48
  onAdd?: () => void;
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { render } from '@testing-library/react';
3
3
  import { Tab } from '../Tab';
4
+ import { TabAction } from '../TabAction';
4
5
  import { TabTitleText } from '../TabTitleText';
5
6
 
6
7
  test('should not render anything', () => {
@@ -11,3 +12,12 @@ test('should not render anything', () => {
11
12
  );
12
13
  expect(asFragment()).toMatchSnapshot();
13
14
  });
15
+
16
+ test('renders tab action', () => {
17
+ const { asFragment } = render(
18
+ <Tab eventKey={1} title={<TabTitleText>"Tab item 2"</TabTitleText>} actions={<TabAction>test</TabAction>}>
19
+ Tab 2 section
20
+ </Tab>
21
+ );
22
+ expect(asFragment()).toMatchSnapshot();
23
+ });