Skip to content

Commit

Permalink
Added tests for the TextareaField component and added character limit… (
Browse files Browse the repository at this point in the history
#14740)

Resolves #13755 

### Description
Increases the allowed characters for the Hearing notes field to 1000 and adds a character counter UI component to assist with tracking how many characters are consumed

### Acceptance Criteria
- [ ] Code compiles correctly
- [ ] User unable to add characters beyond the limit
- [ ] Hearing notes with values that contain 1000 or less characters should save successfully

### Testing Plan

**General**
1. You will need to reset your environment in order to get a fresh copy of FACOLS
1. Once you have a new copy of your environment, shadow a hearing admin user (BVASYELLOW)
1. Navigate to the hearing schedule and select an available hearing: http://localhost:3000/hearings/schedule
1. Click the 'Edit Hearing Details' link
1. Start typing in some notes and ensure that you see the character counter appear
1. Keep typing until the counter reaches 0 and ensure you cannot enter anymore characters

**Unique character**
1. Repeat the steps above to navigate to a different hearing
1. Search google for an emoji to copy to your clipboard
1. Paste the emoji into the notes field and ensure the counter decreases by 2

### User Facing Changes
 - [x] Screenshots of UI changes added to PR & Original Issue

 BEFORE|AFTER
 ---|---
![BeforeNotesField](https://user-images.githubusercontent.com/61989526/88050957-d2422480-cb25-11ea-850a-bc0c7c108810.png)|![AfterNotesField](https://user-images.githubusercontent.com/61989526/88050739-68c21600-cb25-11ea-92b1-1f97627a738f.png)

### Storybook Story
*For Frontend (Presentationa) Components*
* [x] Add a [Storybook](https://github.com/department-of-veterans-affairs/caseflow/wiki/Documenting-React-Components-with-Storybook) file alongside the component file (e.g. create `MyComponent.stories.js` alongside `MyComponent.jsx`)
* [x] Give it a title that reflects the component's location within the overall Caseflow hierarchy 
* [x] Write a separate story (within the same file) for each discrete variation of the component
  • Loading branch information
sahalliburton committed Jul 22, 2020
1 parent 2daa54b commit 8238b6e
Show file tree
Hide file tree
Showing 4 changed files with 698 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ export const NotesField = ({ hearing, update, readOnly }) => {

return (
<TextareaField
maxlength={1000}
label="Notes"
name={`${hearing.externalId}-notes`}
strongLabel
Expand Down
1 change: 1 addition & 0 deletions client/app/hearings/components/details/DetailsForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ const DetailsForm = (props) => {
disabled={readOnly}
value={hearing?.notes || ''}
onChange={(notes) => update('hearing', { notes })}
maxlength={1000}
/>
</div>
</ContentSection>
Expand Down
124 changes: 124 additions & 0 deletions client/test/app/components/TextareaField.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import React from 'react';
import { mount } from 'enzyme';
import TextareaField from 'app/components/TextareaField';
import { FormLabel } from 'app/components/FormLabel';

// Setup the constants
const limit = 10;
const emoji = '😀';
const testValue = 'hello';
const changeSpy = jest.fn();
const name = 'Test Field';
const error = 'Something went wrong';

describe('TextareaField', () => {
test('Matches snapshot with default props', () => {
// Run the test
const textField = mount(<TextareaField onChange={changeSpy} name={name} />);

// Assertions
expect(textField.find('textarea')).toHaveLength(1);
expect(textField.find('label').prop('className')).toEqual('question-label');
expect(textField.find(FormLabel).prop('name')).toEqual(name);
expect(textField.prop('disabled')).toEqual(false);
expect(textField.prop('optional')).toEqual(false);
expect(textField.prop('required')).toEqual(false);
expect(textField).toMatchSnapshot();
});

test('Can accept input', () => {
// Setup the test
const textField = mount(<TextareaField onChange={changeSpy} name={name} />);

// Run the test
textField.find('textarea').simulate('change', { target: { value: testValue } });

// Assertions
expect(changeSpy).toHaveBeenCalledWith(testValue);
expect(textField).toMatchSnapshot();
});

test('Respects disabled prop on the textarea field', () => {
// Setup the test
const textField = mount(<TextareaField disabled onChange={changeSpy} name={name} />);

// Assertions
expect(textField.find('textarea').prop('disabled')).toEqual(true);
expect(textField).toMatchSnapshot();
});

test('Respects optional prop on the textarea field', () => {
// Setup the test
const textField = mount(<TextareaField optional onChange={changeSpy} name={name} />);

// Assertions
expect(textField.find(FormLabel).prop('optional')).toEqual(true);
expect(textField.find('.cf-optional').text()).toEqual('Optional');
expect(textField).toMatchSnapshot();
});

test('Respects required prop on the textarea field', () => {
// Setup the test
const textField = mount(<TextareaField required onChange={changeSpy} name={name} />);

// Assertions
expect(textField.find(FormLabel).prop('required')).toEqual(true);
expect(textField.find('.cf-required').text()).toEqual('Required');
expect(textField).toMatchSnapshot();
});

test('Displays screen-reader only label when hideLabel prop is true', () => {
// Setup the test
const textField = mount(<TextareaField hideLabel onChange={changeSpy} name={name} />);

// Assertions
expect(textField.find('label').prop('className')).toEqual('sr-only question-label');
expect(textField).toMatchSnapshot();
});

test('Displays a bold label when strongLabel prop is true', () => {
// Setup the test
const textField = mount(<TextareaField strongLabel onChange={changeSpy} name={name} />);

// Assertions
expect(textField.find('strong').text()).toEqual(name);
expect(textField).toMatchSnapshot();
});

test('Displays error message when present', () => {
// Setup the test
const textField = mount(<TextareaField errorMessage={error} onChange={changeSpy} name={name} />);

// Assertions
expect(textField.find('.usa-input-error-message').text()).toEqual(error);
expect(textField).toMatchSnapshot();
});

test('Displays character count when maxlength and value are present', () => {
// Setup the test
const textField = mount(<TextareaField maxlength={limit} value={testValue} onChange={changeSpy} name={name} />);

// Assertions
expect(textField.find('i').text()).toEqual(`${limit - testValue.length} characters left`);
expect(textField).toMatchSnapshot();
});

test('Emojis consume 2 characters', () => {
// Setup the test
const textField = mount(<TextareaField maxlength={2} value={emoji} onChange={changeSpy} name={name} />);

// Assertions
expect(textField.find('i').text()).toEqual('0 characters left');
expect(textField).toMatchSnapshot();

});

test('Does not display character count when maxlength is present, but value is not present', () => {
// Setup the test
const textField = mount(<TextareaField maxlength={limit} onChange={changeSpy} name={name} />);

// Assertions
expect(textField.find('i')).toHaveLength(0);
expect(textField).toMatchSnapshot();
});
});
Loading

0 comments on commit 8238b6e

Please sign in to comment.