@instructure/ui-form-field 10.4.2-snapshot-10 → 10.4.2-snapshot-11

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 (40) hide show
  1. package/CHANGELOG.md +5 -2
  2. package/es/FormFieldGroup/index.js +2 -1
  3. package/es/FormFieldLayout/index.js +7 -2
  4. package/es/FormFieldLayout/props.js +2 -1
  5. package/es/FormFieldLayout/styles.js +3 -0
  6. package/es/FormFieldMessage/index.js +11 -2
  7. package/es/FormFieldMessage/styles.js +8 -0
  8. package/es/FormFieldMessage/theme.js +4 -2
  9. package/es/FormPropTypes.js +1 -1
  10. package/lib/FormFieldGroup/index.js +2 -1
  11. package/lib/FormFieldLayout/index.js +7 -2
  12. package/lib/FormFieldLayout/props.js +2 -1
  13. package/lib/FormFieldLayout/styles.js +3 -0
  14. package/lib/FormFieldMessage/index.js +11 -2
  15. package/lib/FormFieldMessage/styles.js +8 -0
  16. package/lib/FormFieldMessage/theme.js +4 -2
  17. package/lib/FormPropTypes.js +1 -1
  18. package/package.json +15 -15
  19. package/src/FormFieldGroup/index.tsx +1 -0
  20. package/src/FormFieldLayout/index.tsx +8 -2
  21. package/src/FormFieldLayout/props.ts +6 -3
  22. package/src/FormFieldLayout/styles.ts +3 -0
  23. package/src/FormFieldMessage/index.tsx +11 -2
  24. package/src/FormFieldMessage/props.ts +1 -1
  25. package/src/FormFieldMessage/styles.ts +6 -0
  26. package/src/FormFieldMessage/theme.ts +3 -2
  27. package/src/FormPropTypes.ts +7 -1
  28. package/tsconfig.build.tsbuildinfo +1 -1
  29. package/types/FormFieldGroup/index.d.ts.map +1 -1
  30. package/types/FormFieldLayout/index.d.ts.map +1 -1
  31. package/types/FormFieldLayout/props.d.ts +2 -1
  32. package/types/FormFieldLayout/props.d.ts.map +1 -1
  33. package/types/FormFieldLayout/styles.d.ts.map +1 -1
  34. package/types/FormFieldMessage/index.d.ts.map +1 -1
  35. package/types/FormFieldMessage/props.d.ts +1 -1
  36. package/types/FormFieldMessage/props.d.ts.map +1 -1
  37. package/types/FormFieldMessage/styles.d.ts.map +1 -1
  38. package/types/FormFieldMessage/theme.d.ts.map +1 -1
  39. package/types/FormPropTypes.d.ts +1 -1
  40. package/types/FormPropTypes.d.ts.map +1 -1
package/CHANGELOG.md CHANGED
@@ -3,9 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [10.4.2-snapshot-10](https://github.com/instructure/instructure-ui/compare/v10.4.1...v10.4.2-snapshot-10) (2024-11-06)
6
+ ## [10.4.2-snapshot-11](https://github.com/instructure/instructure-ui/compare/v10.4.1...v10.4.2-snapshot-11) (2024-11-06)
7
7
 
8
- **Note:** Version bump only for package @instructure/ui-form-field
8
+
9
+ ### Features
10
+
11
+ * **many:** add new form field error msg style + add asterisk for required fields ([9b03683](https://github.com/instructure/instructure-ui/commit/9b03683dadeef4c5deae2c60bea10686f143ff5d))
9
12
 
10
13
 
11
14
 
@@ -103,7 +103,8 @@ let FormFieldGroup = (_dec = withStyle(generateStyle, generateComponentTheme), _
103
103
  label: props.description,
104
104
  "aria-disabled": props.disabled ? 'true' : void 0,
105
105
  "aria-invalid": this.invalid ? 'true' : void 0,
106
- elementRef: this.handleRef
106
+ elementRef: this.handleRef,
107
+ isGroup: true
107
108
  }), this.renderFields());
108
109
  }
109
110
  }, _FormFieldGroup.displayName = "FormFieldGroup", _FormFieldGroup.componentId = 'FormFieldGroup', _FormFieldGroup.propTypes = propTypes, _FormFieldGroup.allowedProps = allowedProps, _FormFieldGroup.defaultProps = {
@@ -1,5 +1,5 @@
1
1
  import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
2
- const _excluded = ["makeStyles", "styles"];
2
+ const _excluded = ["makeStyles", "styles", "messages", "isGroup"];
3
3
  var _dec, _dec2, _class, _FormFieldLayout;
4
4
  /*
5
5
  * The MIT License (MIT)
@@ -123,10 +123,13 @@ let FormFieldLayout = (_dec = withDeterministicId(), _dec2 = withStyle(generateS
123
123
  const _this$props3 = this.props,
124
124
  makeStyles = _this$props3.makeStyles,
125
125
  styles = _this$props3.styles,
126
+ messages = _this$props3.messages,
127
+ isGroup = _this$props3.isGroup,
126
128
  props = _objectWithoutProperties(_this$props3, _excluded);
127
129
  const width = props.width,
128
130
  layout = props.layout,
129
131
  children = props.children;
132
+ const hasNewErrorMsg = !!(messages !== null && messages !== void 0 && messages.find(m => m.type === 'newError')) && isGroup;
130
133
  return jsx(ElementType, Object.assign({}, omitProps(props, [...FormFieldLayout.allowedProps, ...Grid.allowedProps]), {
131
134
  css: styles === null || styles === void 0 ? void 0 : styles.formFieldLayout,
132
135
  style: {
@@ -141,7 +144,9 @@ let FormFieldLayout = (_dec = withDeterministicId(), _dec2 = withStyle(generateS
141
144
  }, pickProps(props, Grid.allowedProps)), jsx(Grid.Row, null, this.renderLabel(), jsx(Grid.Col, {
142
145
  width: this.inlineContainerAndLabel ? 'auto' : void 0,
143
146
  elementRef: this.handleInputContainerRef
144
- }, children)), this.renderVisibleMessages()));
147
+ }, hasNewErrorMsg && jsx("div", {
148
+ css: styles === null || styles === void 0 ? void 0 : styles.groupErrorMessage
149
+ }, this.renderVisibleMessages()), children)), !hasNewErrorMsg && this.renderVisibleMessages()));
145
150
  }
146
151
  }, _FormFieldLayout.displayName = "FormFieldLayout", _FormFieldLayout.componentId = 'FormFieldLayout', _FormFieldLayout.propTypes = propTypes, _FormFieldLayout.allowedProps = allowedProps, _FormFieldLayout.defaultProps = {
147
152
  inline: false,
@@ -37,7 +37,8 @@ const propTypes = {
37
37
  vAlign: PropTypes.oneOf(['top', 'middle', 'bottom']),
38
38
  width: PropTypes.string,
39
39
  inputContainerRef: PropTypes.func,
40
- elementRef: PropTypes.func
40
+ elementRef: PropTypes.func,
41
+ isGroup: PropTypes.bool
41
42
  };
42
43
  const allowedProps = ['label', 'id', 'as', 'messages', 'messagesId', 'children', 'inline', 'layout', 'labelAlign', 'width', 'inputContainerRef', 'elementRef'
43
44
 
@@ -35,6 +35,9 @@
35
35
  const generateStyle = (_componentTheme, props) => {
36
36
  const inline = props.inline;
37
37
  return {
38
+ groupErrorMessage: {
39
+ margin: '0.5rem'
40
+ },
38
41
  formFieldLayout: {
39
42
  label: 'formFieldLayout',
40
43
  all: 'initial',
@@ -1,4 +1,4 @@
1
- var _dec, _class, _FormFieldMessage;
1
+ var _dec, _class, _FormFieldMessage, _IconWarningSolid;
2
2
  /*
3
3
  * The MIT License (MIT)
4
4
  *
@@ -26,6 +26,7 @@ var _dec, _class, _FormFieldMessage;
26
26
  /** @jsx jsx */
27
27
  import { Component } from 'react';
28
28
  import { ScreenReaderContent } from '@instructure/ui-a11y-content';
29
+ import { IconWarningSolid } from '@instructure/ui-icons';
29
30
  import { withStyle, jsx } from '@instructure/emotion';
30
31
  import generateStyle from './styles';
31
32
  import generateComponentTheme from './theme';
@@ -66,9 +67,17 @@ let FormFieldMessage = (_dec = withStyle(generateStyle, generateComponentTheme),
66
67
  children = _this$props3.children,
67
68
  styles = _this$props3.styles;
68
69
  return this.props.variant !== 'screenreader-only' ? jsx("span", {
70
+ css: {
71
+ display: 'flex'
72
+ }
73
+ }, this.props.variant === 'newError' && jsx("span", {
74
+ css: styles === null || styles === void 0 ? void 0 : styles.errorIcon
75
+ }, _IconWarningSolid || (_IconWarningSolid = jsx(IconWarningSolid, {
76
+ color: "error"
77
+ }))), jsx("span", {
69
78
  css: styles === null || styles === void 0 ? void 0 : styles.formFieldMessage,
70
79
  ref: this.handleRef
71
- }, children) : jsx(ScreenReaderContent, {
80
+ }, children)) : jsx(ScreenReaderContent, {
72
81
  elementRef: this.handleRef
73
82
  }, children);
74
83
  }
@@ -41,12 +41,20 @@ const generateStyle = (componentTheme, props) => {
41
41
  error: {
42
42
  color: componentTheme.colorError
43
43
  },
44
+ newError: {
45
+ color: componentTheme.colorError
46
+ },
44
47
  success: {
45
48
  color: componentTheme.colorSuccess
46
49
  },
47
50
  'screenreader-only': {}
48
51
  };
49
52
  return {
53
+ errorIcon: {
54
+ fontSize: componentTheme.fontSize,
55
+ marginRight: componentTheme.errorIconMarginRight,
56
+ lineHeight: 1.25
57
+ },
50
58
  formFieldMessage: {
51
59
  label: 'formFieldMessage',
52
60
  fontFamily: componentTheme.fontFamily,
@@ -31,7 +31,8 @@ const generateComponentTheme = theme => {
31
31
  var _colors$contrasts, _colors$contrasts2, _colors$contrasts3;
32
32
  const colors = theme.colors,
33
33
  typography = theme.typography,
34
- themeName = theme.key;
34
+ themeName = theme.key,
35
+ spacing = theme.spacing;
35
36
  const themeSpecificStyle = {
36
37
  canvas: {
37
38
  colorHint: theme['ic-brand-font-color-dark']
@@ -44,7 +45,8 @@ const generateComponentTheme = theme => {
44
45
  fontFamily: typography === null || typography === void 0 ? void 0 : typography.fontFamily,
45
46
  fontWeight: typography === null || typography === void 0 ? void 0 : typography.fontWeightNormal,
46
47
  fontSize: typography === null || typography === void 0 ? void 0 : typography.fontSizeSmall,
47
- lineHeight: typography === null || typography === void 0 ? void 0 : typography.lineHeight
48
+ lineHeight: typography === null || typography === void 0 ? void 0 : typography.lineHeight,
49
+ errorIconMarginRight: spacing.xxSmall
48
50
  };
49
51
  return {
50
52
  ...componentVariables,
@@ -23,7 +23,7 @@
23
23
  */
24
24
 
25
25
  import PropTypes from 'prop-types';
26
- const formMessageTypePropType = PropTypes.oneOf(['error', 'hint', 'success', 'screenreader-only']);
26
+ const formMessageTypePropType = PropTypes.oneOf(['error', 'newError', 'hint', 'success', 'screenreader-only']);
27
27
  const formMessageChildPropType = PropTypes.node;
28
28
  /**
29
29
  * ---
@@ -110,7 +110,8 @@ let FormFieldGroup = exports.FormFieldGroup = (_dec = (0, _emotion.withStyle)(_s
110
110
  label: props.description,
111
111
  "aria-disabled": props.disabled ? 'true' : void 0,
112
112
  "aria-invalid": this.invalid ? 'true' : void 0,
113
- elementRef: this.handleRef
113
+ elementRef: this.handleRef,
114
+ isGroup: true
114
115
  }), this.renderFields());
115
116
  }
116
117
  }, _FormFieldGroup.displayName = "FormFieldGroup", _FormFieldGroup.componentId = 'FormFieldGroup', _FormFieldGroup.propTypes = _props.propTypes, _FormFieldGroup.allowedProps = _props.allowedProps, _FormFieldGroup.defaultProps = {
@@ -20,7 +20,7 @@ var _FormFieldLabel = require("../FormFieldLabel");
20
20
  var _FormFieldMessages = require("../FormFieldMessages");
21
21
  var _styles = _interopRequireDefault(require("./styles"));
22
22
  var _props = require("./props");
23
- const _excluded = ["makeStyles", "styles"];
23
+ const _excluded = ["makeStyles", "styles", "messages", "isGroup"];
24
24
  var _dec, _dec2, _class, _FormFieldLayout;
25
25
  /*
26
26
  * The MIT License (MIT)
@@ -132,10 +132,13 @@ let FormFieldLayout = exports.FormFieldLayout = (_dec = (0, _withDeterministicId
132
132
  const _this$props3 = this.props,
133
133
  makeStyles = _this$props3.makeStyles,
134
134
  styles = _this$props3.styles,
135
+ messages = _this$props3.messages,
136
+ isGroup = _this$props3.isGroup,
135
137
  props = (0, _objectWithoutProperties2.default)(_this$props3, _excluded);
136
138
  const width = props.width,
137
139
  layout = props.layout,
138
140
  children = props.children;
141
+ const hasNewErrorMsg = !!(messages !== null && messages !== void 0 && messages.find(m => m.type === 'newError')) && isGroup;
139
142
  return (0, _emotion.jsx)(ElementType, Object.assign({}, (0, _omitProps.omitProps)(props, [...FormFieldLayout.allowedProps, ..._Grid.Grid.allowedProps]), {
140
143
  css: styles === null || styles === void 0 ? void 0 : styles.formFieldLayout,
141
144
  style: {
@@ -150,7 +153,9 @@ let FormFieldLayout = exports.FormFieldLayout = (_dec = (0, _withDeterministicId
150
153
  }, (0, _pickProps.pickProps)(props, _Grid.Grid.allowedProps)), (0, _emotion.jsx)(_Grid.Grid.Row, null, this.renderLabel(), (0, _emotion.jsx)(_Grid.Grid.Col, {
151
154
  width: this.inlineContainerAndLabel ? 'auto' : void 0,
152
155
  elementRef: this.handleInputContainerRef
153
- }, children)), this.renderVisibleMessages()));
156
+ }, hasNewErrorMsg && (0, _emotion.jsx)("div", {
157
+ css: styles === null || styles === void 0 ? void 0 : styles.groupErrorMessage
158
+ }, this.renderVisibleMessages()), children)), !hasNewErrorMsg && this.renderVisibleMessages()));
154
159
  }
155
160
  }, _FormFieldLayout.displayName = "FormFieldLayout", _FormFieldLayout.componentId = 'FormFieldLayout', _FormFieldLayout.propTypes = _props.propTypes, _FormFieldLayout.allowedProps = _props.allowedProps, _FormFieldLayout.defaultProps = {
156
161
  inline: false,
@@ -44,7 +44,8 @@ const propTypes = exports.propTypes = {
44
44
  vAlign: _propTypes.default.oneOf(['top', 'middle', 'bottom']),
45
45
  width: _propTypes.default.string,
46
46
  inputContainerRef: _propTypes.default.func,
47
- elementRef: _propTypes.default.func
47
+ elementRef: _propTypes.default.func,
48
+ isGroup: _propTypes.default.bool
48
49
  };
49
50
  const allowedProps = exports.allowedProps = ['label', 'id', 'as', 'messages', 'messagesId', 'children', 'inline', 'layout', 'labelAlign', 'width', 'inputContainerRef', 'elementRef'
50
51
 
@@ -41,6 +41,9 @@ exports.default = void 0;
41
41
  const generateStyle = (_componentTheme, props) => {
42
42
  const inline = props.inline;
43
43
  return {
44
+ groupErrorMessage: {
45
+ margin: '0.5rem'
46
+ },
44
47
  formFieldLayout: {
45
48
  label: 'formFieldLayout',
46
49
  all: 'initial',
@@ -7,11 +7,12 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.default = exports.FormFieldMessage = void 0;
8
8
  var _react = require("react");
9
9
  var _ScreenReaderContent = require("@instructure/ui-a11y-content/lib/ScreenReaderContent");
10
+ var _IconWarningSolid2 = require("@instructure/ui-icons/lib/IconWarningSolid.js");
10
11
  var _emotion = require("@instructure/emotion");
11
12
  var _styles = _interopRequireDefault(require("./styles"));
12
13
  var _theme = _interopRequireDefault(require("./theme"));
13
14
  var _props = require("./props");
14
- var _dec, _class, _FormFieldMessage;
15
+ var _dec, _class, _FormFieldMessage, _IconWarningSolid;
15
16
  /*
16
17
  * The MIT License (MIT)
17
18
  *
@@ -72,9 +73,17 @@ let FormFieldMessage = exports.FormFieldMessage = (_dec = (0, _emotion.withStyle
72
73
  children = _this$props3.children,
73
74
  styles = _this$props3.styles;
74
75
  return this.props.variant !== 'screenreader-only' ? (0, _emotion.jsx)("span", {
76
+ css: {
77
+ display: 'flex'
78
+ }
79
+ }, this.props.variant === 'newError' && (0, _emotion.jsx)("span", {
80
+ css: styles === null || styles === void 0 ? void 0 : styles.errorIcon
81
+ }, _IconWarningSolid || (_IconWarningSolid = (0, _emotion.jsx)(_IconWarningSolid2.IconWarningSolid, {
82
+ color: "error"
83
+ }))), (0, _emotion.jsx)("span", {
75
84
  css: styles === null || styles === void 0 ? void 0 : styles.formFieldMessage,
76
85
  ref: this.handleRef
77
- }, children) : (0, _emotion.jsx)(_ScreenReaderContent.ScreenReaderContent, {
86
+ }, children)) : (0, _emotion.jsx)(_ScreenReaderContent.ScreenReaderContent, {
78
87
  elementRef: this.handleRef
79
88
  }, children);
80
89
  }
@@ -47,12 +47,20 @@ const generateStyle = (componentTheme, props) => {
47
47
  error: {
48
48
  color: componentTheme.colorError
49
49
  },
50
+ newError: {
51
+ color: componentTheme.colorError
52
+ },
50
53
  success: {
51
54
  color: componentTheme.colorSuccess
52
55
  },
53
56
  'screenreader-only': {}
54
57
  };
55
58
  return {
59
+ errorIcon: {
60
+ fontSize: componentTheme.fontSize,
61
+ marginRight: componentTheme.errorIconMarginRight,
62
+ lineHeight: 1.25
63
+ },
56
64
  formFieldMessage: {
57
65
  label: 'formFieldMessage',
58
66
  fontFamily: componentTheme.fontFamily,
@@ -37,7 +37,8 @@ const generateComponentTheme = theme => {
37
37
  var _colors$contrasts, _colors$contrasts2, _colors$contrasts3;
38
38
  const colors = theme.colors,
39
39
  typography = theme.typography,
40
- themeName = theme.key;
40
+ themeName = theme.key,
41
+ spacing = theme.spacing;
41
42
  const themeSpecificStyle = {
42
43
  canvas: {
43
44
  colorHint: theme['ic-brand-font-color-dark']
@@ -50,7 +51,8 @@ const generateComponentTheme = theme => {
50
51
  fontFamily: typography === null || typography === void 0 ? void 0 : typography.fontFamily,
51
52
  fontWeight: typography === null || typography === void 0 ? void 0 : typography.fontWeightNormal,
52
53
  fontSize: typography === null || typography === void 0 ? void 0 : typography.fontSizeSmall,
53
- lineHeight: typography === null || typography === void 0 ? void 0 : typography.lineHeight
54
+ lineHeight: typography === null || typography === void 0 ? void 0 : typography.lineHeight,
55
+ errorIconMarginRight: spacing.xxSmall
54
56
  };
55
57
  return {
56
58
  ...componentVariables,
@@ -30,7 +30,7 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
30
30
  * SOFTWARE.
31
31
  */
32
32
 
33
- const formMessageTypePropType = exports.formMessageTypePropType = _propTypes.default.oneOf(['error', 'hint', 'success', 'screenreader-only']);
33
+ const formMessageTypePropType = exports.formMessageTypePropType = _propTypes.default.oneOf(['error', 'newError', 'hint', 'success', 'screenreader-only']);
34
34
  const formMessageChildPropType = exports.formMessageChildPropType = _propTypes.default.node;
35
35
  /**
36
36
  * ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-form-field",
3
- "version": "10.4.2-snapshot-10",
3
+ "version": "10.4.2-snapshot-11",
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.4.2-snapshot-10",
27
- "@instructure/ui-babel-preset": "10.4.2-snapshot-10",
28
- "@instructure/ui-test-utils": "10.4.2-snapshot-10",
29
- "@instructure/ui-themes": "10.4.2-snapshot-10",
26
+ "@instructure/ui-axe-check": "10.4.2-snapshot-11",
27
+ "@instructure/ui-babel-preset": "10.4.2-snapshot-11",
28
+ "@instructure/ui-test-utils": "10.4.2-snapshot-11",
29
+ "@instructure/ui-themes": "10.4.2-snapshot-11",
30
30
  "@testing-library/jest-dom": "^6.4.6",
31
31
  "@testing-library/react": "^16.0.1",
32
32
  "vitest": "^2.1.1"
33
33
  },
34
34
  "dependencies": {
35
35
  "@babel/runtime": "^7.25.6",
36
- "@instructure/console": "10.4.2-snapshot-10",
37
- "@instructure/emotion": "10.4.2-snapshot-10",
38
- "@instructure/shared-types": "10.4.2-snapshot-10",
39
- "@instructure/ui-a11y-content": "10.4.2-snapshot-10",
40
- "@instructure/ui-a11y-utils": "10.4.2-snapshot-10",
41
- "@instructure/ui-grid": "10.4.2-snapshot-10",
42
- "@instructure/ui-icons": "10.4.2-snapshot-10",
43
- "@instructure/ui-react-utils": "10.4.2-snapshot-10",
44
- "@instructure/ui-utils": "10.4.2-snapshot-10",
45
- "@instructure/uid": "10.4.2-snapshot-10",
36
+ "@instructure/console": "10.4.2-snapshot-11",
37
+ "@instructure/emotion": "10.4.2-snapshot-11",
38
+ "@instructure/shared-types": "10.4.2-snapshot-11",
39
+ "@instructure/ui-a11y-content": "10.4.2-snapshot-11",
40
+ "@instructure/ui-a11y-utils": "10.4.2-snapshot-11",
41
+ "@instructure/ui-grid": "10.4.2-snapshot-11",
42
+ "@instructure/ui-icons": "10.4.2-snapshot-11",
43
+ "@instructure/ui-react-utils": "10.4.2-snapshot-11",
44
+ "@instructure/ui-utils": "10.4.2-snapshot-11",
45
+ "@instructure/uid": "10.4.2-snapshot-11",
46
46
  "prop-types": "^15.8.1"
47
47
  },
48
48
  "peerDependencies": {
@@ -145,6 +145,7 @@ class FormFieldGroup extends Component<FormFieldGroupProps> {
145
145
  aria-disabled={props.disabled ? 'true' : undefined}
146
146
  aria-invalid={this.invalid ? 'true' : undefined}
147
147
  elementRef={this.handleRef}
148
+ isGroup
148
149
  >
149
150
  {this.renderFields()}
150
151
  </FormFieldLayout>
@@ -179,9 +179,12 @@ class FormFieldLayout extends Component<FormFieldLayoutProps> {
179
179
  // any cast is needed to prevent Expression produces a union type that is too complex to represent errors
180
180
  const ElementType = this.elementType as any
181
181
 
182
- const { makeStyles, styles, ...props } = this.props
182
+ const { makeStyles, styles, messages, isGroup, ...props } = this.props
183
183
 
184
184
  const { width, layout, children } = props
185
+
186
+ const hasNewErrorMsg =
187
+ !!messages?.find((m) => m.type === 'newError') && isGroup
185
188
  return (
186
189
  <ElementType
187
190
  {...omitProps(props, [
@@ -208,10 +211,13 @@ class FormFieldLayout extends Component<FormFieldLayoutProps> {
208
211
  width={this.inlineContainerAndLabel ? 'auto' : undefined}
209
212
  elementRef={this.handleInputContainerRef}
210
213
  >
214
+ {hasNewErrorMsg && (
215
+ <div css={styles?.groupErrorMessage}>{this.renderVisibleMessages()}</div>
216
+ )}
211
217
  {children}
212
218
  </Grid.Col>
213
219
  </Grid.Row>
214
- {this.renderVisibleMessages()}
220
+ {!hasNewErrorMsg && this.renderVisibleMessages()}
215
221
  </Grid>
216
222
  </ElementType>
217
223
  )
@@ -67,6 +67,7 @@ type FormFieldLayoutOwnProps = {
67
67
  * provides a reference to the underlying html root element
68
68
  */
69
69
  elementRef?: (element: Element | null) => void
70
+ isGroup?: boolean
70
71
  }
71
72
 
72
73
  type PropKeys = keyof FormFieldLayoutOwnProps
@@ -75,9 +76,10 @@ type AllowedPropKeys = Readonly<Array<PropKeys>>
75
76
 
76
77
  type FormFieldLayoutProps = FormFieldLayoutOwnProps &
77
78
  WithStyleProps<null, FormFieldLayoutStyle> &
78
- OtherHTMLAttributes<FormFieldLayoutOwnProps> & WithDeterministicIdProps
79
+ OtherHTMLAttributes<FormFieldLayoutOwnProps> &
80
+ WithDeterministicIdProps
79
81
 
80
- type FormFieldLayoutStyle = ComponentStyle<'formFieldLayout'>
82
+ type FormFieldLayoutStyle = ComponentStyle<'formFieldLayout' | 'groupErrorMessage'>
81
83
 
82
84
  const propTypes: PropValidators<PropKeys> = {
83
85
  label: PropTypes.node.isRequired,
@@ -92,7 +94,8 @@ const propTypes: PropValidators<PropKeys> = {
92
94
  vAlign: PropTypes.oneOf(['top', 'middle', 'bottom']),
93
95
  width: PropTypes.string,
94
96
  inputContainerRef: PropTypes.func,
95
- elementRef: PropTypes.func
97
+ elementRef: PropTypes.func,
98
+ isGroup: PropTypes.bool
96
99
  }
97
100
 
98
101
  const allowedProps: AllowedPropKeys = [
@@ -41,6 +41,9 @@ const generateStyle = (
41
41
  const { inline } = props
42
42
 
43
43
  return {
44
+ groupErrorMessage: {
45
+ margin: '0.5rem',
46
+ },
44
47
  formFieldLayout: {
45
48
  label: 'formFieldLayout',
46
49
  all: 'initial',
@@ -27,6 +27,8 @@ import { Component } from 'react'
27
27
 
28
28
  import { ScreenReaderContent } from '@instructure/ui-a11y-content'
29
29
 
30
+ import { IconWarningSolid } from '@instructure/ui-icons'
31
+
30
32
  import { withStyle, jsx } from '@instructure/emotion'
31
33
 
32
34
  import generateStyle from './styles'
@@ -78,8 +80,15 @@ class FormFieldMessage extends Component<FormFieldMessageProps> {
78
80
  const { children, styles } = this.props
79
81
 
80
82
  return this.props.variant !== 'screenreader-only' ? (
81
- <span css={styles?.formFieldMessage} ref={this.handleRef}>
82
- {children}
83
+ <span css={{ display: 'flex' }}>
84
+ {this.props.variant === 'newError' && (
85
+ <span css={styles?.errorIcon}>
86
+ <IconWarningSolid color="error" />
87
+ </span>
88
+ )}
89
+ <span css={styles?.formFieldMessage} ref={this.handleRef}>
90
+ {children}
91
+ </span>
83
92
  </span>
84
93
  ) : (
85
94
  <ScreenReaderContent elementRef={this.handleRef}>
@@ -46,7 +46,7 @@ type AllowedPropKeys = Readonly<Array<PropKeys>>
46
46
  type FormFieldMessageProps = FormFieldMessageOwnProps &
47
47
  WithStyleProps<FormFieldMessageTheme, FormFieldMessageStyle>
48
48
 
49
- type FormFieldMessageStyle = ComponentStyle<'formFieldMessage'>
49
+ type FormFieldMessageStyle = ComponentStyle<'formFieldMessage' | 'errorIcon'>
50
50
 
51
51
  const propTypes: PropValidators<PropKeys> = {
52
52
  variant: formMessageTypePropType,
@@ -45,11 +45,17 @@ const generateStyle = (
45
45
  const variants: Record<FormMessageType, { color?: string }> = {
46
46
  hint: { color: componentTheme.colorHint },
47
47
  error: { color: componentTheme.colorError },
48
+ newError: { color: componentTheme.colorError },
48
49
  success: { color: componentTheme.colorSuccess },
49
50
  'screenreader-only': {}
50
51
  }
51
52
 
52
53
  return {
54
+ errorIcon: {
55
+ fontSize: componentTheme.fontSize,
56
+ marginRight: componentTheme.errorIconMarginRight,
57
+ lineHeight: 1.25,
58
+ },
53
59
  formFieldMessage: {
54
60
  label: 'formFieldMessage',
55
61
  fontFamily: componentTheme.fontFamily,
@@ -31,7 +31,7 @@ import { FormFieldMessageTheme } from '@instructure/shared-types'
31
31
  * @return {Object} The final theme object with the overrides and component variables
32
32
  */
33
33
  const generateComponentTheme = (theme: Theme): FormFieldMessageTheme => {
34
- const { colors, typography, key: themeName } = theme
34
+ const { colors, typography, key: themeName, spacing } = theme
35
35
 
36
36
  const themeSpecificStyle: ThemeSpecificStyle<FormFieldMessageTheme> = {
37
37
  canvas: {
@@ -47,7 +47,8 @@ const generateComponentTheme = (theme: Theme): FormFieldMessageTheme => {
47
47
  fontFamily: typography?.fontFamily,
48
48
  fontWeight: typography?.fontWeightNormal,
49
49
  fontSize: typography?.fontSizeSmall,
50
- lineHeight: typography?.lineHeight
50
+ lineHeight: typography?.lineHeight,
51
+ errorIconMarginRight: spacing.xxSmall
51
52
  }
52
53
 
53
54
  return {
@@ -27,13 +27,19 @@ import PropTypes from 'prop-types'
27
27
 
28
28
  const formMessageTypePropType = PropTypes.oneOf([
29
29
  'error',
30
+ 'newError',
30
31
  'hint',
31
32
  'success',
32
33
  'screenreader-only'
33
34
  ])
34
35
  const formMessageChildPropType = PropTypes.node
35
36
 
36
- type FormMessageType = 'error' | 'hint' | 'success' | 'screenreader-only'
37
+ type FormMessageType =
38
+ | 'newError'
39
+ | 'error'
40
+ | 'hint'
41
+ | 'success'
42
+ | 'screenreader-only'
37
43
  type FormMessageChild = React.ReactNode
38
44
 
39
45
  /**