@react-ui-org/react-ui 0.60.0 → 0.61.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/.env.playwright +9 -0
- package/.env.playwright.dist +9 -0
- package/.eslintrc-ts +40 -0
- package/README.md +18 -1
- package/dist/react-ui.css +1 -1
- package/dist/react-ui.development.css +2 -0
- package/dist/react-ui.development.js +32 -32
- package/dist/react-ui.js +1 -1
- package/jest.config-ts.js +34 -0
- package/package.json +25 -6
- package/playwright-ct.config.ts +68 -0
- package/src/components/Alert/Alert.jsx +2 -2
- package/src/components/Button/Button.jsx +5 -5
- package/src/components/Button/README.md +1 -1
- package/src/components/ButtonGroup/ButtonGroup.jsx +1 -1
- package/src/components/Card/CardFooter.jsx +1 -1
- package/src/components/CheckboxField/CheckboxField.jsx +3 -3
- package/src/components/FileInputField/FileInputField.jsx +13 -5
- package/src/components/FormLayout/FormLayout.jsx +1 -1
- package/src/components/FormLayout/FormLayoutCustomField.jsx +4 -4
- package/src/components/Grid/Grid.jsx +1 -1
- package/src/components/Grid/GridSpan.jsx +1 -1
- package/src/components/InputGroup/InputGroup.jsx +24 -4
- package/src/components/InputGroup/InputGroup.module.scss +2 -1
- package/src/components/InputGroup/README.md +69 -14
- package/src/components/Modal/Modal.jsx +12 -7
- package/src/components/Modal/ModalBody.jsx +1 -1
- package/src/components/Modal/ModalContent.jsx +1 -1
- package/src/components/Popover/Popover.jsx +5 -5
- package/src/components/Radio/Radio.jsx +3 -3
- package/src/components/ScrollView/ScrollView.jsx +3 -3
- package/src/components/SelectField/SelectField.jsx +9 -6
- package/src/components/Table/Table.jsx +1 -1
- package/src/components/Table/_components/TableBodyCell/TableBodyCell.jsx +1 -1
- package/src/components/Table/_components/TableHeaderCell/TableHeaderCell.jsx +1 -1
- package/src/components/Tabs/TabsItem.jsx +3 -3
- package/src/components/Text/Text.jsx +1 -1
- package/src/components/TextArea/TextArea.jsx +3 -3
- package/src/components/TextField/TextField.jsx +11 -8
- package/src/components/Toggle/Toggle.jsx +3 -3
- package/src/components/Toolbar/Toolbar.jsx +1 -1
- package/src/components/Toolbar/ToolbarGroup.jsx +1 -1
- package/src/components/Toolbar/ToolbarItem.jsx +1 -1
- package/src/providers/globalProps/GlobalPropsProvider.jsx +1 -1
- package/src/providers/translations/TranslationsProvider.jsx +1 -1
- package/src/styles/settings/_breakpoints.scss +2 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = {
|
|
3
|
+
extensionsToTreatAsEsm: [
|
|
4
|
+
'.ts',
|
|
5
|
+
'.tsx',
|
|
6
|
+
],
|
|
7
|
+
moduleFileExtensions: [
|
|
8
|
+
'js',
|
|
9
|
+
'jsx',
|
|
10
|
+
'ts',
|
|
11
|
+
'tsx',
|
|
12
|
+
],
|
|
13
|
+
moduleNameMapper: {
|
|
14
|
+
'\\.scss$': 'identity-obj-proxy',
|
|
15
|
+
'\\.svg$': '<rootDir>/tests/jest/mocks/svgrMock.jsx',
|
|
16
|
+
},
|
|
17
|
+
preset: 'ts-jest',
|
|
18
|
+
setupFiles: [
|
|
19
|
+
'<rootDir>/tests/jest/setupJest.js',
|
|
20
|
+
],
|
|
21
|
+
setupFilesAfterEnv: [
|
|
22
|
+
],
|
|
23
|
+
testEnvironment: '@happy-dom/jest-environment',
|
|
24
|
+
testMatch: [
|
|
25
|
+
'**/*.test.{ts,tsx}',
|
|
26
|
+
],
|
|
27
|
+
transform: {
|
|
28
|
+
'^.+\\.(ts|tsx)$': [
|
|
29
|
+
'ts-jest',
|
|
30
|
+
{ useESM: true },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
verbose: true,
|
|
34
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-ui-org/react-ui",
|
|
3
3
|
"description": "React UI is a themeable UI library for React apps.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.61.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
7
7
|
"ui",
|
|
@@ -47,17 +47,24 @@
|
|
|
47
47
|
"copy": "npm run copy:css && npm run copy:js",
|
|
48
48
|
"copy:css": "cp src/docs/_assets/generated/react-ui.css dist && cp src/docs/_assets/generated/react-ui.development.css dist",
|
|
49
49
|
"copy:js": "cp src/docs/_assets/generated/react-ui.js dist && cp src/docs/_assets/generated/react-ui.development.js dist",
|
|
50
|
-
"eslint": "eslint
|
|
51
|
-
"
|
|
50
|
+
"eslint": "npm run eslint:ts && npm run eslint:js",
|
|
51
|
+
"eslint:js": "eslint --ext js,jsx src",
|
|
52
|
+
"eslint:ts": "eslint --ext ts,tsx -c .eslintrc-ts src tests",
|
|
52
53
|
"lint": "npm run eslint && npm run markdownlint && npm run stylelint",
|
|
53
|
-
"markdownlint": "markdownlint-cli2 \"
|
|
54
|
+
"markdownlint": "markdownlint-cli2 \"README.md\" \"src/**/*.md\"",
|
|
54
55
|
"postbuild": "npm run copy",
|
|
55
|
-
"postinstall": "cp -n .env.dist .env || true",
|
|
56
|
+
"postinstall": "cp -n .env.dist .env && cp -n .env.playwright.dist .env.playwright || true",
|
|
56
57
|
"precopy": "rm -rf dist && mkdir dist",
|
|
57
58
|
"prepublishOnly": "npm run build",
|
|
58
59
|
"start": "webpack --watch --mode=development",
|
|
59
60
|
"stylelint": "stylelint \"src/**/*.{css,scss}\" \"!src/docs/_assets/generated/**\" --config stylelint.config.js",
|
|
60
|
-
"test": "npm run jest"
|
|
61
|
+
"test": "npm run test:jest",
|
|
62
|
+
"test:jest": "npm run test:jest:ts && npm run test:jest:js",
|
|
63
|
+
"test:jest:js": "jest --coverage",
|
|
64
|
+
"test:jest:ts": "jest -c jest.config-ts.js --coverage",
|
|
65
|
+
"test:playwright-ct:all": "playwright test -c playwright-ct.config.ts",
|
|
66
|
+
"test:playwright-ct:all-with-update": "playwright test -c playwright-ct.config.ts --update-snapshots",
|
|
67
|
+
"test:playwright-ct:show-report": "playwright show-report --host=0.0.0.0 --port=9323"
|
|
61
68
|
},
|
|
62
69
|
"dependencies": {
|
|
63
70
|
"normalize.css": "^8.0.1"
|
|
@@ -77,11 +84,18 @@
|
|
|
77
84
|
"@babel/preset-react": "^7.24.7",
|
|
78
85
|
"@babel/register": "^7.24.6",
|
|
79
86
|
"@happy-dom/jest-environment": "^16.6.0",
|
|
87
|
+
"@playwright/experimental-ct-react": "^1.50.1",
|
|
80
88
|
"@stylistic/stylelint-config": "^1.0.1",
|
|
81
89
|
"@svgr/webpack": "^8.1.0",
|
|
82
90
|
"@testing-library/jest-dom": "^6.6.3",
|
|
83
91
|
"@testing-library/react": "^16.1.0",
|
|
84
92
|
"@testing-library/user-event": "^14.5.2",
|
|
93
|
+
"@types/jest": "^29.5.14",
|
|
94
|
+
"@types/node": "^22.13.5",
|
|
95
|
+
"@types/react": "^19.0.10",
|
|
96
|
+
"@types/react-dom": "^19.0.4",
|
|
97
|
+
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
98
|
+
"@typescript-eslint/parser": "^7.18.0",
|
|
85
99
|
"@visionappscz/eslint-config-visionapps": "^1.7.0",
|
|
86
100
|
"@visionappscz/stylelint-config": "^4.0.0",
|
|
87
101
|
"autoprefixer": "^10.4.19",
|
|
@@ -89,13 +103,17 @@
|
|
|
89
103
|
"babel-loader": "^9.1.3",
|
|
90
104
|
"core-js": "^3.37.1",
|
|
91
105
|
"css-loader": "^7.1.2",
|
|
106
|
+
"dotenv": "^16.4.7",
|
|
92
107
|
"eslint": "^8.57.0",
|
|
93
108
|
"eslint-config-airbnb": "^19.0.4",
|
|
109
|
+
"eslint-config-airbnb-typescript": "^18.0.0",
|
|
110
|
+
"eslint-plugin-deprecation": "^3.0.0",
|
|
94
111
|
"eslint-plugin-import": "^2.29.1",
|
|
95
112
|
"eslint-plugin-jsx-a11y": "^6.9.0",
|
|
96
113
|
"eslint-plugin-markdown": "^3.0.1",
|
|
97
114
|
"eslint-plugin-react": "^7.34.3",
|
|
98
115
|
"eslint-plugin-react-hooks": "^4.6.2",
|
|
116
|
+
"eslint-plugin-typescript-sort-keys": "^3.3.0",
|
|
99
117
|
"identity-obj-proxy": "^3.0.0",
|
|
100
118
|
"jest": "^29.7.0",
|
|
101
119
|
"markdownlint-cli2": "^0.13.0",
|
|
@@ -110,6 +128,7 @@
|
|
|
110
128
|
"stylelint": "^16.7.0",
|
|
111
129
|
"stylelint-webpack-plugin": "^5.0.1",
|
|
112
130
|
"terser-webpack-plugin": "^5.3.10",
|
|
131
|
+
"ts-jest": "^29.2.6",
|
|
113
132
|
"webpack": "^5.92.1",
|
|
114
133
|
"webpack-cli": "^5.1.4",
|
|
115
134
|
"webpack-visualizer-plugin2": "^1.1.0"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineConfig,
|
|
3
|
+
devices,
|
|
4
|
+
} from '@playwright/experimental-ct-react';
|
|
5
|
+
import { parseEnvironment } from './tests/playwright/env/parseEnvironment';
|
|
6
|
+
|
|
7
|
+
const environment = parseEnvironment();
|
|
8
|
+
|
|
9
|
+
const isCI = !!process.env.CI;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @see https://playwright.dev/docs/test-configuration
|
|
13
|
+
*/
|
|
14
|
+
module.exports = defineConfig({
|
|
15
|
+
// Fail the build on CI if you accidentally left test.only in the source code
|
|
16
|
+
forbidOnly: isCI,
|
|
17
|
+
// Run tests in files in parallel
|
|
18
|
+
fullyParallel: true,
|
|
19
|
+
// Directory where the output generated by Playwright is stored
|
|
20
|
+
outputDir: './tests/playwright/.temp/playwright-ct-output/',
|
|
21
|
+
// Run tests for following projects
|
|
22
|
+
projects: [
|
|
23
|
+
{
|
|
24
|
+
name: 'chromium',
|
|
25
|
+
use: { ...devices['Desktop Chrome'] },
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
|
29
|
+
reporter: 'html',
|
|
30
|
+
// Retry failed tests once to eliminate flakiness
|
|
31
|
+
retries: 1,
|
|
32
|
+
// Directory where the snapshots are located
|
|
33
|
+
// (must be same as testDir to save snapshots in place where tests are located)
|
|
34
|
+
snapshotDir: './src/',
|
|
35
|
+
// Directory where the tests are located
|
|
36
|
+
testDir: './src/',
|
|
37
|
+
// Pattern to search for test files
|
|
38
|
+
testMatch: '*/*.spec.{ts,tsx}',
|
|
39
|
+
// Timeout for each test
|
|
40
|
+
timeout: 10 * 1000,
|
|
41
|
+
// Do not update snapshots automatically
|
|
42
|
+
updateSnapshots: 'none',
|
|
43
|
+
// Configuration for Playwright component testing
|
|
44
|
+
use: {
|
|
45
|
+
// Directory where the cache is stored
|
|
46
|
+
ctCacheDir: './tests/playwright/.temp/playwright-ct-cache',
|
|
47
|
+
// Port to use for Playwright component endpoint
|
|
48
|
+
ctPort: environment.CT_PORT,
|
|
49
|
+
// Directory where the templates are stored
|
|
50
|
+
ctTemplateDir: './tests/playwright/templates',
|
|
51
|
+
// Launch options for Playwright
|
|
52
|
+
launchOptions: {
|
|
53
|
+
args: [
|
|
54
|
+
'--use-gl=egl',
|
|
55
|
+
// # Must be set to `new` to enable new version of Chromium Headless to obtain correct snapshots
|
|
56
|
+
'--headless=new',
|
|
57
|
+
],
|
|
58
|
+
ignoreDefaultArgs: [
|
|
59
|
+
// # Must be ignored to enable new version of Chromium Headless to obtain correct snapshots
|
|
60
|
+
'--headless',
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
// Collect trace on all retries
|
|
64
|
+
trace: 'on-all-retries',
|
|
65
|
+
},
|
|
66
|
+
// Number of workers to use
|
|
67
|
+
workers: environment.WORKERS,
|
|
68
|
+
});
|
|
@@ -101,18 +101,18 @@ export const Button = React.forwardRef((props, ref) => {
|
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
Button.defaultProps = {
|
|
104
|
-
afterLabel:
|
|
105
|
-
beforeLabel:
|
|
104
|
+
afterLabel: undefined,
|
|
105
|
+
beforeLabel: undefined,
|
|
106
106
|
block: false,
|
|
107
107
|
color: 'primary',
|
|
108
108
|
disabled: false,
|
|
109
|
-
endCorner:
|
|
110
|
-
feedbackIcon:
|
|
109
|
+
endCorner: undefined,
|
|
110
|
+
feedbackIcon: undefined,
|
|
111
111
|
id: undefined,
|
|
112
112
|
labelVisibility: 'xs',
|
|
113
113
|
priority: 'filled',
|
|
114
114
|
size: 'medium',
|
|
115
|
-
startCorner:
|
|
115
|
+
startCorner: undefined,
|
|
116
116
|
type: 'button',
|
|
117
117
|
};
|
|
118
118
|
|
|
@@ -28,7 +28,7 @@ See [API](#api) for all available options.
|
|
|
28
28
|
- **Don't overwhelm your UI** with too many high-emphasis actions. There should
|
|
29
29
|
always be one but chances are that having more of them is not necessary.
|
|
30
30
|
|
|
31
|
-
- Ensure the **button action is well
|
|
31
|
+
- Ensure the **button action is well recognizable** across your target audience.
|
|
32
32
|
This is especially important when using the button [with an icon only](#icon-buttons).
|
|
33
33
|
|
|
34
34
|
## Priorities
|
|
@@ -79,14 +79,14 @@ export const CheckboxField = React.forwardRef((props, ref) => {
|
|
|
79
79
|
|
|
80
80
|
CheckboxField.defaultProps = {
|
|
81
81
|
disabled: false,
|
|
82
|
-
helpText:
|
|
82
|
+
helpText: undefined,
|
|
83
83
|
id: undefined,
|
|
84
84
|
isLabelVisible: true,
|
|
85
85
|
labelPosition: 'after',
|
|
86
86
|
renderAsRequired: false,
|
|
87
87
|
required: false,
|
|
88
|
-
validationState:
|
|
89
|
-
validationText:
|
|
88
|
+
validationState: undefined,
|
|
89
|
+
validationText: undefined,
|
|
90
90
|
};
|
|
91
91
|
|
|
92
92
|
CheckboxField.propTypes = {
|
|
@@ -78,6 +78,7 @@ export const FileInputField = React.forwardRef((props, ref) => {
|
|
|
78
78
|
const handleFileChange = (files, event) => {
|
|
79
79
|
if (files.length === 0) {
|
|
80
80
|
setSelectedFileNames([]);
|
|
81
|
+
onFilesChanged([], event);
|
|
81
82
|
return;
|
|
82
83
|
}
|
|
83
84
|
|
|
@@ -85,6 +86,7 @@ export const FileInputField = React.forwardRef((props, ref) => {
|
|
|
85
86
|
// does not accept multiple files, no files are processed.
|
|
86
87
|
if (files.length > 1 && !multiple) {
|
|
87
88
|
setSelectedFileNames([]);
|
|
89
|
+
onFilesChanged([], event);
|
|
88
90
|
return;
|
|
89
91
|
}
|
|
90
92
|
|
|
@@ -221,7 +223,7 @@ export const FileInputField = React.forwardRef((props, ref) => {
|
|
|
221
223
|
</Text>
|
|
222
224
|
</button>
|
|
223
225
|
</div>
|
|
224
|
-
{helpText && (
|
|
226
|
+
{(helpText && !inputGroupContext) && (
|
|
225
227
|
<div
|
|
226
228
|
className={styles.helpText}
|
|
227
229
|
id={`${id}__helpText`}
|
|
@@ -229,7 +231,7 @@ export const FileInputField = React.forwardRef((props, ref) => {
|
|
|
229
231
|
{helpText}
|
|
230
232
|
</div>
|
|
231
233
|
)}
|
|
232
|
-
{validationText && (
|
|
234
|
+
{(validationText && !inputGroupContext) && (
|
|
233
235
|
<div
|
|
234
236
|
className={styles.validationText}
|
|
235
237
|
id={`${id}__validationText`}
|
|
@@ -245,14 +247,14 @@ export const FileInputField = React.forwardRef((props, ref) => {
|
|
|
245
247
|
FileInputField.defaultProps = {
|
|
246
248
|
disabled: false,
|
|
247
249
|
fullWidth: false,
|
|
248
|
-
helpText:
|
|
250
|
+
helpText: undefined,
|
|
249
251
|
isLabelVisible: true,
|
|
250
252
|
layout: 'vertical',
|
|
251
253
|
multiple: false,
|
|
252
254
|
required: false,
|
|
253
255
|
size: 'medium',
|
|
254
|
-
validationState:
|
|
255
|
-
validationText:
|
|
256
|
+
validationState: undefined,
|
|
257
|
+
validationText: undefined,
|
|
256
258
|
};
|
|
257
259
|
|
|
258
260
|
FileInputField.propTypes = {
|
|
@@ -266,6 +268,9 @@ FileInputField.propTypes = {
|
|
|
266
268
|
fullWidth: PropTypes.bool,
|
|
267
269
|
/**
|
|
268
270
|
* Optional help text.
|
|
271
|
+
*
|
|
272
|
+
* Help text is never rendered when the component is placed into `InputGroup`.
|
|
273
|
+
* If a help text is needed, it must be defined on the `InputGroup` component instead.
|
|
269
274
|
*/
|
|
270
275
|
helpText: PropTypes.node,
|
|
271
276
|
/**
|
|
@@ -319,6 +324,9 @@ FileInputField.propTypes = {
|
|
|
319
324
|
validationState: PropTypes.oneOf(['invalid', 'valid', 'warning']),
|
|
320
325
|
/**
|
|
321
326
|
* Validation message to be displayed.
|
|
327
|
+
*
|
|
328
|
+
* Validation text is never rendered when the component is placed into `InputGroup`.
|
|
329
|
+
* If a validation text is needed, it must be defined on the `InputGroup` component instead.
|
|
322
330
|
*/
|
|
323
331
|
validationText: PropTypes.node,
|
|
324
332
|
};
|
|
@@ -80,15 +80,15 @@ export const FormLayoutCustomField = ({
|
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
FormLayoutCustomField.defaultProps = {
|
|
83
|
-
children:
|
|
83
|
+
children: undefined,
|
|
84
84
|
disabled: false,
|
|
85
85
|
fullWidth: false,
|
|
86
86
|
id: undefined,
|
|
87
|
-
innerFieldSize:
|
|
88
|
-
label:
|
|
87
|
+
innerFieldSize: undefined,
|
|
88
|
+
label: undefined,
|
|
89
89
|
labelForId: undefined,
|
|
90
90
|
required: false,
|
|
91
|
-
validationState:
|
|
91
|
+
validationState: undefined,
|
|
92
92
|
};
|
|
93
93
|
|
|
94
94
|
FormLayoutCustomField.propTypes = {
|
|
@@ -18,6 +18,7 @@ import styles from './InputGroup.module.scss';
|
|
|
18
18
|
export const InputGroup = ({
|
|
19
19
|
children,
|
|
20
20
|
disabled,
|
|
21
|
+
helpTexts,
|
|
21
22
|
id,
|
|
22
23
|
isLabelVisible,
|
|
23
24
|
label,
|
|
@@ -89,7 +90,21 @@ export const InputGroup = ({
|
|
|
89
90
|
{children}
|
|
90
91
|
</InputGroupContext.Provider>
|
|
91
92
|
</div>
|
|
92
|
-
{
|
|
93
|
+
{helpTexts && helpTexts.length > 0 && (
|
|
94
|
+
<ul
|
|
95
|
+
className={styles.helpText}
|
|
96
|
+
id={id && `${id}__helpTexts`}
|
|
97
|
+
>
|
|
98
|
+
{helpTexts.map((helpText) => (
|
|
99
|
+
<li key={helpText}>
|
|
100
|
+
<Text blockLevel>
|
|
101
|
+
{helpText}
|
|
102
|
+
</Text>
|
|
103
|
+
</li>
|
|
104
|
+
))}
|
|
105
|
+
</ul>
|
|
106
|
+
)}
|
|
107
|
+
{validationTexts && validationTexts.length > 0 && (
|
|
93
108
|
<ul
|
|
94
109
|
className={styles.validationText}
|
|
95
110
|
id={id && `${id}__validationTexts`}
|
|
@@ -109,14 +124,15 @@ export const InputGroup = ({
|
|
|
109
124
|
};
|
|
110
125
|
|
|
111
126
|
InputGroup.defaultProps = {
|
|
112
|
-
children:
|
|
127
|
+
children: undefined,
|
|
113
128
|
disabled: false,
|
|
129
|
+
helpTexts: undefined,
|
|
114
130
|
id: undefined,
|
|
115
131
|
isLabelVisible: true,
|
|
116
132
|
layout: 'vertical',
|
|
117
133
|
required: false,
|
|
118
134
|
size: 'medium',
|
|
119
|
-
validationTexts:
|
|
135
|
+
validationTexts: undefined,
|
|
120
136
|
};
|
|
121
137
|
|
|
122
138
|
InputGroup.propTypes = {
|
|
@@ -133,6 +149,10 @@ InputGroup.propTypes = {
|
|
|
133
149
|
* If `true`, the whole input group with all nested inputs and buttons will be disabled.
|
|
134
150
|
*/
|
|
135
151
|
disabled: PropTypes.bool,
|
|
152
|
+
/**
|
|
153
|
+
* An array of help texts to be displayed.
|
|
154
|
+
*/
|
|
155
|
+
helpTexts: PropTypes.arrayOf(PropTypes.node),
|
|
136
156
|
/**
|
|
137
157
|
* ID of the root HTML element.
|
|
138
158
|
*
|
|
@@ -171,7 +191,7 @@ InputGroup.propTypes = {
|
|
|
171
191
|
/**
|
|
172
192
|
* An array of validation messages to be displayed.
|
|
173
193
|
*/
|
|
174
|
-
validationTexts: PropTypes.node,
|
|
194
|
+
validationTexts: PropTypes.arrayOf(PropTypes.node),
|
|
175
195
|
};
|
|
176
196
|
|
|
177
197
|
export const InputGroupWithGlobalProps = withGlobalProps(InputGroup, 'InputGroup');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// 1. The class
|
|
1
|
+
// 1. The class names are intentionally singular because they are also targeted by other mixins.
|
|
2
2
|
// 2. Use a block-level display mode to prevent extra white space below grouped inputs in Safari.
|
|
3
3
|
// 3. Let wide input groups honor the minimum input width and overflow horizontally without wrapping and distorting
|
|
4
4
|
// the inputs.
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// 1.
|
|
42
|
+
.helpText,
|
|
42
43
|
.validationText {
|
|
43
44
|
@include reset.list();
|
|
44
45
|
@include foundation.help-text();
|
|
@@ -60,13 +60,14 @@ See [API](#api) for all available options.
|
|
|
60
60
|
Make sure your inputs fit their container, especially on small screens.
|
|
61
61
|
|
|
62
62
|
- In the background, InputGroup uses the [`fieldset`][fieldset] element. Not
|
|
63
|
-
only it
|
|
64
|
-
use of its built-in features like disabling all nested inputs or pairing
|
|
65
|
-
group with a form outside. Consult [the MDN docs][fieldset] to learn more.
|
|
63
|
+
only does it improve the [accessibility] of the group, it also allows you to
|
|
64
|
+
make use of its built-in features like disabling all nested inputs or pairing
|
|
65
|
+
the group with a form outside. Consult [the MDN docs][fieldset] to learn more.
|
|
66
66
|
|
|
67
67
|
- InputGroup currently **supports grouping of**
|
|
68
68
|
[TextField](/components/TextField), [SelectField](/components/SelectField),
|
|
69
|
-
and [Button](/components/Button)
|
|
69
|
+
[FileInputField](/components/FileInputField), and [Button](/components/Button)
|
|
70
|
+
components.
|
|
70
71
|
|
|
71
72
|
- To group [Buttons](/components/Button) only, use the
|
|
72
73
|
[ButtonGroup](/components/ButtonGroup) component which is designed
|
|
@@ -104,7 +105,7 @@ You can set the `size` property directly on InputGroup to be shared for all
|
|
|
104
105
|
fields and buttons inside the group. This property is then passed over to
|
|
105
106
|
individual elements. At the same time, it **cannot be overridden** on the
|
|
106
107
|
fields' or buttons' level. While technically possible, from the design point of
|
|
107
|
-
view it's undesirable to group elements of totally different types or sizes.
|
|
108
|
+
view, it's undesirable to group elements of totally different types or sizes.
|
|
108
109
|
|
|
109
110
|
## Invisible Label
|
|
110
111
|
|
|
@@ -136,9 +137,9 @@ the input.
|
|
|
136
137
|
|
|
137
138
|
## Horizontal layout
|
|
138
139
|
|
|
139
|
-
The default vertical layout is
|
|
140
|
-
are situations where horizontal layout suits better — and that's why
|
|
141
|
-
supports this kind of layout as well.
|
|
140
|
+
The default vertical layout is straightforward to use and work with. However,
|
|
141
|
+
there are situations where horizontal layout suits better — and that's why
|
|
142
|
+
React UI supports this kind of layout as well.
|
|
142
143
|
|
|
143
144
|
```docoff-react-preview
|
|
144
145
|
<InputGroup
|
|
@@ -150,6 +151,57 @@ supports this kind of layout as well.
|
|
|
150
151
|
</InputGroup>
|
|
151
152
|
```
|
|
152
153
|
|
|
154
|
+
## Help Text
|
|
155
|
+
|
|
156
|
+
You may provide one or more additional help texts to clarify how the input group
|
|
157
|
+
should be filled.
|
|
158
|
+
|
|
159
|
+
These messages are not semantically tied to the `children` elements, the
|
|
160
|
+
connection should be expressed in textual form in the actual message.
|
|
161
|
+
|
|
162
|
+
⚠️ Help texts passed to input elements' `helpText` prop are ignored within
|
|
163
|
+
InputGroup.
|
|
164
|
+
|
|
165
|
+
```docoff-react-preview
|
|
166
|
+
React.createElement(() => {
|
|
167
|
+
const [fruit, setFruit] = React.useState('apple');
|
|
168
|
+
const options = [
|
|
169
|
+
{
|
|
170
|
+
label: 'Apple',
|
|
171
|
+
value: 'apple',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
label: 'Pear',
|
|
175
|
+
value: 'pear',
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
label: 'Cherry',
|
|
179
|
+
value: 'cherry',
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
return (
|
|
183
|
+
<InputGroup
|
|
184
|
+
label="Your favourite fruit"
|
|
185
|
+
helpTexts={[
|
|
186
|
+
"Choose one or more kinds of fruit to feel happy.",
|
|
187
|
+
]}
|
|
188
|
+
>
|
|
189
|
+
<SelectField
|
|
190
|
+
label="Your favourite fruit"
|
|
191
|
+
onChange={(e) => setFruit(e.target.value)}
|
|
192
|
+
options={options}
|
|
193
|
+
value={fruit}
|
|
194
|
+
/>
|
|
195
|
+
<TextField
|
|
196
|
+
label="Variety"
|
|
197
|
+
placeholder="Eg. Golden delicious"
|
|
198
|
+
/>
|
|
199
|
+
<Button label="Submit" />
|
|
200
|
+
</InputGroup>
|
|
201
|
+
);
|
|
202
|
+
})
|
|
203
|
+
```
|
|
204
|
+
|
|
153
205
|
## States
|
|
154
206
|
|
|
155
207
|
### Disabled State
|
|
@@ -169,12 +221,15 @@ Validation states visually present the result of validation of the grouped
|
|
|
169
221
|
inputs. Input group's validation state is taken from its child inputs. You
|
|
170
222
|
should always **provide validation messages for states other than valid**
|
|
171
223
|
directly through `validationTexts` prop so users know what happened and what
|
|
172
|
-
action they should take or what options they have.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
224
|
+
action they should take or what options they have.
|
|
225
|
+
|
|
226
|
+
These messages are not semantically tied to the `children` elements, the
|
|
227
|
+
connection should be expressed in textual form in the actual message. The
|
|
228
|
+
individual `children` elements must not show any `validationText`, they only
|
|
229
|
+
show their respective `validationState`.
|
|
230
|
+
|
|
231
|
+
⚠️ Validation messages passed to input elements' `validationText` prop are
|
|
232
|
+
ignored within InputGroup.
|
|
178
233
|
|
|
179
234
|
👉 While there is a `required` property to visually denote the whole input group
|
|
180
235
|
is required, there is no functional effect as there is no such HTML attribute
|
|
@@ -76,7 +76,12 @@ export const Modal = ({
|
|
|
76
76
|
useModalScrollPrevention(preventScrollUnderneath);
|
|
77
77
|
|
|
78
78
|
const onCancel = useCallback(
|
|
79
|
-
(e) =>
|
|
79
|
+
(e) => {
|
|
80
|
+
if (e.target !== internalDialogRef.current) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
dialogOnCancelHandler(e, closeButtonRef, restProps.onCancel);
|
|
84
|
+
},
|
|
80
85
|
[closeButtonRef, restProps.onCancel],
|
|
81
86
|
);
|
|
82
87
|
const onClick = useCallback(
|
|
@@ -121,7 +126,7 @@ export const Modal = ({
|
|
|
121
126
|
onMouseDown,
|
|
122
127
|
};
|
|
123
128
|
|
|
124
|
-
if (portalId ===
|
|
129
|
+
if (portalId === undefined) {
|
|
125
130
|
return preRender(
|
|
126
131
|
children,
|
|
127
132
|
color,
|
|
@@ -152,14 +157,14 @@ Modal.defaultProps = {
|
|
|
152
157
|
allowCloseOnEscapeKey: true,
|
|
153
158
|
allowPrimaryActionOnEnterKey: true,
|
|
154
159
|
autoFocus: true,
|
|
155
|
-
children:
|
|
156
|
-
closeButtonRef:
|
|
160
|
+
children: undefined,
|
|
161
|
+
closeButtonRef: undefined,
|
|
157
162
|
color: undefined,
|
|
158
|
-
dialogRef:
|
|
159
|
-
portalId:
|
|
163
|
+
dialogRef: undefined,
|
|
164
|
+
portalId: undefined,
|
|
160
165
|
position: 'center',
|
|
161
166
|
preventScrollUnderneath: window.document.body,
|
|
162
|
-
primaryButtonRef:
|
|
167
|
+
primaryButtonRef: undefined,
|
|
163
168
|
size: 'medium',
|
|
164
169
|
};
|
|
165
170
|
|