+
+
+
+ test
+
+
+
+
+ test
+
+
+
+
+ test
+
+
+
+
+ test
+
+ hidden text} width="400px" layout="stacked">
+
+
+ test
+
+
```
diff --git a/packages/ui-form-field/src/FormField/index.tsx b/packages/ui-form-field/src/FormField/index.tsx
index a5cfe3cfa3..04c664dea0 100644
--- a/packages/ui-form-field/src/FormField/index.tsx
+++ b/packages/ui-form-field/src/FormField/index.tsx
@@ -68,7 +68,6 @@ class FormField extends Component {
label={this.props.label}
vAlign={this.props.vAlign}
as="label"
- htmlFor={this.props.id}
elementRef={this.handleRef}
margin={this.props.margin}
/>
diff --git a/packages/ui-form-field/src/FormFieldGroup/__new-tests__/FormFieldGroup.test.tsx b/packages/ui-form-field/src/FormFieldGroup/__new-tests__/FormFieldGroup.test.tsx
index 908fa83da2..922074e799 100644
--- a/packages/ui-form-field/src/FormFieldGroup/__new-tests__/FormFieldGroup.test.tsx
+++ b/packages/ui-form-field/src/FormFieldGroup/__new-tests__/FormFieldGroup.test.tsx
@@ -66,7 +66,7 @@ describe('', () => {
)
const formFieldGroup = container.querySelector(
- "fieldset[class$='-formFieldLayout']"
+ "span[class$='-formFieldLayout__label']"
)
const firstNameInput = screen.getByLabelText('First:')
const middleNameInput = screen.getByLabelText('Middle:')
@@ -94,9 +94,7 @@ describe('', () => {
)
- const formFieldGroup = container.querySelector(
- "fieldset[class$='-formFieldLayout']"
- )
+ const formFieldGroup = container.querySelector('label')
expect(formFieldGroup).toBeInTheDocument()
})
@@ -136,7 +134,7 @@ describe('', () => {
expect(message).toHaveAttribute('id', messagesId)
})
- it('displays description message inside the legend', () => {
+ it('displays description message inside the label', () => {
const description = 'Please enter your full name'
const { container } = render(
@@ -154,7 +152,7 @@ describe('', () => {
)
const legend = container.querySelector(
- "legend[class$='-screenReaderContent']"
+ "span[class$='-formFieldLayout__label']"
)
expect(legend).toBeInTheDocument()
diff --git a/packages/ui-form-field/src/FormFieldGroup/index.tsx b/packages/ui-form-field/src/FormFieldGroup/index.tsx
index 16138eb518..f04f83ef99 100644
--- a/packages/ui-form-field/src/FormFieldGroup/index.tsx
+++ b/packages/ui-form-field/src/FormFieldGroup/index.tsx
@@ -23,7 +23,7 @@
*/
/** @jsx jsx */
-import { Component, Children, ReactElement } from 'react'
+import { Component, Children, ReactElement, AriaAttributes } from 'react'
import { Grid } from '@instructure/ui-grid'
import { pickProps, omitProps } from '@instructure/ui-react-utils'
@@ -53,7 +53,8 @@ class FormFieldGroup extends Component {
disabled: false,
rowSpacing: 'medium',
colSpacing: 'small',
- vAlign: 'middle'
+ vAlign: 'middle',
+ isGroup: true
}
ref: Element | null = null
@@ -77,14 +78,20 @@ class FormFieldGroup extends Component {
}
get makeStylesVariables(): FormFieldGroupStyleProps {
- return { invalid: this.invalid }
+ // new form errors dont need borders
+ const oldInvalid =
+ !!this.props.messages &&
+ this.props.messages.findIndex((message) => {
+ return message.type === 'error'
+ }) >= 0
+ return { invalid: oldInvalid }
}
get invalid() {
return (
!!this.props.messages &&
this.props.messages.findIndex((message) => {
- return message.type === 'error'
+ return message.type === 'error' || message.type === 'newError'
}) >= 0
)
}
@@ -134,7 +141,35 @@ class FormFieldGroup extends Component {
render() {
const { styles, makeStyles, isGroup, ...props } = this.props
-
+ // This is quite ugly, but according to ARIA spec the `aria-invalid` prop
+ // can only be used with certain roles see
+ // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-invalid#associated_roles
+ // `aria-invalid` is put on in FormFieldLayout because the error message
+ // DOM part gets there its ID.
+ let ariaInvalid: AriaAttributes['aria-invalid'] = undefined
+ 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)
+ ) {
+ ariaInvalid = 'true'
+ }
return (
{
layout={props.layout === 'inline' ? 'inline' : 'stacked'}
label={props.description}
aria-disabled={props.disabled ? 'true' : undefined}
- aria-invalid={this.invalid ? 'true' : undefined}
+ aria-invalid={ariaInvalid}
elementRef={this.handleRef}
isGroup={isGroup}
>
diff --git a/packages/ui-form-field/src/FormFieldLabel/__new-tests__/FormFieldLabel.test.tsx b/packages/ui-form-field/src/FormFieldLabel/__new-tests__/FormFieldLabel.test.tsx
deleted file mode 100644
index b1e8484b3b..0000000000
--- a/packages/ui-form-field/src/FormFieldLabel/__new-tests__/FormFieldLabel.test.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - present Instructure, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-import React from 'react'
-import { render } from '@testing-library/react'
-import { vi } from 'vitest'
-import { runAxeCheck } from '@instructure/ui-axe-check'
-import '@testing-library/jest-dom'
-
-import { FormFieldLabel } from '../index'
-
-describe('', () => {
- let consoleWarningMock: ReturnType
- let consoleErrorMock: ReturnType
-
- beforeEach(() => {
- // Mocking console to prevent test output pollution and expect for messages
- consoleWarningMock = vi
- .spyOn(console, 'warn')
- .mockImplementation(() => {}) as any
- consoleErrorMock = vi
- .spyOn(console, 'error')
- .mockImplementation(() => {}) as any
- })
-
- afterEach(() => {
- consoleWarningMock.mockRestore()
- consoleErrorMock.mockRestore()
- })
-
- it('should render', () => {
- const { container } = render(Foo)
-
- const formFieldLabel = container.querySelector(
- "span[class$='-formFieldLabel']"
- )
-
- expect(formFieldLabel).toBeInTheDocument()
- expect(formFieldLabel).toHaveTextContent('Foo')
- })
-
- it('should render as specified via the `as` prop', () => {
- const { container } = render(Foo)
-
- const formFieldLabel = container.querySelector('li')
-
- expect(formFieldLabel).toBeInTheDocument()
- expect(formFieldLabel).toHaveTextContent('Foo')
- })
-
- it('should meet a11y standards', async () => {
- const { container } = render(Foo)
-
- const axeCheck = await runAxeCheck(container)
-
- expect(axeCheck).toBe(true)
- })
-})
diff --git a/packages/ui-form-field/src/FormFieldLabel/index.tsx b/packages/ui-form-field/src/FormFieldLabel/index.tsx
deleted file mode 100644
index 2250bb2d5d..0000000000
--- a/packages/ui-form-field/src/FormFieldLabel/index.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - present Instructure, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-/** @jsx jsx */
-import { Component } from 'react'
-
-import { omitProps, getElementType } from '@instructure/ui-react-utils'
-import { withStyle, jsx } from '@instructure/emotion'
-
-import generateStyle from './styles'
-import generateComponentTheme from './theme'
-
-import { propTypes, allowedProps } from './props'
-import type { FormFieldLabelProps } from './props'
-
-/**
----
-parent: FormField
----
-
-This is a helper component that is used by most of the custom form
-components. In most cases it shouldn't be used directly.
-
-```js
----
-type: example
----
-Hello
-```
-
-**/
-@withStyle(generateStyle, generateComponentTheme)
-class FormFieldLabel extends Component {
- static readonly componentId = 'FormFieldLabel'
-
- static propTypes = propTypes
- static allowedProps = allowedProps
- static defaultProps = {
- as: 'span'
- } as const
-
- ref: Element | null = null
-
- handleRef = (el: Element | null) => {
- this.ref = el
- }
-
- componentDidMount() {
- this.props.makeStyles?.()
- }
-
- componentDidUpdate() {
- this.props.makeStyles?.()
- }
-
- render() {
- const ElementType = getElementType(FormFieldLabel, this.props)
-
- const { styles, children } = this.props
-
- return (
-
- {children}
-
- )
- }
-}
-
-export default FormFieldLabel
-export { FormFieldLabel }
diff --git a/packages/ui-form-field/src/FormFieldLabel/props.ts b/packages/ui-form-field/src/FormFieldLabel/props.ts
deleted file mode 100644
index 2ff1f89b97..0000000000
--- a/packages/ui-form-field/src/FormFieldLabel/props.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - present Instructure, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-import PropTypes from 'prop-types'
-
-import type {
- AsElementType,
- PropValidators,
- FormFieldLabelTheme,
- OtherHTMLAttributes
-} from '@instructure/shared-types'
-import type { WithStyleProps, ComponentStyle } from '@instructure/emotion'
-
-type FormFieldLabelOwnProps = {
- children: React.ReactNode
- as?: AsElementType
-}
-
-type PropKeys = keyof FormFieldLabelOwnProps
-
-type AllowedPropKeys = Readonly>
-
-type FormFieldLabelProps = FormFieldLabelOwnProps &
- WithStyleProps &
- OtherHTMLAttributes
-
-type FormFieldLabelStyle = ComponentStyle<'formFieldLabel'>
-
-const propTypes: PropValidators = {
- children: PropTypes.node.isRequired,
- as: PropTypes.elementType
-}
-
-const allowedProps: AllowedPropKeys = ['as', 'children']
-
-export type { FormFieldLabelProps, FormFieldLabelStyle }
-export { propTypes, allowedProps }
diff --git a/packages/ui-form-field/src/FormFieldLabel/styles.ts b/packages/ui-form-field/src/FormFieldLabel/styles.ts
deleted file mode 100644
index 4900c1a92e..0000000000
--- a/packages/ui-form-field/src/FormFieldLabel/styles.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - present Instructure, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-import { hasVisibleChildren } from '@instructure/ui-a11y-utils'
-
-import type { FormFieldLabelTheme } from '@instructure/shared-types'
-import type { FormFieldLabelProps, FormFieldLabelStyle } from './props'
-
-/**
- * ---
- * private: true
- * ---
- * Generates the style object from the theme and provided additional information
- * @param {Object} componentTheme The theme variable object.
- * @param {Object} props the props of the component, the style is applied to
- * @param {Object} state the state of the component, the style is applied to
- * @return {Object} The final style object, which will be used in the component
- */
-const generateStyle = (
- componentTheme: FormFieldLabelTheme,
- props: FormFieldLabelProps
-): FormFieldLabelStyle => {
- const { children } = props
-
- const hasContent = hasVisibleChildren(children)
-
- const labelStyles = {
- all: 'initial',
- display: 'block',
- ...(hasContent && {
- color: componentTheme.color,
- fontFamily: componentTheme.fontFamily,
- fontWeight: componentTheme.fontWeight,
- fontSize: componentTheme.fontSize,
- lineHeight: componentTheme.lineHeight,
- margin: 0,
- textAlign: 'inherit'
- })
- }
-
- return {
- formFieldLabel: {
- label: 'formFieldLabel',
- ...labelStyles,
-
- // NOTE: needs separate groups for `:is()` and `:-webkit-any()` because of css selector group validation (see https://www.w3.org/TR/selectors-3/#grouping)
- '&:is(label)': labelStyles,
- '&:-webkit-any(label)': labelStyles
- }
- }
-}
-
-export default generateStyle
diff --git a/packages/ui-form-field/src/FormFieldLabel/theme.ts b/packages/ui-form-field/src/FormFieldLabel/theme.ts
deleted file mode 100644
index e30f82a61d..0000000000
--- a/packages/ui-form-field/src/FormFieldLabel/theme.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 - present Instructure, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-import type { Theme, ThemeSpecificStyle } from '@instructure/ui-themes'
-import { FormFieldLabelTheme } from '@instructure/shared-types'
-
-/**
- * Generates the theme object for the component from the theme and provided additional information
- * @param {Object} theme The actual theme object.
- * @return {Object} The final theme object with the overrides and component variables
- */
-const generateComponentTheme = (theme: Theme): FormFieldLabelTheme => {
- const { colors, typography, key: themeName } = theme
-
- const themeSpecificStyle: ThemeSpecificStyle = {
- canvas: {
- color: theme['ic-brand-font-color-dark']
- }
- }
-
- const componentVariables: FormFieldLabelTheme = {
- color: colors?.contrasts?.grey125125,
- fontFamily: typography?.fontFamily,
- fontWeight: typography?.fontWeightBold,
- fontSize: typography?.fontSizeMedium,
- lineHeight: typography?.lineHeightFit
- }
-
- return {
- ...componentVariables,
- ...themeSpecificStyle[themeName]
- }
-}
-
-export default generateComponentTheme
diff --git a/packages/ui-form-field/src/FormFieldLayout/__new-tests__/FormFieldLayout.test.tsx b/packages/ui-form-field/src/FormFieldLayout/__new-tests__/FormFieldLayout.test.tsx
index c4f1ecd16e..ecb7151b6a 100644
--- a/packages/ui-form-field/src/FormFieldLayout/__new-tests__/FormFieldLayout.test.tsx
+++ b/packages/ui-form-field/src/FormFieldLayout/__new-tests__/FormFieldLayout.test.tsx
@@ -23,7 +23,7 @@
*/
import React from 'react'
-import { render, screen } from '@testing-library/react'
+import { render } from '@testing-library/react'
import { vi } from 'vitest'
import { runAxeCheck } from '@instructure/ui-axe-check'
import '@testing-library/jest-dom'
@@ -56,7 +56,7 @@ describe('', () => {
"label[class$='-formFieldLayout']"
)
const formFieldLabel = container.querySelector(
- "span[class$='-formFieldLabel']"
+ "span[class$='-formFieldLayout__label']"
)
expect(formFieldLayout).toBeInTheDocument()
@@ -74,15 +74,13 @@ describe('', () => {
it('should provide a ref to the input container', () => {
const inputContainerRef = vi.fn()
-
+ const ref = React.createRef()
render(
-
+
)
-
- const input = screen.getByLabelText('Username')
-
- expect(inputContainerRef).toHaveBeenCalledWith(input.parentElement)
+ expect(ref.current).toBeInstanceOf(HTMLInputElement)
+ expect(inputContainerRef).toHaveBeenCalledWith(ref.current!.parentElement)
})
})
diff --git a/packages/ui-form-field/src/FormFieldLayout/index.tsx b/packages/ui-form-field/src/FormFieldLayout/index.tsx
index 37b2d160fe..ee6bfc91d2 100644
--- a/packages/ui-form-field/src/FormFieldLayout/index.tsx
+++ b/packages/ui-form-field/src/FormFieldLayout/index.tsx
@@ -24,25 +24,17 @@
/** @jsx jsx */
import { Component } from 'react'
-
import { hasVisibleChildren } from '@instructure/ui-a11y-utils'
-import { ScreenReaderContent } from '@instructure/ui-a11y-content'
-import { Grid } from '@instructure/ui-grid'
import {
omitProps,
- pickProps,
getElementType,
withDeterministicId
} from '@instructure/ui-react-utils'
import { withStyle, jsx } from '@instructure/emotion'
-
-import { FormFieldLabel } from '../FormFieldLabel'
import { FormFieldMessages } from '../FormFieldMessages'
-
import generateStyle from './styles'
-
-import { propTypes, allowedProps } from './props'
+import { propTypes, allowedProps, FormFieldStyleProps } from './props'
import type { FormFieldLayoutProps } from './props'
import generateComponentTheme from './theme'
@@ -68,9 +60,11 @@ class FormFieldLayout extends Component {
constructor(props: FormFieldLayoutProps) {
super(props)
this._messagesId = props.messagesId || props.deterministicId!()
+ this._labelId = props.deterministicId!('FormField-Label')
}
private _messagesId: string
+ private _labelId: string
ref: Element | null = null
@@ -85,31 +79,51 @@ class FormFieldLayout extends Component {
}
componentDidMount() {
- this.props.makeStyles?.()
+ this.props.makeStyles?.(this.makeStyleProps())
}
componentDidUpdate() {
- this.props.makeStyles?.()
+ this.props.makeStyles?.(this.makeStyleProps())
+ }
+
+ makeStyleProps = (): FormFieldStyleProps => {
+ const hasNewErrorMsgAndIsGroup =
+ !!this.props.messages?.find((m) => m.type === 'newError') &&
+ !!this.props.isGroup
+ return {
+ hasMessages: this.hasMessages,
+ hasVisibleLabel: this.hasVisibleLabel,
+ // if true render error message above the controls (and below the label)
+ hasNewErrorMsgAndIsGroup: hasNewErrorMsgAndIsGroup
+ }
}
get hasVisibleLabel() {
- return this.props.label && hasVisibleChildren(this.props.label)
+ return this.props.label ? hasVisibleChildren(this.props.label) : false
}
get hasMessages() {
- return this.props.messages && this.props.messages.length > 0
+ if (!this.props.messages || this.props.messages.length == 0) {
+ return false
+ }
+ for (const msg of this.props.messages) {
+ if (msg.text) {
+ if (typeof msg.text === 'string') {
+ return msg.text.length > 0
+ }
+ // this is more complicated (e.g. an array, a React component,...)
+ // but we don't try to optimize here for these cases
+ return true
+ }
+ }
+ return false
}
get elementType() {
return getElementType(FormFieldLayout, this.props)
}
- get inlineContainerAndLabel() {
- // Return if both the component container and label will display inline
- return this.props.inline && this.props.layout === 'inline'
- }
-
- handleInputContainerRef = (node: HTMLSpanElement | null) => {
+ handleInputContainerRef = (node: HTMLElement | null) => {
if (typeof this.props.inputContainerRef === 'function') {
this.props.inputContainerRef(node)
}
@@ -117,101 +131,73 @@ class FormFieldLayout extends Component {
renderLabel() {
if (this.hasVisibleLabel) {
+ if (this.elementType == 'fieldset') {
+ // `legend` has some special built in CSS, this can only be reset
+ // this way https://stackoverflow.com/a/65866981/319473
+ return (
+
+ )
+ }
return (
-
-
- {this.props.label}
-
-
+ {this.props.label}
)
- } else if (this.elementType !== 'fieldset') {
- // to avoid duplicate label/legend content
- return this.props.label
- } else {
- return null
- }
- }
-
- renderLegend() {
- // note: the legend element must be the first child of a fieldset element for SR
- // so we render it twice in that case (once for SR-only and one that is visible)
- return (
-
- {this.props.label}
- {this.hasMessages && (
-
- )}
-
- )
+ } else if (this.props.label) {
+ if (this.elementType == 'fieldset') {
+ return (
+
+ )
+ }
+ // needs to be wrapped because it needs an `id`
+ return
{this.props.label}
+ } else return null
}
renderVisibleMessages() {
return this.hasMessages ? (
-
-
-
-
-
+
) : null
}
render() {
- // any cast is needed to prevent Expression produces a union type that is too complex to represent errors
- const ElementType = this.elementType as any
+ // Should be `