@patternfly/react-core 4.232.13 → 4.233.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.
- package/CHANGELOG.md +11 -0
- package/dist/esm/components/ExpandableSection/ExpandableSection.d.ts +17 -3
- package/dist/esm/components/ExpandableSection/ExpandableSection.d.ts.map +1 -1
- package/dist/esm/components/ExpandableSection/ExpandableSection.js +39 -8
- package/dist/esm/components/ExpandableSection/ExpandableSection.js.map +1 -1
- package/dist/esm/components/ExpandableSection/ExpandableSectionToggle.d.ts +2 -0
- package/dist/esm/components/ExpandableSection/ExpandableSectionToggle.d.ts.map +1 -1
- package/dist/esm/components/ExpandableSection/ExpandableSectionToggle.js +4 -4
- package/dist/esm/components/ExpandableSection/ExpandableSectionToggle.js.map +1 -1
- package/dist/esm/components/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/esm/components/InputGroup/InputGroup.js +5 -3
- package/dist/esm/components/InputGroup/InputGroup.js.map +1 -1
- package/dist/esm/components/NumberInput/NumberInput.d.ts +5 -0
- package/dist/esm/components/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/esm/components/NumberInput/NumberInput.js +5 -4
- package/dist/esm/components/NumberInput/NumberInput.js.map +1 -1
- package/dist/js/components/ExpandableSection/ExpandableSection.d.ts +17 -3
- package/dist/js/components/ExpandableSection/ExpandableSection.d.ts.map +1 -1
- package/dist/js/components/ExpandableSection/ExpandableSection.js +40 -9
- package/dist/js/components/ExpandableSection/ExpandableSection.js.map +1 -1
- package/dist/js/components/ExpandableSection/ExpandableSectionToggle.d.ts +2 -0
- package/dist/js/components/ExpandableSection/ExpandableSectionToggle.d.ts.map +1 -1
- package/dist/js/components/ExpandableSection/ExpandableSectionToggle.js +4 -4
- package/dist/js/components/ExpandableSection/ExpandableSectionToggle.js.map +1 -1
- package/dist/js/components/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/js/components/InputGroup/InputGroup.js +5 -3
- package/dist/js/components/InputGroup/InputGroup.js.map +1 -1
- package/dist/js/components/NumberInput/NumberInput.d.ts +5 -0
- package/dist/js/components/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/js/components/NumberInput/NumberInput.js +4 -3
- package/dist/js/components/NumberInput/NumberInput.js.map +1 -1
- package/dist/umd/react-core.css +69 -69
- package/dist/umd/react-core.js +56 -18
- package/dist/umd/react-core.min.css +921 -921
- package/dist/umd/react-core.min.js +5 -5
- package/package.json +6 -6
- package/src/components/ExpandableSection/ExpandableSection.tsx +73 -19
- package/src/components/ExpandableSection/ExpandableSectionToggle.tsx +15 -9
- package/src/components/ExpandableSection/__tests__/ExpandableSection.test.tsx +27 -1
- package/src/components/ExpandableSection/examples/ExpandableSection.md +7 -0
- package/src/components/ExpandableSection/examples/ExpandableSectionTruncateExpansion.tsx +27 -0
- package/src/components/InputGroup/InputGroup.tsx +5 -3
- package/src/components/InputGroup/__tests__/InputGroup.test.tsx +30 -0
- package/src/components/NumberInput/NumberInput.tsx +14 -8
- package/src/components/NumberInput/__tests__/NumberInput.test.tsx +16 -0
- package/src/components/NumberInput/__tests__/__snapshots__/NumberInput.test.tsx.snap +292 -18
- package/src/components/NumberInput/examples/NumberInput.md +5 -0
- package/src/components/NumberInput/examples/WithStatus.tsx +59 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/react-core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.233.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",
|
|
@@ -50,16 +50,16 @@
|
|
|
50
50
|
"generate": "node scripts/copyStyles.js"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@patternfly/react-icons": "^4.
|
|
54
|
-
"@patternfly/react-styles": "^4.
|
|
55
|
-
"@patternfly/react-tokens": "^4.
|
|
53
|
+
"@patternfly/react-icons": "^4.84.0",
|
|
54
|
+
"@patternfly/react-styles": "^4.83.0",
|
|
55
|
+
"@patternfly/react-tokens": "^4.85.0",
|
|
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.
|
|
62
|
+
"@patternfly/patternfly": "4.209.0",
|
|
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": "
|
|
79
|
+
"gitHead": "816b8605d11e6374b6f6b4c5d6da80d43265fc70"
|
|
80
80
|
}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import styles from '@patternfly/react-styles/css/components/ExpandableSection/expandable-section';
|
|
3
3
|
import { css } from '@patternfly/react-styles';
|
|
4
|
+
import lineClamp from '@patternfly/react-tokens/dist/esm/c_expandable_section_m_truncate__content_LineClamp';
|
|
4
5
|
import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
|
|
5
6
|
import { PickOptional } from '../../helpers/typeUtils';
|
|
6
7
|
|
|
8
|
+
export enum ExpandableSectionVariant {
|
|
9
|
+
default = 'default',
|
|
10
|
+
truncate = 'truncate'
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
export interface ExpandableSectionProps extends React.HTMLProps<HTMLDivElement> {
|
|
8
|
-
/** Content rendered inside the
|
|
14
|
+
/** Content rendered inside the expandable section. */
|
|
9
15
|
children?: React.ReactNode;
|
|
10
|
-
/** Additional classes added to the
|
|
16
|
+
/** Additional classes added to the expandable section. */
|
|
11
17
|
className?: string;
|
|
12
18
|
/** Flag to indicate if the content is expanded */
|
|
13
19
|
isExpanded?: boolean;
|
|
@@ -19,7 +25,7 @@ export interface ExpandableSectionProps extends React.HTMLProps<HTMLDivElement>
|
|
|
19
25
|
toggleTextCollapsed?: string;
|
|
20
26
|
/** React node that appears in the attached toggle in place of toggle text */
|
|
21
27
|
toggleContent?: React.ReactNode;
|
|
22
|
-
/** Callback function to toggle the expandable
|
|
28
|
+
/** Callback function to toggle the expandable section. Detached expandable sections should use the onToggle property of the expandable section toggle sub-component. */
|
|
23
29
|
onToggle?: (isExpanded: boolean) => void;
|
|
24
30
|
/** Forces active state */
|
|
25
31
|
isActive?: boolean;
|
|
@@ -33,12 +39,27 @@ export interface ExpandableSectionProps extends React.HTMLProps<HTMLDivElement>
|
|
|
33
39
|
isWidthLimited?: boolean;
|
|
34
40
|
/** Flag to indicate if the content is indented */
|
|
35
41
|
isIndented?: boolean;
|
|
42
|
+
/** @beta Determines the variant of the expandable section. When passing in "truncate" as the
|
|
43
|
+
* variant, the expandable content will be truncated after 3 lines by default.
|
|
44
|
+
*/
|
|
45
|
+
variant?: 'default' | 'truncate';
|
|
46
|
+
/** @beta Truncates the expandable content to the specified number of lines when using the
|
|
47
|
+
* "truncate" variant. */
|
|
48
|
+
truncateMaxLines?: number;
|
|
36
49
|
}
|
|
37
50
|
|
|
38
51
|
interface ExpandableSectionState {
|
|
39
52
|
isExpanded: boolean;
|
|
40
53
|
}
|
|
41
54
|
|
|
55
|
+
const setLineClamp = (lines: number, element: HTMLDivElement) => {
|
|
56
|
+
if (!element || lines < 1) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
element.style.setProperty(lineClamp.name, lines.toString());
|
|
61
|
+
};
|
|
62
|
+
|
|
42
63
|
export class ExpandableSection extends React.Component<ExpandableSectionProps, ExpandableSectionState> {
|
|
43
64
|
static displayName = 'ExpandableSection';
|
|
44
65
|
constructor(props: ExpandableSectionProps) {
|
|
@@ -49,6 +70,7 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
|
|
|
49
70
|
};
|
|
50
71
|
}
|
|
51
72
|
|
|
73
|
+
expandableContentRef = React.createRef<HTMLDivElement>();
|
|
52
74
|
static defaultProps: PickOptional<ExpandableSectionProps> = {
|
|
53
75
|
className: '',
|
|
54
76
|
toggleText: '',
|
|
@@ -61,7 +83,8 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
|
|
|
61
83
|
displaySize: 'default',
|
|
62
84
|
isWidthLimited: false,
|
|
63
85
|
isIndented: false,
|
|
64
|
-
contentId: ''
|
|
86
|
+
contentId: '',
|
|
87
|
+
variant: 'default'
|
|
65
88
|
};
|
|
66
89
|
|
|
67
90
|
private calculateToggleText(
|
|
@@ -79,6 +102,23 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
|
|
|
79
102
|
return toggleText;
|
|
80
103
|
}
|
|
81
104
|
|
|
105
|
+
componentDidMount() {
|
|
106
|
+
if (this.props.variant === ExpandableSectionVariant.truncate && this.props.truncateMaxLines) {
|
|
107
|
+
const expandableContent = this.expandableContentRef.current;
|
|
108
|
+
setLineClamp(this.props.truncateMaxLines, expandableContent);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
componentDidUpdate(prevProps: ExpandableSectionProps) {
|
|
113
|
+
if (
|
|
114
|
+
this.props.variant === ExpandableSectionVariant.truncate &&
|
|
115
|
+
prevProps.truncateMaxLines !== this.props.truncateMaxLines
|
|
116
|
+
) {
|
|
117
|
+
const expandableContent = this.expandableContentRef.current;
|
|
118
|
+
setLineClamp(this.props.truncateMaxLines, expandableContent);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
82
122
|
render() {
|
|
83
123
|
const {
|
|
84
124
|
onToggle: onToggleProp,
|
|
@@ -96,6 +136,9 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
|
|
|
96
136
|
isWidthLimited,
|
|
97
137
|
isIndented,
|
|
98
138
|
contentId,
|
|
139
|
+
variant,
|
|
140
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
141
|
+
truncateMaxLines,
|
|
99
142
|
...props
|
|
100
143
|
} = this.props;
|
|
101
144
|
let onToggle = onToggleProp;
|
|
@@ -116,9 +159,24 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
|
|
|
116
159
|
propOrStateIsExpanded
|
|
117
160
|
);
|
|
118
161
|
|
|
162
|
+
const expandableToggle = !isDetached && (
|
|
163
|
+
<button
|
|
164
|
+
className={css(styles.expandableSectionToggle)}
|
|
165
|
+
type="button"
|
|
166
|
+
aria-expanded={propOrStateIsExpanded}
|
|
167
|
+
onClick={() => onToggle(!propOrStateIsExpanded)}
|
|
168
|
+
>
|
|
169
|
+
{variant !== ExpandableSectionVariant.truncate && (
|
|
170
|
+
<span className={css(styles.expandableSectionToggleIcon)}>
|
|
171
|
+
<AngleRightIcon aria-hidden />
|
|
172
|
+
</span>
|
|
173
|
+
)}
|
|
174
|
+
<span className={css(styles.expandableSectionToggleText)}>{toggleContent || computedToggleText}</span>
|
|
175
|
+
</button>
|
|
176
|
+
);
|
|
177
|
+
|
|
119
178
|
return (
|
|
120
179
|
<div
|
|
121
|
-
{...props}
|
|
122
180
|
className={css(
|
|
123
181
|
styles.expandableSection,
|
|
124
182
|
propOrStateIsExpanded && styles.modifiers.expanded,
|
|
@@ -127,25 +185,21 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
|
|
|
127
185
|
displaySize === 'large' && styles.modifiers.displayLg,
|
|
128
186
|
isWidthLimited && styles.modifiers.limitWidth,
|
|
129
187
|
isIndented && styles.modifiers.indented,
|
|
188
|
+
variant === ExpandableSectionVariant.truncate && styles.modifiers.truncate,
|
|
130
189
|
className
|
|
131
190
|
)}
|
|
191
|
+
{...props}
|
|
132
192
|
>
|
|
133
|
-
{
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
<span className={css(styles.expandableSectionToggleIcon)}>
|
|
141
|
-
<AngleRightIcon aria-hidden />
|
|
142
|
-
</span>
|
|
143
|
-
<span className={css(styles.expandableSectionToggleText)}>{toggleContent || computedToggleText}</span>
|
|
144
|
-
</button>
|
|
145
|
-
)}
|
|
146
|
-
<div className={css(styles.expandableSectionContent)} hidden={!propOrStateIsExpanded} id={contentId}>
|
|
193
|
+
{variant === ExpandableSectionVariant.default && expandableToggle}
|
|
194
|
+
<div
|
|
195
|
+
ref={this.expandableContentRef}
|
|
196
|
+
className={css(styles.expandableSectionContent)}
|
|
197
|
+
hidden={variant !== ExpandableSectionVariant.truncate && !propOrStateIsExpanded}
|
|
198
|
+
id={contentId}
|
|
199
|
+
>
|
|
147
200
|
{children}
|
|
148
201
|
</div>
|
|
202
|
+
{variant === ExpandableSectionVariant.truncate && expandableToggle}
|
|
149
203
|
</div>
|
|
150
204
|
);
|
|
151
205
|
}
|
|
@@ -16,6 +16,8 @@ export interface ExpandableSectionToggleProps extends React.HTMLProps<HTMLDivEle
|
|
|
16
16
|
contentId?: string;
|
|
17
17
|
/** Direction the toggle arrow should point when the expandable section is expanded. */
|
|
18
18
|
direction?: 'up' | 'down';
|
|
19
|
+
/** @beta Flag to determine toggle styling when the expandable content is truncated. */
|
|
20
|
+
hasTruncatedContent?: boolean;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
export const ExpandableSectionToggle: React.FunctionComponent<ExpandableSectionToggleProps> = ({
|
|
@@ -25,16 +27,18 @@ export const ExpandableSectionToggle: React.FunctionComponent<ExpandableSectionT
|
|
|
25
27
|
onToggle,
|
|
26
28
|
contentId,
|
|
27
29
|
direction = 'down',
|
|
30
|
+
hasTruncatedContent = false,
|
|
28
31
|
...props
|
|
29
32
|
}: ExpandableSectionToggleProps) => (
|
|
30
33
|
<div
|
|
31
|
-
{...props}
|
|
32
34
|
className={css(
|
|
33
35
|
styles.expandableSection,
|
|
34
36
|
isExpanded && styles.modifiers.expanded,
|
|
35
37
|
styles.modifiers.detached,
|
|
38
|
+
hasTruncatedContent && styles.modifiers.truncate,
|
|
36
39
|
className
|
|
37
40
|
)}
|
|
41
|
+
{...props}
|
|
38
42
|
>
|
|
39
43
|
<button
|
|
40
44
|
className={css(styles.expandableSectionToggle)}
|
|
@@ -43,14 +47,16 @@ export const ExpandableSectionToggle: React.FunctionComponent<ExpandableSectionT
|
|
|
43
47
|
aria-controls={contentId}
|
|
44
48
|
onClick={() => onToggle(!isExpanded)}
|
|
45
49
|
>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
{!hasTruncatedContent && (
|
|
51
|
+
<span
|
|
52
|
+
className={css(
|
|
53
|
+
styles.expandableSectionToggleIcon,
|
|
54
|
+
isExpanded && direction === 'up' && styles.modifiers.expandTop
|
|
55
|
+
)}
|
|
56
|
+
>
|
|
57
|
+
<AngleRightIcon aria-hidden />
|
|
58
|
+
</span>
|
|
59
|
+
)}
|
|
54
60
|
<span className={css(styles.expandableSectionToggleText)}>{children}</span>
|
|
55
61
|
</button>
|
|
56
62
|
</div>
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { render, screen } from '@testing-library/react';
|
|
4
4
|
import userEvent from '@testing-library/user-event';
|
|
5
5
|
|
|
6
|
-
import { ExpandableSection } from '../ExpandableSection';
|
|
6
|
+
import { ExpandableSection, ExpandableSectionVariant } from '../ExpandableSection';
|
|
7
7
|
import { ExpandableSectionToggle } from '../ExpandableSectionToggle';
|
|
8
8
|
|
|
9
9
|
const props = {};
|
|
@@ -64,3 +64,29 @@ test('Renders ExpandableSection indented', () => {
|
|
|
64
64
|
);
|
|
65
65
|
expect(asFragment()).toMatchSnapshot();
|
|
66
66
|
});
|
|
67
|
+
|
|
68
|
+
test('Does not render with pf-m-truncate class when variant is not passed', () => {
|
|
69
|
+
render(<ExpandableSection {...props}>test</ExpandableSection>);
|
|
70
|
+
|
|
71
|
+
expect(screen.getByText('test').parentElement).not.toHaveClass('pf-m-truncate');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('Does not render with pf-m-truncate class when variant is not truncate', () => {
|
|
75
|
+
render(
|
|
76
|
+
<ExpandableSection variant={ExpandableSectionVariant.default} {...props}>
|
|
77
|
+
test
|
|
78
|
+
</ExpandableSection>
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(screen.getByText('test').parentElement).not.toHaveClass('pf-m-truncate');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('Renders with pf-m-truncate class when variant is truncate', () => {
|
|
85
|
+
render(
|
|
86
|
+
<ExpandableSection variant={ExpandableSectionVariant.truncate} {...props}>
|
|
87
|
+
test
|
|
88
|
+
</ExpandableSection>
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
expect(screen.getByText('test').parentElement).toHaveClass('pf-m-truncate');
|
|
92
|
+
});
|
|
@@ -45,3 +45,10 @@ By using the `toggleContent` prop, you can pass in content other than a simple s
|
|
|
45
45
|
|
|
46
46
|
```ts file="ExpandableSectionCustomToggle.tsx"
|
|
47
47
|
```
|
|
48
|
+
|
|
49
|
+
### Truncate expansion
|
|
50
|
+
|
|
51
|
+
By passing in `variant="truncate"`, the expandable content will be visible up to a maximum number of lines before being truncated, with the toggle revealing or hiding the truncated content. By default the expandable content will truncate after 3 lines, and this can be customized by also passing in the `truncateMaxLines` prop.
|
|
52
|
+
|
|
53
|
+
```ts file="ExpandableSectionTruncateExpansion.tsx" isBeta
|
|
54
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ExpandableSection, ExpandableSectionVariant } from '@patternfly/react-core';
|
|
3
|
+
|
|
4
|
+
export const ExpandableSectionTruncateExpansion: React.FunctionComponent = () => {
|
|
5
|
+
const [isExpanded, setIsExpanded] = React.useState(false);
|
|
6
|
+
|
|
7
|
+
const onToggle = (isExpanded: boolean) => {
|
|
8
|
+
setIsExpanded(isExpanded);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<ExpandableSection
|
|
13
|
+
variant={ExpandableSectionVariant.truncate}
|
|
14
|
+
toggleText={isExpanded ? 'Show less' : 'Show more'}
|
|
15
|
+
onToggle={onToggle}
|
|
16
|
+
isExpanded={isExpanded}
|
|
17
|
+
>
|
|
18
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque nec dignissim turpis, et tristique purus.
|
|
19
|
+
Phasellus efficitur ante quis dolor viverra imperdiet. Orci varius natoque penatibus et magnis dis parturient
|
|
20
|
+
montes, nascetur ridiculus mus. Pellentesque laoreet, sem ac elementum semper, lectus mauris vestibulum nulla,
|
|
21
|
+
eget volutpat massa neque vel turpis. Donec finibus enim eu leo accumsan consectetur. Praesent massa diam,
|
|
22
|
+
tincidunt eu dui ac, ullamcorper elementum est. Phasellus metus felis, venenatis vitae semper nec, porta a metus.
|
|
23
|
+
Vestibulum justo nisi, imperdiet id eleifend at, varius nec lorem. Fusce porttitor mollis nibh, ut elementum ante
|
|
24
|
+
commodo tincidunt. Integer tincidunt at ipsum non aliquet.
|
|
25
|
+
</ExpandableSection>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
@@ -31,9 +31,11 @@ export const InputGroup: React.FunctionComponent<InputGroupProps> = ({
|
|
|
31
31
|
<div ref={inputGroupRef} className={css(styles.inputGroup, className)} {...props}>
|
|
32
32
|
{idItem
|
|
33
33
|
? React.Children.map(children, (child: any) =>
|
|
34
|
-
formCtrls.includes(child.type.displayName)
|
|
35
|
-
?
|
|
36
|
-
: child
|
|
34
|
+
!formCtrls.includes(child.type.displayName) || child.props['aria-describedby']
|
|
35
|
+
? child
|
|
36
|
+
: React.cloneElement(child, {
|
|
37
|
+
'aria-describedby': child.props['aria-describedby'] === '' ? undefined : idItem.props.id
|
|
38
|
+
})
|
|
37
39
|
)
|
|
38
40
|
: children}
|
|
39
41
|
</div>
|
|
@@ -20,4 +20,34 @@ describe('InputGroup', () => {
|
|
|
20
20
|
);
|
|
21
21
|
expect(screen.getByLabelText('some text')).toHaveAttribute('aria-describedby', 'button-id');
|
|
22
22
|
});
|
|
23
|
+
|
|
24
|
+
test('wont add aria-describedby to form-control if describedby is empty string', () => {
|
|
25
|
+
// In this test, TextInput is a form-control component and Button is not.
|
|
26
|
+
// If Button has an id props, this should be used in aria-describedby, but this
|
|
27
|
+
// example has an empty aria-describedby to prevent that from happening.
|
|
28
|
+
render(
|
|
29
|
+
<InputGroup>
|
|
30
|
+
<TextInput value="some data" aria-describedby="" aria-label="some text" />
|
|
31
|
+
<Button id="button-id">
|
|
32
|
+
hello
|
|
33
|
+
</Button>
|
|
34
|
+
</InputGroup>
|
|
35
|
+
);
|
|
36
|
+
expect(screen.getByLabelText('some text')).not.toHaveAttribute('aria-describedby');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('wont override aria-describedby in form-control if describedby has value', () => {
|
|
40
|
+
// In this test, TextInput is a form-control component and Button is not.
|
|
41
|
+
// If Button has an id props, this should be used in aria-describedby, but this
|
|
42
|
+
// example has a predefined aria-describedby to prevent that from happening
|
|
43
|
+
render(
|
|
44
|
+
<InputGroup>
|
|
45
|
+
<TextInput value="some data" aria-describedby="myself" aria-label="some text" />
|
|
46
|
+
<Button id="button-id">
|
|
47
|
+
hello
|
|
48
|
+
</Button>
|
|
49
|
+
</InputGroup>
|
|
50
|
+
);
|
|
51
|
+
expect(screen.getByLabelText('some text')).toHaveAttribute('aria-describedby', 'myself');
|
|
52
|
+
});
|
|
23
53
|
});
|
|
@@ -5,7 +5,8 @@ import MinusIcon from '@patternfly/react-icons/dist/esm/icons/minus-icon';
|
|
|
5
5
|
import PlusIcon from '@patternfly/react-icons/dist/esm/icons/plus-icon';
|
|
6
6
|
import { InputGroup } from '../InputGroup';
|
|
7
7
|
import { Button, ButtonProps } from '../Button';
|
|
8
|
-
import { KEY_CODES } from '../../helpers';
|
|
8
|
+
import { KEY_CODES, ValidatedOptions } from '../../helpers';
|
|
9
|
+
import { TextInput } from '../TextInput';
|
|
9
10
|
|
|
10
11
|
export interface NumberInputProps extends React.HTMLProps<HTMLDivElement> {
|
|
11
12
|
/** Value of the number input */
|
|
@@ -16,6 +17,10 @@ export interface NumberInputProps extends React.HTMLProps<HTMLDivElement> {
|
|
|
16
17
|
widthChars?: number;
|
|
17
18
|
/** Indicates the whole number input should be disabled */
|
|
18
19
|
isDisabled?: boolean;
|
|
20
|
+
/** Value to indicate if the input is modified to show that validation state
|
|
21
|
+
* @beta
|
|
22
|
+
*/
|
|
23
|
+
validated?: 'default' | 'error' | 'warning' | 'success' | ValidatedOptions;
|
|
19
24
|
/** Callback for the minus button */
|
|
20
25
|
onMinus?: (event: React.MouseEvent, name?: string) => void;
|
|
21
26
|
/** Callback for the text input changing */
|
|
@@ -66,6 +71,7 @@ export const NumberInput: React.FunctionComponent<NumberInputProps> = ({
|
|
|
66
71
|
className,
|
|
67
72
|
widthChars,
|
|
68
73
|
isDisabled = false,
|
|
74
|
+
validated = ValidatedOptions.default,
|
|
69
75
|
onMinus = () => {},
|
|
70
76
|
onChange,
|
|
71
77
|
onBlur,
|
|
@@ -99,7 +105,7 @@ export const NumberInput: React.FunctionComponent<NumberInputProps> = ({
|
|
|
99
105
|
|
|
100
106
|
return (
|
|
101
107
|
<div
|
|
102
|
-
className={css(styles.numberInput, className)}
|
|
108
|
+
className={css(styles.numberInput, validated !== 'default' && styles.modifiers.status, className)}
|
|
103
109
|
{...(widthChars && {
|
|
104
110
|
style: {
|
|
105
111
|
'--pf-c-number-input--c-form-control--width-chars': widthChars,
|
|
@@ -121,18 +127,18 @@ export const NumberInput: React.FunctionComponent<NumberInputProps> = ({
|
|
|
121
127
|
<MinusIcon aria-hidden="true" />
|
|
122
128
|
</span>
|
|
123
129
|
</Button>
|
|
124
|
-
<
|
|
125
|
-
className={css(styles.formControl)}
|
|
130
|
+
<TextInput
|
|
126
131
|
type="number"
|
|
127
132
|
value={value}
|
|
128
133
|
name={inputName}
|
|
129
134
|
aria-label={inputAriaLabel}
|
|
130
|
-
{...(isDisabled && {
|
|
131
|
-
{...(onChange && { onChange })}
|
|
135
|
+
{...(isDisabled && { isDisabled })}
|
|
136
|
+
{...(onChange && { onChange: (value, event) => onChange(event) })}
|
|
132
137
|
onBlur={handleBlur}
|
|
133
|
-
{...(!onChange && {
|
|
134
|
-
{...inputProps}
|
|
138
|
+
{...(!onChange && { isReadOnly: true })}
|
|
135
139
|
onKeyDown={keyDownHandler}
|
|
140
|
+
validated={validated}
|
|
141
|
+
{...inputProps}
|
|
136
142
|
/>
|
|
137
143
|
<Button
|
|
138
144
|
variant="control"
|
|
@@ -9,6 +9,21 @@ describe('numberInput', () => {
|
|
|
9
9
|
expect(asFragment()).toMatchSnapshot();
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
+
test('renders success validated', () => {
|
|
13
|
+
const { asFragment } = render(<NumberInput validated='success' />);
|
|
14
|
+
expect(asFragment()).toMatchSnapshot();
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('renders error validated', () => {
|
|
18
|
+
const { asFragment } = render(<NumberInput validated='error' />);
|
|
19
|
+
expect(asFragment()).toMatchSnapshot();
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('renders warning validated', () => {
|
|
23
|
+
const { asFragment } = render(<NumberInput validated='warning' />);
|
|
24
|
+
expect(asFragment()).toMatchSnapshot();
|
|
25
|
+
})
|
|
26
|
+
|
|
12
27
|
test('renders value', () => {
|
|
13
28
|
const { asFragment } = render(<NumberInput value={90} />);
|
|
14
29
|
expect(asFragment()).toMatchSnapshot();
|
|
@@ -54,6 +69,7 @@ describe('numberInput', () => {
|
|
|
54
69
|
<NumberInput
|
|
55
70
|
value={5}
|
|
56
71
|
onMinus={jest.fn()}
|
|
72
|
+
inputProps={{ 'aria-describedby': '' }}
|
|
57
73
|
minusBtnProps={{ id: 'minus-id' }}
|
|
58
74
|
onPlus={jest.fn()}
|
|
59
75
|
plusBtnProps={{ id: 'plus-id' }}
|