Files
web/packages/design-system/lib/components/PasswordInput/NewPasswordValidation.tsx
Rasmus Langvad ffef566316 Merged in feat/new-passwordinput-component (pull request #3376)
feat(SW-3672): Update PasswordInput component

* Update PasswordInput component

* Removed some tests not working as expected

* Remove IconButton from PasswordInput

* Remove IconButton from Input

* Merge branch 'master' into feat/new-passwordinput-component


Approved-by: Linus Flood
2026-01-07 09:10:22 +00:00

127 lines
3.1 KiB
TypeScript

import { useIntl } from 'react-intl'
import { passwordValidators } from '@scandic-hotels/common/utils/zod/passwordValidator'
import type { PasswordValidatorKey } from '@scandic-hotels/common/utils/zod/newPassword'
import { MaterialIcon } from '../Icons/MaterialIcon'
import { Typography } from '../Typography'
import styles from './passwordInput.module.css'
export function NewPasswordValidation({
value,
errors,
id,
}: {
value: string
errors: string[]
id: string
}) {
const intl = useIntl()
if (!value) return null
function getErrorMessage(key: PasswordValidatorKey) {
switch (key) {
case 'length':
return intl.formatMessage(
{
id: 'passwordInput.lengthRequirement',
defaultMessage: '{min} to {max} characters',
},
{
min: 10,
max: 40,
}
)
case 'hasUppercase':
return intl.formatMessage(
{
id: 'passwordInput.uppercaseRequirement',
defaultMessage: '{count} uppercase letter',
},
{ count: 1 }
)
case 'hasLowercase':
return intl.formatMessage(
{
id: 'passwordInput.lowercaseRequirement',
defaultMessage: '{count} lowercase letter',
},
{ count: 1 }
)
case 'hasNumber':
return intl.formatMessage(
{
id: 'passwordInput.numberRequirement',
defaultMessage: '{count} number',
},
{ count: 1 }
)
case 'hasSpecialChar':
return intl.formatMessage(
{
id: 'passwordInput.specialCharacterRequirement',
defaultMessage: '{count} special character',
},
{ count: 1 }
)
case 'allowedCharacters':
return intl.formatMessage({
id: 'passwordInput.allowedCharactersRequirement',
defaultMessage: 'Only allowed characters',
})
}
}
return (
<div
className={styles.errors}
role="status"
aria-live="polite"
aria-atomic="false"
id={id}
>
{Object.entries(passwordValidators).map(([key, { message }]) => (
<Typography variant="Label/xsRegular" key={key}>
<span className={styles.helpText}>
<Icon errorMessage={message} errors={errors} />
{getErrorMessage(key as PasswordValidatorKey)}
</span>
</Typography>
))}
</div>
)
}
interface IconProps {
errorMessage: string
errors: string[]
}
function Icon({ errorMessage, errors }: IconProps) {
const intl = useIntl()
return errors.includes(errorMessage) ? (
<MaterialIcon
icon="close"
color="Icon/Feedback/Error"
size={20}
role="img"
aria-label={intl.formatMessage({
id: 'common.error',
defaultMessage: 'Error',
})}
/>
) : (
<MaterialIcon
icon="check"
color="Icon/Feedback/Success"
size={20}
role="img"
aria-label={intl.formatMessage({
id: 'common.success',
defaultMessage: 'Success',
})}
/>
)
}