Skip to content

Commit

Permalink
LG-4135 | LG-4151: [CCP] PasswordInput updates (#2310)
Browse files Browse the repository at this point in the history
* [CCP] PasswordInput changes

* PasswordInput changeset

* Refactor

* Add types tests

* Add to README
  • Loading branch information
stephl3 authored Apr 25, 2024
1 parent 60cd5c4 commit d817f25
Show file tree
Hide file tree
Showing 21 changed files with 379 additions and 362 deletions.
18 changes: 18 additions & 0 deletions .changeset/neat-kiwis-know.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@leafygreen-ui/password-input': major
---

[LG-4135](https://jira.mongodb.org/browse/LG-4135)

1. Updated styling:
- updated spacing for `'small'` and `'large'` size variants
- added `'xsmall'` variant
- updated placeholder text color
- removed validation icon from inside the input

2. Added `errorMessage` and `successMessage` props
- `errorMessage` will render a default of `'This input needs your attention'` and can be customized
- `errorMessage` will only render when `state` is `warning` or `error`
- `successMessage` will render a default of `'Success'` and can be customized
- `successMessage` will only render when `state` is `valid`
- both will only render if `aria-describedby` is undefined and `stateNotifications` is not an array
62 changes: 44 additions & 18 deletions packages/password-input/README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/password-input/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"dependencies": {
"@leafygreen-ui/a11y": "^1.4.13",
"@leafygreen-ui/emotion": "^4.0.8",
"@leafygreen-ui/form-field": "^1.1.1",
"@leafygreen-ui/hooks": "^8.1.3",
"@leafygreen-ui/icon": "^12.0.1",
"@leafygreen-ui/icon-button": "^15.0.21",
Expand Down
61 changes: 0 additions & 61 deletions packages/password-input/src/InputIcon/InputIcon.styles.ts

This file was deleted.

44 changes: 0 additions & 44 deletions packages/password-input/src/InputIcon/InputIcon.tsx

This file was deleted.

9 changes: 0 additions & 9 deletions packages/password-input/src/InputIcon/InputIcon.types.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/password-input/src/InputIcon/index.ts

This file was deleted.

25 changes: 18 additions & 7 deletions packages/password-input/src/PasswordInput.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import {
} from '@lg-tools/storybook-utils';
import { StoryFn } from '@storybook/react';

import { spacing } from '@leafygreen-ui/tokens';
import { Error } from '@leafygreen-ui/typography';

import {
NotificationProps,
Size,
Expand Down Expand Up @@ -98,7 +101,6 @@ const meta: StoryMetaType<typeof PasswordInput> = {
generate: {
combineArgs: {
darkMode: [false, true],
value: [undefined, 'password'],
label: [undefined, 'Label'],
stateNotifications: [
undefined,
Expand Down Expand Up @@ -175,12 +177,21 @@ export const CustomContainer = ({
}: PasswordInputProps) => {
const ref = useRef<HTMLInputElement>(null);
return (
<PasswordInput
{...rest}
stateNotifications={stateNotifications as State}
aria-describedby={'my-id'}
ref={ref}
/>
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: `${spacing[100]}px`,
}}
>
<PasswordInput
{...rest}
stateNotifications={stateNotifications as State}
aria-describedby={'my-id'}
ref={ref}
/>
<Error id="my-id">This is a custom error</Error>
</div>
);
};
CustomContainer.argTypes = {
Expand Down
92 changes: 46 additions & 46 deletions packages/password-input/src/PasswordInput/PasswordInput.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { axe } from 'jest-axe';

import { LGIDS_PASSWORD_INPUT } from '../constants';

import { PasswordInput } from '.';

const defaultProps = {
Expand All @@ -14,6 +16,8 @@ const defaultProps = {
id: 'This is id',
onChange: jest.fn(),
onBlur: jest.fn(),
errorMessage: 'This is an error',
successMessage: 'This is a valid input',
notifications: [
{
notification: 'message one',
Expand All @@ -26,26 +30,22 @@ const defaultProps = {
],
};

const iconAriaLabels = {
warning: 'Warning Icon',
error: 'X Icon',
valid: 'Checkmark Icon',
};

function renderPasswordInput(props = {}) {
const utils = render(
// @ts-expect-error - data-testid gives an error but passes in types checks test below
<PasswordInput data-testid="password-input" {...props} />,
);
const passwordInput = utils.getByTestId('password-input');
const labelElement = utils.container.querySelector('label');
const messageContainer = utils.container.querySelector('ul');
const stateNotifications = utils.queryByTestId(
LGIDS_PASSWORD_INPUT.stateNotifications,
);
const toggleButton = utils.container.querySelector('button');
return {
...utils,
passwordInput,
labelElement,
messageContainer,
stateNotifications,
toggleButton,
};
}
Expand Down Expand Up @@ -194,31 +194,31 @@ describe('packages/password-input', () => {
});

describe('Password input with stateNotifications array', () => {
test('renders message container', async () => {
const { messageContainer } = renderPasswordInput({
test('renders state notifications', async () => {
const { stateNotifications } = renderPasswordInput({
label: defaultProps.label,
});
expect(messageContainer).toBeInTheDocument();
expect(stateNotifications).toBeInTheDocument();
});

test('renders message container with two messages', async () => {
const { messageContainer } = renderPasswordInput({
test('renders state notifications with two messages', async () => {
const { stateNotifications } = renderPasswordInput({
stateNotifications: defaultProps.notifications,
label: defaultProps.label,
});
const allMessages = messageContainer?.querySelectorAll('li');
const allMessages = stateNotifications?.querySelectorAll('li');
expect(allMessages).toHaveLength(2);
});
});

describe('Password input with aria-describedby', () => {
test('does not render message container if aria-describedby is passed', async () => {
const { messageContainer } = renderPasswordInput({
test('does not render state notifications if aria-describedby is passed', async () => {
const { stateNotifications } = renderPasswordInput({
[`aria-describedby`]: defaultProps.ariaDescribedby,
stateNotifications: 'error',
label: defaultProps.label,
});
expect(messageContainer).not.toBeInTheDocument();
expect(stateNotifications).not.toBeInTheDocument();
});

test('input aria-describedby attribute matches aria-describedby prop', async () => {
Expand All @@ -231,49 +231,34 @@ describe('packages/password-input', () => {
defaultProps.ariaDescribedby,
);
});
});

test('input shows error(x) icon', async () => {
const { container } = renderPasswordInput({
[`aria-describedby`]: defaultProps.ariaDescribedby,
describe('without stateNotifications array or aria-describedby', () => {
test(`it renders a general error message when stateNotifications is 'error'`, async () => {
const { queryByText } = renderPasswordInput({
stateNotifications: 'error',
label: defaultProps.label,
errorMessage: defaultProps.errorMessage,
});
expect(
container.querySelector(`svg[aria-label='${iconAriaLabels.error}']`),
).toBeInTheDocument();
expect(queryByText(defaultProps.errorMessage)).toBeInTheDocument();
});

test('input shows warning icon', async () => {
const { container } = renderPasswordInput({
[`aria-describedby`]: defaultProps.ariaDescribedby,
test(`it renders a general error message when stateNotifications is 'warning'`, async () => {
const { queryByText } = renderPasswordInput({
stateNotifications: 'warning',
label: defaultProps.label,
errorMessage: defaultProps.errorMessage,
});
expect(
container.querySelector(`svg[aria-label='${iconAriaLabels.warning}']`),
).toBeInTheDocument();
expect(queryByText(defaultProps.errorMessage)).toBeInTheDocument();
});

test('input shows checkmark icon', async () => {
const { container } = renderPasswordInput({
[`aria-describedby`]: defaultProps.ariaDescribedby,
test(`it renders a general success message when stateNotifications is 'valid'`, async () => {
const { queryByText } = renderPasswordInput({
stateNotifications: 'valid',
label: defaultProps.label,
successMessage: defaultProps.successMessage,
});
expect(
container.querySelector(`svg[aria-label='${iconAriaLabels.valid}']`),
).toBeInTheDocument();
});

test('input does not show checkmark icon', async () => {
const { container } = renderPasswordInput({
[`aria-describedby`]: defaultProps.ariaDescribedby,
stateNotifications: 'none',
label: defaultProps.label,
});
expect(
container.querySelector(`svg[aria-label='${iconAriaLabels.valid}']`),
).not.toBeInTheDocument();
expect(queryByText(defaultProps.successMessage)).toBeInTheDocument();
});
});

Expand Down Expand Up @@ -318,6 +303,21 @@ describe('packages/password-input', () => {
aria-labelledby="label"
stateNotifications={[{ notification: 'hi', state: 'error' }]}
/>
<PasswordInput
data-attribute="data test"
aria-labelledby="label"
stateNotifications="error"
/>
<PasswordInput
data-attribute="data test"
aria-labelledby="label"
stateNotifications="valid"
/>
<PasswordInput
data-attribute="data test"
aria-labelledby="label"
stateNotifications="warning"
/>
</>;
});
});
Loading

0 comments on commit d817f25

Please sign in to comment.