@instructure/ui-form-field 10.12.0 → 10.12.1-snapshot-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/es/FormField/index.js +0 -1
- package/es/FormFieldGroup/__new-tests__/FormFieldGroup.test.js +4 -4
- package/es/FormFieldGroup/index.js +18 -4
- package/es/FormFieldLayout/__new-tests__/FormFieldLayout.test.js +10 -8
- package/es/FormFieldLayout/index.js +73 -54
- package/es/FormFieldLayout/styles.js +109 -10
- package/es/FormFieldLayout/theme.js +28 -2
- package/es/FormFieldMessages/props.js +3 -2
- package/es/FormFieldMessages/styles.js +5 -3
- package/es/FormPropTypes.js +6 -0
- package/es/index.js +0 -1
- package/lib/FormField/index.js +0 -1
- package/lib/FormFieldGroup/__new-tests__/FormFieldGroup.test.js +4 -4
- package/lib/FormFieldGroup/index.js +18 -4
- package/lib/FormFieldLayout/__new-tests__/FormFieldLayout.test.js +9 -7
- package/lib/FormFieldLayout/index.js +72 -54
- package/lib/FormFieldLayout/styles.js +109 -10
- package/lib/FormFieldLayout/theme.js +28 -2
- package/lib/FormFieldMessages/props.js +3 -2
- package/lib/FormFieldMessages/styles.js +5 -3
- package/lib/FormPropTypes.js +6 -0
- package/lib/index.js +0 -7
- package/package.json +15 -15
- package/src/FormField/README.md +31 -3
- package/src/FormField/index.tsx +0 -1
- package/src/FormFieldGroup/__new-tests__/FormFieldGroup.test.tsx +4 -6
- package/src/FormFieldGroup/index.tsx +41 -6
- package/src/FormFieldLayout/__new-tests__/FormFieldLayout.test.tsx +6 -8
- package/src/FormFieldLayout/index.tsx +78 -92
- package/src/FormFieldLayout/props.ts +28 -2
- package/src/FormFieldLayout/styles.ts +128 -14
- package/src/FormFieldLayout/theme.ts +30 -4
- package/src/FormFieldMessages/props.ts +8 -2
- package/src/FormFieldMessages/styles.ts +5 -4
- package/src/FormPropTypes.ts +4 -0
- package/src/index.ts +0 -2
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/FormField/index.d.ts.map +1 -1
- package/types/FormFieldGroup/index.d.ts +1 -0
- package/types/FormFieldGroup/index.d.ts.map +1 -1
- package/types/FormFieldLayout/index.d.ts +8 -7
- package/types/FormFieldLayout/index.d.ts.map +1 -1
- package/types/FormFieldLayout/props.d.ts +27 -3
- package/types/FormFieldLayout/props.d.ts.map +1 -1
- package/types/FormFieldLayout/styles.d.ts +4 -3
- package/types/FormFieldLayout/styles.d.ts.map +1 -1
- package/types/FormFieldLayout/theme.d.ts +7 -1
- package/types/FormFieldLayout/theme.d.ts.map +1 -1
- package/types/FormFieldMessages/index.d.ts +8 -2
- package/types/FormFieldMessages/index.d.ts.map +1 -1
- package/types/FormFieldMessages/props.d.ts +5 -0
- package/types/FormFieldMessages/props.d.ts.map +1 -1
- package/types/FormFieldMessages/styles.d.ts +2 -3
- package/types/FormFieldMessages/styles.d.ts.map +1 -1
- package/types/FormPropTypes.d.ts +3 -0
- package/types/FormPropTypes.d.ts.map +1 -1
- package/types/index.d.ts +0 -2
- package/types/index.d.ts.map +1 -1
- package/es/FormFieldLabel/__new-tests__/FormFieldLabel.test.js +0 -66
- package/es/FormFieldLabel/index.js +0 -79
- package/es/FormFieldLabel/props.js +0 -31
- package/es/FormFieldLabel/styles.js +0 -62
- package/es/FormFieldLabel/theme.js +0 -52
- package/lib/FormFieldLabel/__new-tests__/FormFieldLabel.test.js +0 -68
- package/lib/FormFieldLabel/index.js +0 -85
- package/lib/FormFieldLabel/props.js +0 -37
- package/lib/FormFieldLabel/styles.js +0 -68
- package/lib/FormFieldLabel/theme.js +0 -58
- package/src/FormFieldLabel/__new-tests__/FormFieldLabel.test.tsx +0 -79
- package/src/FormFieldLabel/index.tsx +0 -95
- package/src/FormFieldLabel/props.ts +0 -58
- package/src/FormFieldLabel/styles.ts +0 -74
- package/src/FormFieldLabel/theme.ts +0 -56
- package/types/FormFieldLabel/__new-tests__/FormFieldLabel.test.d.ts +0 -2
- package/types/FormFieldLabel/__new-tests__/FormFieldLabel.test.d.ts.map +0 -1
- package/types/FormFieldLabel/index.d.ts +0 -42
- package/types/FormFieldLabel/index.d.ts.map +0 -1
- package/types/FormFieldLabel/props.d.ts +0 -15
- package/types/FormFieldLabel/props.d.ts.map +0 -1
- package/types/FormFieldLabel/styles.d.ts +0 -15
- package/types/FormFieldLabel/styles.d.ts.map +0 -1
- package/types/FormFieldLabel/theme.d.ts +0 -10
- package/types/FormFieldLabel/theme.d.ts.map +0 -1
|
@@ -67,13 +67,17 @@ let FormFieldGroup = exports.FormFieldGroup = (_dec = (0, _emotion.withStyle)(_s
|
|
|
67
67
|
(_this$props$makeStyle2 = (_this$props2 = this.props).makeStyles) === null || _this$props$makeStyle2 === void 0 ? void 0 : _this$props$makeStyle2.call(_this$props2, this.makeStylesVariables);
|
|
68
68
|
}
|
|
69
69
|
get makeStylesVariables() {
|
|
70
|
+
// new form errors dont need borders
|
|
71
|
+
const oldInvalid = !!this.props.messages && this.props.messages.findIndex(message => {
|
|
72
|
+
return message.type === 'error';
|
|
73
|
+
}) >= 0;
|
|
70
74
|
return {
|
|
71
|
-
invalid:
|
|
75
|
+
invalid: oldInvalid
|
|
72
76
|
};
|
|
73
77
|
}
|
|
74
78
|
get invalid() {
|
|
75
79
|
return !!this.props.messages && this.props.messages.findIndex(message => {
|
|
76
|
-
return message.type === 'error';
|
|
80
|
+
return message.type === 'error' || message.type === 'newError';
|
|
77
81
|
}) >= 0;
|
|
78
82
|
}
|
|
79
83
|
renderColumns() {
|
|
@@ -105,12 +109,21 @@ let FormFieldGroup = exports.FormFieldGroup = (_dec = (0, _emotion.withStyle)(_s
|
|
|
105
109
|
makeStyles = _this$props3.makeStyles,
|
|
106
110
|
isGroup = _this$props3.isGroup,
|
|
107
111
|
props = (0, _objectWithoutProperties2.default)(_this$props3, _excluded);
|
|
112
|
+
// This is quite ugly, but according to ARIA spec the `aria-invalid` prop
|
|
113
|
+
// can only be used with certain roles see
|
|
114
|
+
// https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-invalid#associated_roles
|
|
115
|
+
// `aria-invalid` is put on in FormFieldLayout because the error message
|
|
116
|
+
// DOM part gets there its ID.
|
|
117
|
+
let ariaInvalid = void 0;
|
|
118
|
+
if (this.props.role && this.invalid && ['application', 'checkbox', 'combobox', 'gridcell', 'listbox', 'radiogroup', 'slider', 'spinbutton', 'textbox', 'tree', 'columnheader', 'rowheader', 'searchbox', 'switch', 'treegrid'].includes(this.props.role)) {
|
|
119
|
+
ariaInvalid = 'true';
|
|
120
|
+
}
|
|
108
121
|
return (0, _emotion.jsx)(_FormFieldLayout.FormFieldLayout, Object.assign({}, (0, _omitProps.omitProps)(props, FormFieldGroup.allowedProps), (0, _pickProps.pickProps)(props, _FormFieldLayout.FormFieldLayout.allowedProps), {
|
|
109
122
|
vAlign: props.vAlign,
|
|
110
123
|
layout: props.layout === 'inline' ? 'inline' : 'stacked',
|
|
111
124
|
label: props.description,
|
|
112
125
|
"aria-disabled": props.disabled ? 'true' : void 0,
|
|
113
|
-
"aria-invalid":
|
|
126
|
+
"aria-invalid": ariaInvalid,
|
|
114
127
|
elementRef: this.handleRef,
|
|
115
128
|
isGroup: isGroup
|
|
116
129
|
}), this.renderFields());
|
|
@@ -120,6 +133,7 @@ let FormFieldGroup = exports.FormFieldGroup = (_dec = (0, _emotion.withStyle)(_s
|
|
|
120
133
|
disabled: false,
|
|
121
134
|
rowSpacing: 'medium',
|
|
122
135
|
colSpacing: 'small',
|
|
123
|
-
vAlign: 'middle'
|
|
136
|
+
vAlign: 'middle',
|
|
137
|
+
isGroup: true
|
|
124
138
|
}, _FormFieldGroup)) || _class);
|
|
125
139
|
var _default = exports.default = FormFieldGroup;
|
|
@@ -7,7 +7,7 @@ var _vitest = require("vitest");
|
|
|
7
7
|
var _runAxeCheck = require("@instructure/ui-axe-check/lib/runAxeCheck.js");
|
|
8
8
|
require("@testing-library/jest-dom");
|
|
9
9
|
var _index = require("../index");
|
|
10
|
-
var _FormFieldLayout, _FormFieldLayout2
|
|
10
|
+
var _FormFieldLayout, _FormFieldLayout2;
|
|
11
11
|
/*
|
|
12
12
|
* The MIT License (MIT)
|
|
13
13
|
*
|
|
@@ -49,7 +49,7 @@ describe('<FormFieldLayout />', () => {
|
|
|
49
49
|
}))),
|
|
50
50
|
container = _render.container;
|
|
51
51
|
const formFieldLayout = container.querySelector("label[class$='-formFieldLayout']");
|
|
52
|
-
const formFieldLabel = container.querySelector("span[class$='-
|
|
52
|
+
const formFieldLabel = container.querySelector("span[class$='-formFieldLayout__label']");
|
|
53
53
|
expect(formFieldLayout).toBeInTheDocument();
|
|
54
54
|
expect(formFieldLabel).toBeInTheDocument();
|
|
55
55
|
expect(formFieldLabel).toHaveTextContent('Username');
|
|
@@ -64,13 +64,15 @@ describe('<FormFieldLayout />', () => {
|
|
|
64
64
|
});
|
|
65
65
|
it('should provide a ref to the input container', () => {
|
|
66
66
|
const inputContainerRef = _vitest.vi.fn();
|
|
67
|
+
const ref = /*#__PURE__*/_react.default.createRef();
|
|
67
68
|
(0, _react2.render)(/*#__PURE__*/_react.default.createElement(_index.FormFieldLayout, {
|
|
68
69
|
label: "Username",
|
|
69
70
|
inputContainerRef: inputContainerRef
|
|
70
|
-
},
|
|
71
|
-
type: "text"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
expect(
|
|
71
|
+
}, /*#__PURE__*/_react.default.createElement("input", {
|
|
72
|
+
type: "text",
|
|
73
|
+
ref: ref
|
|
74
|
+
})));
|
|
75
|
+
expect(ref.current).toBeInstanceOf(HTMLInputElement);
|
|
76
|
+
expect(inputContainerRef).toHaveBeenCalledWith(ref.current.parentElement);
|
|
75
77
|
});
|
|
76
78
|
});
|
|
@@ -8,14 +8,10 @@ exports.default = exports.FormFieldLayout = void 0;
|
|
|
8
8
|
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
|
|
9
9
|
var _react = require("react");
|
|
10
10
|
var _hasVisibleChildren = require("@instructure/ui-a11y-utils/lib/hasVisibleChildren.js");
|
|
11
|
-
var _ScreenReaderContent = require("@instructure/ui-a11y-content/lib/ScreenReaderContent");
|
|
12
|
-
var _Grid = require("@instructure/ui-grid/lib/Grid");
|
|
13
11
|
var _omitProps = require("@instructure/ui-react-utils/lib/omitProps.js");
|
|
14
|
-
var _pickProps = require("@instructure/ui-react-utils/lib/pickProps.js");
|
|
15
12
|
var _getElementType = require("@instructure/ui-react-utils/lib/getElementType.js");
|
|
16
13
|
var _withDeterministicId = require("@instructure/ui-react-utils/lib/DeterministicIdContext/withDeterministicId.js");
|
|
17
14
|
var _emotion = require("@instructure/emotion");
|
|
18
|
-
var _FormFieldLabel = require("../FormFieldLabel");
|
|
19
15
|
var _FormFieldMessages = require("../FormFieldMessages");
|
|
20
16
|
var _styles = _interopRequireDefault(require("./styles"));
|
|
21
17
|
var _props = require("./props");
|
|
@@ -55,6 +51,7 @@ let FormFieldLayout = exports.FormFieldLayout = (_dec = (0, _withDeterministicId
|
|
|
55
51
|
constructor(props) {
|
|
56
52
|
super(props);
|
|
57
53
|
this._messagesId = void 0;
|
|
54
|
+
this._labelId = void 0;
|
|
58
55
|
this.ref = null;
|
|
59
56
|
this.handleRef = el => {
|
|
60
57
|
const elementRef = this.props.elementRef;
|
|
@@ -63,69 +60,96 @@ let FormFieldLayout = exports.FormFieldLayout = (_dec = (0, _withDeterministicId
|
|
|
63
60
|
elementRef(el);
|
|
64
61
|
}
|
|
65
62
|
};
|
|
63
|
+
this.makeStyleProps = () => {
|
|
64
|
+
var _this$props$messages;
|
|
65
|
+
const hasNewErrorMsgAndIsGroup = !!((_this$props$messages = this.props.messages) !== null && _this$props$messages !== void 0 && _this$props$messages.find(m => m.type === 'newError')) && !!this.props.isGroup;
|
|
66
|
+
return {
|
|
67
|
+
hasMessages: this.hasMessages,
|
|
68
|
+
hasVisibleLabel: this.hasVisibleLabel,
|
|
69
|
+
// if true render error message above the controls (and below the label)
|
|
70
|
+
hasNewErrorMsgAndIsGroup: hasNewErrorMsgAndIsGroup
|
|
71
|
+
};
|
|
72
|
+
};
|
|
66
73
|
this.handleInputContainerRef = node => {
|
|
67
74
|
if (typeof this.props.inputContainerRef === 'function') {
|
|
68
75
|
this.props.inputContainerRef(node);
|
|
69
76
|
}
|
|
70
77
|
};
|
|
71
78
|
this._messagesId = props.messagesId || props.deterministicId();
|
|
79
|
+
this._labelId = props.deterministicId('FormField-Label');
|
|
72
80
|
}
|
|
73
81
|
componentDidMount() {
|
|
74
82
|
var _this$props$makeStyle, _this$props;
|
|
75
|
-
(_this$props$makeStyle = (_this$props = this.props).makeStyles) === null || _this$props$makeStyle === void 0 ? void 0 : _this$props$makeStyle.call(_this$props);
|
|
83
|
+
(_this$props$makeStyle = (_this$props = this.props).makeStyles) === null || _this$props$makeStyle === void 0 ? void 0 : _this$props$makeStyle.call(_this$props, this.makeStyleProps());
|
|
76
84
|
}
|
|
77
85
|
componentDidUpdate() {
|
|
78
86
|
var _this$props$makeStyle2, _this$props2;
|
|
79
|
-
(_this$props$makeStyle2 = (_this$props2 = this.props).makeStyles) === null || _this$props$makeStyle2 === void 0 ? void 0 : _this$props$makeStyle2.call(_this$props2);
|
|
87
|
+
(_this$props$makeStyle2 = (_this$props2 = this.props).makeStyles) === null || _this$props$makeStyle2 === void 0 ? void 0 : _this$props$makeStyle2.call(_this$props2, this.makeStyleProps());
|
|
80
88
|
}
|
|
81
89
|
get hasVisibleLabel() {
|
|
82
|
-
return this.props.label
|
|
90
|
+
return this.props.label ? (0, _hasVisibleChildren.hasVisibleChildren)(this.props.label) : false;
|
|
83
91
|
}
|
|
84
92
|
get hasMessages() {
|
|
85
|
-
|
|
93
|
+
if (!this.props.messages || this.props.messages.length == 0) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
for (const msg of this.props.messages) {
|
|
97
|
+
if (msg.text) {
|
|
98
|
+
if (typeof msg.text === 'string') {
|
|
99
|
+
return msg.text.length > 0;
|
|
100
|
+
}
|
|
101
|
+
// this is more complicated (e.g. an array, a React component,...)
|
|
102
|
+
// but we don't try to optimize here for these cases
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
86
107
|
}
|
|
87
108
|
get elementType() {
|
|
88
109
|
return (0, _getElementType.getElementType)(FormFieldLayout, this.props);
|
|
89
110
|
}
|
|
90
|
-
get inlineContainerAndLabel() {
|
|
91
|
-
// Return if both the component container and label will display inline
|
|
92
|
-
return this.props.inline && this.props.layout === 'inline';
|
|
93
|
-
}
|
|
94
111
|
renderLabel() {
|
|
95
112
|
if (this.hasVisibleLabel) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
var _this$props$styles2;
|
|
114
|
+
if (this.elementType == 'fieldset') {
|
|
115
|
+
var _this$props$styles;
|
|
116
|
+
// `legend` has some special built in CSS, this can only be reset
|
|
117
|
+
// this way https://stackoverflow.com/a/65866981/319473
|
|
118
|
+
return (0, _emotion.jsx)("legend", {
|
|
119
|
+
style: {
|
|
120
|
+
display: 'contents'
|
|
121
|
+
}
|
|
122
|
+
}, (0, _emotion.jsx)("span", {
|
|
123
|
+
css: (_this$props$styles = this.props.styles) === null || _this$props$styles === void 0 ? void 0 : _this$props$styles.formFieldLabel
|
|
124
|
+
}, this.props.label));
|
|
125
|
+
}
|
|
126
|
+
return (0, _emotion.jsx)("span", {
|
|
127
|
+
css: (_this$props$styles2 = this.props.styles) === null || _this$props$styles2 === void 0 ? void 0 : _this$props$styles2.formFieldLabel
|
|
128
|
+
}, this.props.label);
|
|
129
|
+
} else if (this.props.label) {
|
|
130
|
+
if (this.elementType == 'fieldset') {
|
|
131
|
+
return (0, _emotion.jsx)("legend", {
|
|
132
|
+
id: this._labelId,
|
|
133
|
+
style: {
|
|
134
|
+
display: 'contents'
|
|
135
|
+
}
|
|
136
|
+
}, this.props.label);
|
|
137
|
+
}
|
|
138
|
+
// needs to be wrapped because it needs an `id`
|
|
139
|
+
return (0, _emotion.jsx)("div", {
|
|
140
|
+
id: this._labelId
|
|
141
|
+
}, this.props.label);
|
|
142
|
+
} else return null;
|
|
117
143
|
}
|
|
118
144
|
renderVisibleMessages() {
|
|
119
|
-
return this.hasMessages ? (0, _emotion.jsx)(
|
|
120
|
-
offset: this.inlineContainerAndLabel ? void 0 : 3,
|
|
121
|
-
textAlign: this.inlineContainerAndLabel ? 'end' : void 0
|
|
122
|
-
}, (0, _emotion.jsx)(_FormFieldMessages.FormFieldMessages, {
|
|
145
|
+
return this.hasMessages ? (0, _emotion.jsx)(_FormFieldMessages.FormFieldMessages, {
|
|
123
146
|
id: this._messagesId,
|
|
124
|
-
messages: this.props.messages
|
|
125
|
-
|
|
147
|
+
messages: this.props.messages,
|
|
148
|
+
gridArea: "messages"
|
|
149
|
+
}) : null;
|
|
126
150
|
}
|
|
127
151
|
render() {
|
|
128
|
-
//
|
|
152
|
+
// Should be `<label>` if it's a FormField, fieldset if it's a group
|
|
129
153
|
const ElementType = this.elementType;
|
|
130
154
|
const _this$props3 = this.props,
|
|
131
155
|
makeStyles = _this$props3.makeStyles,
|
|
@@ -134,26 +158,20 @@ let FormFieldLayout = exports.FormFieldLayout = (_dec = (0, _withDeterministicId
|
|
|
134
158
|
isGroup = _this$props3.isGroup,
|
|
135
159
|
props = (0, _objectWithoutProperties2.default)(_this$props3, _excluded);
|
|
136
160
|
const width = props.width,
|
|
137
|
-
layout = props.layout,
|
|
138
161
|
children = props.children;
|
|
139
|
-
const
|
|
140
|
-
return (0, _emotion.jsx)(ElementType, Object.assign({}, (0, _omitProps.omitProps)(props, [...FormFieldLayout.allowedProps
|
|
162
|
+
const hasNewErrorMsgAndIsGroup = !!(messages !== null && messages !== void 0 && messages.find(m => m.type === 'newError')) && isGroup;
|
|
163
|
+
return (0, _emotion.jsx)(ElementType, Object.assign({}, (0, _omitProps.omitProps)(props, [...FormFieldLayout.allowedProps]), {
|
|
141
164
|
css: styles === null || styles === void 0 ? void 0 : styles.formFieldLayout,
|
|
165
|
+
"aria-describedby": this.hasMessages ? this._messagesId : void 0,
|
|
166
|
+
"aria-errormessage": this.props['aria-invalid'] ? this._messagesId : void 0,
|
|
142
167
|
style: {
|
|
143
168
|
width
|
|
144
169
|
},
|
|
145
|
-
"aria-describedby": this.hasMessages ? this._messagesId : void 0,
|
|
146
170
|
ref: this.handleRef
|
|
147
|
-
}), this.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}, (0, _pickProps.pickProps)(props, _Grid.Grid.allowedProps)), (0, _emotion.jsx)(_Grid.Grid.Row, null, this.renderLabel(), (0, _emotion.jsx)(_Grid.Grid.Col, {
|
|
152
|
-
width: this.inlineContainerAndLabel ? 'auto' : void 0,
|
|
153
|
-
elementRef: this.handleInputContainerRef
|
|
154
|
-
}, hasNewErrorMsg && (0, _emotion.jsx)("div", {
|
|
155
|
-
css: styles === null || styles === void 0 ? void 0 : styles.groupErrorMessage
|
|
156
|
-
}, this.renderVisibleMessages()), children)), !hasNewErrorMsg && this.renderVisibleMessages()));
|
|
171
|
+
}), this.renderLabel(), hasNewErrorMsgAndIsGroup && this.renderVisibleMessages(), (0, _emotion.jsx)("span", {
|
|
172
|
+
css: styles === null || styles === void 0 ? void 0 : styles.formFieldChildren,
|
|
173
|
+
ref: this.handleInputContainerRef
|
|
174
|
+
}, children), !hasNewErrorMsgAndIsGroup && this.renderVisibleMessages());
|
|
157
175
|
}
|
|
158
176
|
}, _FormFieldLayout.displayName = "FormFieldLayout", _FormFieldLayout.componentId = 'FormFieldLayout', _FormFieldLayout.propTypes = _props.propTypes, _FormFieldLayout.allowedProps = _props.allowedProps, _FormFieldLayout.defaultProps = {
|
|
159
177
|
inline: false,
|
|
@@ -29,6 +29,31 @@ var _emotion = require("@instructure/emotion");
|
|
|
29
29
|
* SOFTWARE.
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
+
const generateGridLayout = (isInlineLayout, hasNewErrorMsgAndIsGroup, hasVisibleLabel, hasMessages) => {
|
|
33
|
+
if (isInlineLayout) {
|
|
34
|
+
if (hasNewErrorMsgAndIsGroup) {
|
|
35
|
+
if (hasMessages) {
|
|
36
|
+
return `${hasVisibleLabel ? ' "label messages"' : '. messages'}
|
|
37
|
+
". controls"`;
|
|
38
|
+
} else {
|
|
39
|
+
return `${hasVisibleLabel ? ' "label controls"' : '. controls'}`;
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
return `${hasVisibleLabel ? ' "label controls"' : '. controls'}
|
|
43
|
+
${hasMessages ? ' ". messages"' : ''}`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// stacked layout -- in this case we could use a simple `Flex`
|
|
47
|
+
if (hasNewErrorMsgAndIsGroup) {
|
|
48
|
+
return `${hasVisibleLabel ? ' "label"' : ''}
|
|
49
|
+
${hasMessages ? ' "messages"' : ''}
|
|
50
|
+
"controls"`;
|
|
51
|
+
} else {
|
|
52
|
+
return `${hasVisibleLabel ? ' "label"' : ''}
|
|
53
|
+
"controls"
|
|
54
|
+
${hasMessages ? ' "messages"' : ''}`;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
32
57
|
/**
|
|
33
58
|
* ---
|
|
34
59
|
* private: true
|
|
@@ -36,18 +61,57 @@ var _emotion = require("@instructure/emotion");
|
|
|
36
61
|
* Generates the style object from the theme and provided additional information
|
|
37
62
|
* @param {Object} componentTheme The theme variable object.
|
|
38
63
|
* @param {Object} props the props of the component, the style is applied to
|
|
39
|
-
* @param {Object}
|
|
64
|
+
* @param {Object} styleProps
|
|
40
65
|
* @return {Object} The final style object, which will be used in the component
|
|
41
66
|
*/
|
|
42
|
-
const generateStyle = (componentTheme, props) => {
|
|
67
|
+
const generateStyle = (componentTheme, props, styleProps) => {
|
|
43
68
|
const inline = props.inline,
|
|
69
|
+
layout = props.layout,
|
|
70
|
+
vAlign = props.vAlign,
|
|
71
|
+
labelAlign = props.labelAlign,
|
|
44
72
|
margin = props.margin;
|
|
45
|
-
const
|
|
46
|
-
|
|
73
|
+
const hasMessages = styleProps.hasMessages,
|
|
74
|
+
hasVisibleLabel = styleProps.hasVisibleLabel,
|
|
75
|
+
hasNewErrorMsgAndIsGroup = styleProps.hasNewErrorMsgAndIsGroup;
|
|
76
|
+
const cssMargin = (0, _emotion.mapSpacingToShorthand)(margin, componentTheme.spacing);
|
|
77
|
+
const isInlineLayout = layout === 'inline';
|
|
78
|
+
// This is quite ugly, we should simplify it
|
|
79
|
+
const gridTemplateAreas = generateGridLayout(isInlineLayout, hasNewErrorMsgAndIsGroup, hasVisibleLabel, hasMessages);
|
|
80
|
+
let gridTemplateColumns = '100%'; // stacked layout
|
|
81
|
+
if (isInlineLayout) {
|
|
82
|
+
gridTemplateColumns = '1fr 3fr';
|
|
83
|
+
if (inline) {
|
|
84
|
+
gridTemplateColumns = 'auto 3fr';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const labelStyles = {
|
|
88
|
+
all: 'initial',
|
|
89
|
+
display: 'block',
|
|
90
|
+
gridArea: 'label',
|
|
91
|
+
color: componentTheme.color,
|
|
92
|
+
fontFamily: componentTheme.fontFamily,
|
|
93
|
+
fontWeight: componentTheme.fontWeight,
|
|
94
|
+
fontSize: componentTheme.fontSize,
|
|
95
|
+
lineHeight: componentTheme.lineHeight,
|
|
96
|
+
margin: '0 0 0.75rem 0',
|
|
97
|
+
...(isInlineLayout && {
|
|
98
|
+
// when inline add a small padding between the label and the control
|
|
99
|
+
paddingRight: componentTheme.inlinePadding,
|
|
100
|
+
// and use the horizontal alignment prop
|
|
101
|
+
[`@media screen and (min-width: ${componentTheme.stackedOrInlineBreakpoint})`]: {
|
|
102
|
+
textAlign: labelAlign
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
};
|
|
106
|
+
let alignItems = 'start';
|
|
107
|
+
if (vAlign == 'top') {
|
|
108
|
+
alignItems = 'start';
|
|
109
|
+
} else if (vAlign == 'middle') {
|
|
110
|
+
alignItems = 'center';
|
|
111
|
+
} else if (vAlign == 'bottom') {
|
|
112
|
+
alignItems = 'end';
|
|
113
|
+
}
|
|
47
114
|
return {
|
|
48
|
-
groupErrorMessage: {
|
|
49
|
-
margin: '0.5rem 0'
|
|
50
|
-
},
|
|
51
115
|
formFieldLayout: {
|
|
52
116
|
label: 'formFieldLayout',
|
|
53
117
|
all: 'initial',
|
|
@@ -58,13 +122,48 @@ const generateStyle = (componentTheme, props) => {
|
|
|
58
122
|
direction: 'inherit',
|
|
59
123
|
textAlign: 'start',
|
|
60
124
|
opacity: 'inherit',
|
|
61
|
-
display: '
|
|
125
|
+
display: 'grid',
|
|
126
|
+
alignItems: alignItems,
|
|
127
|
+
verticalAlign: 'middle',
|
|
128
|
+
// removes margin in inline layouts
|
|
129
|
+
gridTemplateColumns: gridTemplateColumns,
|
|
130
|
+
gridTemplateAreas: gridTemplateAreas,
|
|
131
|
+
[`@media screen and (max-width: ${componentTheme.stackedOrInlineBreakpoint})`]: {
|
|
132
|
+
// for small screens use the stacked layout
|
|
133
|
+
gridTemplateColumns: '100%',
|
|
134
|
+
gridTemplateAreas: generateGridLayout(false, hasNewErrorMsgAndIsGroup, hasVisibleLabel, hasMessages)
|
|
135
|
+
},
|
|
136
|
+
columnGap: '0.375rem',
|
|
62
137
|
width: '100%',
|
|
63
138
|
...(inline && {
|
|
64
|
-
display: 'inline-
|
|
65
|
-
verticalAlign: 'middle',
|
|
139
|
+
display: 'inline-grid',
|
|
66
140
|
width: 'auto'
|
|
67
141
|
})
|
|
142
|
+
},
|
|
143
|
+
formFieldLabel: {
|
|
144
|
+
label: 'formFieldLayout__label',
|
|
145
|
+
...(hasVisibleLabel && {
|
|
146
|
+
...labelStyles,
|
|
147
|
+
// NOTE: needs separate groups for `:is()` and `:-webkit-any()` because of css selector group validation (see https://www.w3.org/TR/selectors-3/#grouping)
|
|
148
|
+
'&:is(label)': labelStyles,
|
|
149
|
+
'&:-webkit-any(label)': labelStyles
|
|
150
|
+
})
|
|
151
|
+
},
|
|
152
|
+
formFieldChildren: {
|
|
153
|
+
label: 'formFieldLayout__children',
|
|
154
|
+
gridArea: 'controls',
|
|
155
|
+
// add a small margin between the message and the controls
|
|
156
|
+
...(hasMessages && hasNewErrorMsgAndIsGroup && {
|
|
157
|
+
marginTop: '0.375rem'
|
|
158
|
+
}),
|
|
159
|
+
...(hasMessages && !hasNewErrorMsgAndIsGroup && {
|
|
160
|
+
marginBottom: '0.75rem'
|
|
161
|
+
}),
|
|
162
|
+
...(isInlineLayout && inline && {
|
|
163
|
+
[`@media screen and (min-width: ${componentTheme.stackedOrInlineBreakpoint})`]: {
|
|
164
|
+
justifySelf: 'start'
|
|
165
|
+
}
|
|
166
|
+
})
|
|
68
167
|
}
|
|
69
168
|
};
|
|
70
169
|
};
|
|
@@ -28,10 +28,36 @@ exports.default = void 0;
|
|
|
28
28
|
* SOFTWARE.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Generates the theme object for the component from the theme and provided additional information
|
|
33
|
+
* @param {Object} theme The actual theme object.
|
|
34
|
+
* @return {Object} The final theme object with the overrides and component variables
|
|
35
|
+
*/
|
|
31
36
|
const generateComponentTheme = theme => {
|
|
32
|
-
|
|
37
|
+
var _colors$contrasts;
|
|
38
|
+
const colors = theme.colors,
|
|
39
|
+
typography = theme.typography,
|
|
40
|
+
spacing = theme.spacing,
|
|
41
|
+
breakpoints = theme.breakpoints,
|
|
42
|
+
themeName = theme.key;
|
|
43
|
+
const themeSpecificStyle = {
|
|
44
|
+
canvas: {
|
|
45
|
+
color: theme['ic-brand-font-color-dark']
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const componentVariables = {
|
|
49
|
+
color: colors === null || colors === void 0 ? void 0 : (_colors$contrasts = colors.contrasts) === null || _colors$contrasts === void 0 ? void 0 : _colors$contrasts.grey125125,
|
|
50
|
+
fontFamily: typography === null || typography === void 0 ? void 0 : typography.fontFamily,
|
|
51
|
+
fontWeight: typography === null || typography === void 0 ? void 0 : typography.fontWeightBold,
|
|
52
|
+
fontSize: typography === null || typography === void 0 ? void 0 : typography.fontSizeMedium,
|
|
53
|
+
lineHeight: typography === null || typography === void 0 ? void 0 : typography.lineHeightFit,
|
|
54
|
+
inlinePadding: spacing === null || spacing === void 0 ? void 0 : spacing.xxSmall,
|
|
55
|
+
stackedOrInlineBreakpoint: breakpoints === null || breakpoints === void 0 ? void 0 : breakpoints.medium,
|
|
56
|
+
spacing: theme.spacing
|
|
57
|
+
};
|
|
33
58
|
return {
|
|
34
|
-
|
|
59
|
+
...componentVariables,
|
|
60
|
+
...themeSpecificStyle[themeName]
|
|
35
61
|
};
|
|
36
62
|
};
|
|
37
63
|
var _default = exports.default = generateComponentTheme;
|
|
@@ -32,6 +32,7 @@ var _FormPropTypes = require("../FormPropTypes");
|
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
const propTypes = exports.propTypes = {
|
|
35
|
-
messages: _propTypes.default.arrayOf(_FormPropTypes.FormPropTypes.message)
|
|
35
|
+
messages: _propTypes.default.arrayOf(_FormPropTypes.FormPropTypes.message),
|
|
36
|
+
gridArea: _propTypes.default.string
|
|
36
37
|
};
|
|
37
|
-
const allowedProps = exports.allowedProps = ['messages'];
|
|
38
|
+
const allowedProps = exports.allowedProps = ['messages', 'gridArea'];
|
|
@@ -35,16 +35,18 @@ exports.default = void 0;
|
|
|
35
35
|
* Generates the style object from the theme and provided additional information
|
|
36
36
|
* @param {Object} componentTheme The theme variable object.
|
|
37
37
|
* @param {Object} props the props of the component, the style is applied to
|
|
38
|
-
* @param {Object} state the state of the component, the style is applied to
|
|
39
38
|
* @return {Object} The final style object, which will be used in the component
|
|
40
39
|
*/
|
|
41
|
-
const generateStyle = componentTheme => {
|
|
40
|
+
const generateStyle = (componentTheme, props) => {
|
|
42
41
|
return {
|
|
43
42
|
formFieldMessages: {
|
|
44
43
|
label: 'formFieldMessages',
|
|
45
44
|
padding: 0,
|
|
46
45
|
display: 'block',
|
|
47
|
-
margin: `calc(
|
|
46
|
+
margin: `calc(-${componentTheme.topMargin}) 0 0 0`,
|
|
47
|
+
...(props.gridArea && {
|
|
48
|
+
gridArea: props.gridArea
|
|
49
|
+
})
|
|
48
50
|
},
|
|
49
51
|
message: {
|
|
50
52
|
label: 'formFieldMessages__message',
|
package/lib/FormPropTypes.js
CHANGED
|
@@ -32,6 +32,12 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
|
32
32
|
|
|
33
33
|
const formMessageTypePropType = exports.formMessageTypePropType = _propTypes.default.oneOf(['error', 'newError', 'hint', 'success', 'screenreader-only']);
|
|
34
34
|
const formMessageChildPropType = exports.formMessageChildPropType = _propTypes.default.node;
|
|
35
|
+
|
|
36
|
+
// TODO it will be easier if this would be just a string
|
|
37
|
+
/**
|
|
38
|
+
* The text to display in the form message
|
|
39
|
+
*/
|
|
40
|
+
|
|
35
41
|
/**
|
|
36
42
|
* ---
|
|
37
43
|
* category: utilities/form
|
package/lib/index.js
CHANGED
|
@@ -15,12 +15,6 @@ Object.defineProperty(exports, "FormFieldGroup", {
|
|
|
15
15
|
return _FormFieldGroup.FormFieldGroup;
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
|
-
Object.defineProperty(exports, "FormFieldLabel", {
|
|
19
|
-
enumerable: true,
|
|
20
|
-
get: function () {
|
|
21
|
-
return _FormFieldLabel.FormFieldLabel;
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
18
|
Object.defineProperty(exports, "FormFieldLayout", {
|
|
25
19
|
enumerable: true,
|
|
26
20
|
get: function () {
|
|
@@ -46,7 +40,6 @@ Object.defineProperty(exports, "FormPropTypes", {
|
|
|
46
40
|
}
|
|
47
41
|
});
|
|
48
42
|
var _FormField = require("./FormField");
|
|
49
|
-
var _FormFieldLabel = require("./FormFieldLabel");
|
|
50
43
|
var _FormFieldMessage = require("./FormFieldMessage");
|
|
51
44
|
var _FormFieldMessages = require("./FormFieldMessages");
|
|
52
45
|
var _FormFieldLayout = require("./FormFieldLayout");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/ui-form-field",
|
|
3
|
-
"version": "10.12.0",
|
|
3
|
+
"version": "10.12.1-snapshot-0",
|
|
4
4
|
"description": "Form layout components.",
|
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
6
6
|
"module": "./es/index.js",
|
|
@@ -23,26 +23,26 @@
|
|
|
23
23
|
},
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@instructure/ui-axe-check": "10.12.0",
|
|
27
|
-
"@instructure/ui-babel-preset": "10.12.0",
|
|
28
|
-
"@instructure/ui-test-utils": "10.12.0",
|
|
29
|
-
"@instructure/ui-themes": "10.12.0",
|
|
26
|
+
"@instructure/ui-axe-check": "10.12.1-snapshot-0",
|
|
27
|
+
"@instructure/ui-babel-preset": "10.12.1-snapshot-0",
|
|
28
|
+
"@instructure/ui-test-utils": "10.12.1-snapshot-0",
|
|
29
|
+
"@instructure/ui-themes": "10.12.1-snapshot-0",
|
|
30
30
|
"@testing-library/jest-dom": "^6.6.3",
|
|
31
31
|
"@testing-library/react": "^16.0.1",
|
|
32
32
|
"vitest": "^2.1.8"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@babel/runtime": "^7.26.0",
|
|
36
|
-
"@instructure/console": "10.12.0",
|
|
37
|
-
"@instructure/emotion": "10.12.0",
|
|
38
|
-
"@instructure/shared-types": "10.12.0",
|
|
39
|
-
"@instructure/ui-a11y-content": "10.12.0",
|
|
40
|
-
"@instructure/ui-a11y-utils": "10.12.0",
|
|
41
|
-
"@instructure/ui-grid": "10.12.0",
|
|
42
|
-
"@instructure/ui-icons": "10.12.0",
|
|
43
|
-
"@instructure/ui-react-utils": "10.12.0",
|
|
44
|
-
"@instructure/ui-utils": "10.12.0",
|
|
45
|
-
"@instructure/uid": "10.12.0",
|
|
36
|
+
"@instructure/console": "10.12.1-snapshot-0",
|
|
37
|
+
"@instructure/emotion": "10.12.1-snapshot-0",
|
|
38
|
+
"@instructure/shared-types": "10.12.1-snapshot-0",
|
|
39
|
+
"@instructure/ui-a11y-content": "10.12.1-snapshot-0",
|
|
40
|
+
"@instructure/ui-a11y-utils": "10.12.1-snapshot-0",
|
|
41
|
+
"@instructure/ui-grid": "10.12.1-snapshot-0",
|
|
42
|
+
"@instructure/ui-icons": "10.12.1-snapshot-0",
|
|
43
|
+
"@instructure/ui-react-utils": "10.12.1-snapshot-0",
|
|
44
|
+
"@instructure/ui-utils": "10.12.1-snapshot-0",
|
|
45
|
+
"@instructure/uid": "10.12.1-snapshot-0",
|
|
46
46
|
"prop-types": "^15.8.1"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
package/src/FormField/README.md
CHANGED
|
@@ -9,7 +9,35 @@ components. In most cases it shouldn't be used directly.
|
|
|
9
9
|
---
|
|
10
10
|
type: example
|
|
11
11
|
---
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
|
|
12
|
+
<div>
|
|
13
|
+
<FormField id="_foo121" label="Stacked layout" width="400px" layout="stacked"
|
|
14
|
+
messages={[{type:'success', text: 'This is a success message'}, {type:'newError', text: 'An error message. It will wrap if the text is longer than the width of the container.'}]}>
|
|
15
|
+
<TextInput id="_foo121"/>
|
|
16
|
+
</FormField>
|
|
17
|
+
test
|
|
18
|
+
<hr/>
|
|
19
|
+
<FormField id="_foo122" label="Stacked layout (inline=true)" width="400px" layout="stacked" inline
|
|
20
|
+
messages={[{type:'success', text: 'This is a success message'}, {type:'newError', text: 'An error message. It will wrap if the text is longer than the width of the container.'}]}>
|
|
21
|
+
<TextInput id="_foo122"/>
|
|
22
|
+
</FormField>
|
|
23
|
+
test
|
|
24
|
+
<hr/>
|
|
25
|
+
<FormField id="_foo123" label="Inline layout" width="400px" layout="inline"
|
|
26
|
+
messages={[{type:'success', text: 'success!'}, {type:'newError', text: 'An error message. It will wrap if the text is longer than the width of the container.'}]}>
|
|
27
|
+
<TextInput id="_foo123"/>
|
|
28
|
+
</FormField>
|
|
29
|
+
test
|
|
30
|
+
<hr/>
|
|
31
|
+
<FormField id="_foo124" label="Inline layout (inline=true)" width="400px" layout="inline" inline
|
|
32
|
+
messages={[{type:'success', text: 'success!'}, {type:'newError', text: 'An error message. It will wrap if the text is longer than the width of the container.'}]}>
|
|
33
|
+
<TextInput id="_foo124"/>
|
|
34
|
+
</FormField>
|
|
35
|
+
test
|
|
36
|
+
<hr/>
|
|
37
|
+
<FormField id="_foo121" label={<ScreenReaderContent>hidden text</ScreenReaderContent>} width="400px" layout="stacked">
|
|
38
|
+
<TextInput id="_foo121" />
|
|
39
|
+
</FormField>
|
|
40
|
+
test
|
|
41
|
+
<hr/>
|
|
42
|
+
</div>
|
|
15
43
|
```
|