Skip to content

Commit

Permalink
feat(many): add new form field error msg style + add asterisk for req…
Browse files Browse the repository at this point in the history
…uired fields
  • Loading branch information
balzss committed Oct 11, 2024
1 parent 7737940 commit 9173b37
Show file tree
Hide file tree
Showing 24 changed files with 309 additions and 396 deletions.
3 changes: 3 additions & 0 deletions packages/shared-types/src/ComponentThemeVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ export type CheckboxFacadeTheme = {
iconSizeSmall: string
iconSizeMedium: string
iconSizeLarge: string
errorBorderColor: Colors['contrasts']['red4570']
}

export type ToggleFacadeTheme = {
Expand Down Expand Up @@ -343,6 +344,7 @@ export type ToggleFacadeTheme = {
labelFontSizeSmall: Typography['fontSizeSmall']
labelFontSizeMedium: Typography['fontSizeMedium']
labelFontSizeLarge: Typography['fontSizeLarge']
errorBorderColor: Colors['contrasts']['red4570']
}

export type CodeEditorTheme = {
Expand Down Expand Up @@ -1389,6 +1391,7 @@ export type TextInputTheme = {
mediumHeight: Forms['inputHeightMedium']
largeFontSize: Typography['fontSizeLarge']
largeHeight: Forms['inputHeightLarge']
requiredInvalidColor: Colors['contrasts']['red5782']
}

export type ToggleDetailsTheme = {
Expand Down
4 changes: 3 additions & 1 deletion packages/ui-checkbox/src/Checkbox/CheckboxFacade/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type CheckboxFacadeOwnProps = {
* Visual state showing that child checkboxes are a combination of checked and unchecked
*/
indeterminate?: boolean
error?: boolean
}

type PropKeys = keyof CheckboxFacadeOwnProps
Expand All @@ -57,7 +58,8 @@ const propTypes: PropValidators<PropKeys> = {
focused: PropTypes.bool,
hovered: PropTypes.bool,
size: PropTypes.oneOf(['small', 'medium', 'large']),
indeterminate: PropTypes.bool
indeterminate: PropTypes.bool,
error: PropTypes.bool
}

const allowedProps: AllowedPropKeys = [
Expand Down
6 changes: 4 additions & 2 deletions packages/ui-checkbox/src/Checkbox/CheckboxFacade/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const generateStyle = (
componentTheme: CheckboxFacadeTheme,
props: CheckboxFacadeProps
): CheckboxFacadeStyle => {
const { size, checked, focused, hovered, indeterminate } = props
const { size, checked, focused, hovered, indeterminate, error } = props

const isChecked = checked || indeterminate

Expand Down Expand Up @@ -87,7 +87,9 @@ const generateStyle = (
boxSizing: 'border-box',
flexShrink: 0,
transition: 'all 0.2s',
border: `${componentTheme.borderWidth} solid ${componentTheme.borderColor}`,
border: error
? `${componentTheme.borderWidth} solid ${componentTheme.errorBorderColor}`
: `${componentTheme.borderWidth} solid ${componentTheme.borderColor}`,
borderRadius: componentTheme.borderRadius,
marginInlineEnd: componentTheme.marginRight,
marginInlineStart: '0',
Expand Down
1 change: 1 addition & 0 deletions packages/ui-checkbox/src/Checkbox/CheckboxFacade/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const generateComponentTheme = (theme: Theme): CheckboxFacadeTheme => {
color: colors?.contrasts?.white1010,
borderWidth: borders?.widthSmall,
borderColor: colors?.contrasts?.grey1214,
errorBorderColor: colors?.ui?.textError,
borderRadius: borders?.radiusMedium,
background: colors?.contrasts?.white1010,
marginRight: spacing?.xSmall,
Expand Down
4 changes: 3 additions & 1 deletion packages/ui-checkbox/src/Checkbox/ToggleFacade/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type ToggleFacadeOwnProps = {
focused?: boolean
size?: 'small' | 'medium' | 'large'
labelPlacement?: 'top' | 'start' | 'end'
error?: boolean
}

type PropKeys = keyof ToggleFacadeOwnProps
Expand All @@ -59,7 +60,8 @@ const propTypes: PropValidators<PropKeys> = {
readOnly: PropTypes.bool,
focused: PropTypes.bool,
size: PropTypes.oneOf(['small', 'medium', 'large']),
labelPlacement: PropTypes.oneOf(['top', 'start', 'end'])
labelPlacement: PropTypes.oneOf(['top', 'start', 'end']),
error: PropTypes.bool
}

const allowedProps: AllowedPropKeys = [
Expand Down
8 changes: 5 additions & 3 deletions packages/ui-checkbox/src/Checkbox/ToggleFacade/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const generateStyle = (
componentTheme: ToggleFacadeTheme,
props: ToggleFacadeProps
): ToggleFacadeStyle => {
const { size, checked, focused, labelPlacement } = props
const { size, checked, focused, labelPlacement, error } = props

const labelPlacementVariants = {
start: {
Expand Down Expand Up @@ -81,7 +81,6 @@ const generateStyle = (
alignItems: 'center',
...(labelPlacement === 'top' && { display: 'block' })
},

facade: {
label: 'toggleFacade__facade',
background: componentTheme.background,
Expand All @@ -92,7 +91,10 @@ const generateStyle = (
position: 'relative',
borderRadius: '3rem',
verticalAlign: 'middle',
boxShadow: `inset 0 0 0 ${componentTheme.borderWidth} ${componentTheme.borderColor}`,
boxShadow:
error && !checked
? `inset 0 0 0 ${componentTheme.borderWidth} ${componentTheme.errorBorderColor}`
: `inset 0 0 0 ${componentTheme.borderWidth} ${componentTheme.borderColor}`,
height: componentTheme.toggleSize,
width: `calc(${componentTheme.toggleSize} * 1.5)`,
...labelPlacementVariants[labelPlacement!].facade,
Expand Down
1 change: 1 addition & 0 deletions packages/ui-checkbox/src/Checkbox/ToggleFacade/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const generateComponentTheme = (theme: Theme): ToggleFacadeTheme => {

const componentVariables: ToggleFacadeTheme = {
color: colors?.contrasts?.white1010,
errorBorderColor: colors?.ui?.textError,
background: colors?.contrasts?.grey1111,
borderColor: colors?.contrasts?.grey1214,
borderWidth: borders?.widthSmall,
Expand Down
42 changes: 37 additions & 5 deletions packages/ui-checkbox/src/Checkbox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { CheckboxFacade } from './CheckboxFacade'
import { ToggleFacade } from './ToggleFacade'

import generateStyle from './styles'
import generateComponentTheme from './theme'

import { propTypes, allowedProps } from './props'
import type { CheckboxProps, CheckboxState } from './props'
Expand All @@ -57,7 +58,7 @@ tags: toggle, switch
**/

@withDeterministicId()
@withStyle(generateStyle, null)
@withStyle(generateStyle, generateComponentTheme)
@testable()
class Checkbox extends Component<CheckboxProps, CheckboxState> {
static readonly componentId = 'Checkbox'
Expand Down Expand Up @@ -181,6 +182,16 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
return isActiveElement(this._input)
}

get isNewError() {
return !!this.props.messages?.find((m) => m.type === 'newError')
}

get invalid() {
return !!this.props.messages?.find(
(m) => m.type === 'newError' || m.type === 'error'
)
}

focus() {
this._input && this._input.focus()
}
Expand All @@ -194,7 +205,9 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
readOnly,
indeterminate,
labelPlacement,
themeOverride
themeOverride,
isRequired,
styles
} = this.props

const { hovered, focused } = this.state
Expand All @@ -214,8 +227,12 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
readOnly={readOnly}
labelPlacement={labelPlacement}
themeOverride={themeOverride as Partial<ToggleFacadeTheme>}
error={!!this.props.messages?.find((m) => m.type === 'newError')}
>
{label}
{isRequired && (
<span css={this.invalid ? styles?.requiredInvalid : {}}> *</span>
)}
</ToggleFacade>
)
} else {
Expand All @@ -227,18 +244,31 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
checked={this.checked}
indeterminate={indeterminate}
themeOverride={themeOverride as Partial<CheckboxFacadeTheme>}
error={this.isNewError}
>
{label}
{isRequired && (
<span css={this.invalid ? styles?.requiredInvalid : {}}> *</span>
)}
</CheckboxFacade>
)
}
}

renderMessages() {
const { messages } = this.props
const { messages, styles, variant } = this.props

return messages && messages.length > 0 ? (
<View display="block" margin="small 0 0">
<View
display="block"
margin="small 0 0"
css={
this.isNewError &&
(variant === 'toggle'
? styles?.indentedToggleError
: styles?.indentedError)
}
>
<FormFieldMessages messages={messages} />
</View>
) : null
Expand All @@ -256,7 +286,8 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
onMouseOut,
indeterminate,
variant,
styles
styles,
isRequired
} = this.props

const props = omitProps(this.props, Checkbox.allowedProps)
Expand Down Expand Up @@ -284,6 +315,7 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
ref={(c) => {
this._input = c
}}
required={isRequired}
disabled={disabled || readOnly}
aria-checked={indeterminate ? 'mixed' : undefined}
css={styles?.input}
Expand Down
12 changes: 10 additions & 2 deletions packages/ui-checkbox/src/Checkbox/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type CheckboxOwnProps = {
variant?: 'simple' | 'toggle'
inline?: boolean
labelPlacement?: 'top' | 'start' | 'end'
isRequired?: boolean
}

type PropKeys = keyof CheckboxOwnProps
Expand All @@ -91,7 +92,13 @@ type CheckboxProps = CheckboxOwnProps &
WithDeterministicIdProps

type CheckboxStyle = ComponentStyle<
'checkbox' | 'input' | 'control' | 'container'
| 'checkbox'
| 'input'
| 'control'
| 'container'
| 'requiredInvalid'
| 'indentedError'
| 'indentedToggleError'
>

const propTypes: PropValidators<PropKeys> = {
Expand All @@ -113,7 +120,8 @@ const propTypes: PropValidators<PropKeys> = {
size: PropTypes.oneOf(['small', 'medium', 'large']),
variant: PropTypes.oneOf(['simple', 'toggle']),
inline: PropTypes.bool,
labelPlacement: PropTypes.oneOf(['top', 'start', 'end'])
labelPlacement: PropTypes.oneOf(['top', 'start', 'end']),
isRequired: PropTypes.bool
}

const allowedProps: AllowedPropKeys = [
Expand Down
11 changes: 10 additions & 1 deletion packages/ui-checkbox/src/Checkbox/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,21 @@ import type { ComponentTheme } from '@instructure/shared-types'
* @return {Object} The final style object, which will be used in the component
*/
const generateStyle = (
_componentTheme: ComponentTheme,
componentTheme: ComponentTheme,
props: CheckboxProps
): CheckboxStyle => {
const { inline, disabled } = props

return {
requiredInvalid: {
color: componentTheme.requiredInvalidColor
},
indentedError: {
paddingLeft: '28px'
},
indentedToggleError: {
paddingLeft: '56px'
},
checkbox: {
label: 'checkbox',
position: 'relative',
Expand Down
44 changes: 44 additions & 0 deletions packages/ui-checkbox/src/Checkbox/theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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 } from '@instructure/ui-themes'

/**
* 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): any => {
const { colors } = theme

const componentVariables: any = {
requiredInvalidColor: colors?.contrasts?.red5782
}

return {
...componentVariables
}
}

export default generateComponentTheme
1 change: 1 addition & 0 deletions packages/ui-form-field/src/FormFieldGroup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ class FormFieldGroup extends Component<FormFieldGroupProps> {
aria-disabled={props.disabled ? 'true' : undefined}
aria-invalid={this.invalid ? 'true' : undefined}
elementRef={this.handleRef}
isGroup
>
{this.renderFields()}
</FormFieldLayout>
Expand Down
10 changes: 8 additions & 2 deletions packages/ui-form-field/src/FormFieldLayout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,12 @@ class FormFieldLayout extends Component<FormFieldLayoutProps> {
// any cast is needed to prevent Expression produces a union type that is too complex to represent errors
const ElementType = this.elementType as any

const { makeStyles, styles, ...props } = this.props
const { makeStyles, styles, messages, isGroup, ...props } = this.props

const { width, layout, children } = props

const hasNewErrorMsg =
!!messages?.find((m) => m.type === 'newError') && isGroup
return (
<ElementType
{...omitProps(props, [
Expand All @@ -204,14 +207,17 @@ class FormFieldLayout extends Component<FormFieldLayoutProps> {
>
<Grid.Row>
{this.renderLabel()}
{hasNewErrorMsg && (
<Grid.Col>{this.renderVisibleMessages()}</Grid.Col>
)}
<Grid.Col
width={this.inlineContainerAndLabel ? 'auto' : undefined}
elementRef={this.handleInputContainerRef}
>
{children}
</Grid.Col>
</Grid.Row>
{this.renderVisibleMessages()}
{!hasNewErrorMsg && this.renderVisibleMessages()}
</Grid>
</ElementType>
)
Expand Down
Loading

0 comments on commit 9173b37

Please sign in to comment.