-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: [ASL-4632] Word count component (#352)
- Loading branch information
1 parent
2ff068c
commit 1f81475
Showing
9 changed files
with
131 additions
and
4 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React, { useState } from 'react'; | ||
import { TextArea } from '@ukhomeoffice/react-components'; | ||
import classNames from 'classnames'; | ||
import WordCountHintMessage from './wordcount-hint-message'; | ||
import omit from 'lodash/omit'; | ||
|
||
export default function TextAreaWithWordCount(props) { | ||
|
||
const { value, maxWordCount, error, values, name } = props; | ||
|
||
const [content, setContent] = useState(value || ''); | ||
|
||
const formErrorClass = classNames({ | ||
'govuk-form-group': true, | ||
'govuk-character-count': true, | ||
'govuk-form-group--error': error | ||
}); | ||
|
||
return ( | ||
<div className={formErrorClass} id={`${name}-form-group`}> | ||
<TextArea | ||
{...omit(props, 'maxWordCount')} | ||
value={content} | ||
onChange={e => setContent(e.target.value)} | ||
/> | ||
<WordCountHintMessage content={content} id={values.id} maxWordCount={maxWordCount} /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Added from govuk-frotnend | ||
|
||
.govuk-character-count { | ||
@include govuk-responsive-margin(6, "bottom"); | ||
|
||
.govuk-form-group, | ||
.govuk-textarea { | ||
margin-bottom: govuk-spacing(1); | ||
} | ||
} | ||
|
||
.govuk-character-count__message { | ||
margin-top: 0; | ||
margin-bottom: 0; | ||
|
||
&::after { | ||
// Zero-width space that will reserve vertical space when no hint is | ||
// provided as: | ||
// - setting a min-height is not possible without a magic number because | ||
// the line-height is set by the `govuk-font` call above | ||
// - using `:empty` is not possible as the hint macro outputs line breaks | ||
content: "\200B"; | ||
} | ||
} | ||
|
||
.govuk-character-count__message--disabled { | ||
visibility: hidden; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import React from 'react'; | ||
import { shallow } from 'enzyme'; | ||
import WordCountHintMessage from './wordcount-hint-message'; | ||
import { describe, test, expect } from '@jest/globals'; | ||
|
||
describe('<WordCountHintMessage />', () => { | ||
const id = 'applicantTrainingUseAtWork'; | ||
const wordCountHintId = '#applicantTrainingUseAtWork-wordcount-hint'; | ||
|
||
test('displays max words remaining when wordCount is not provided', () => { | ||
const wrapper = shallow(<WordCountHintMessage content='' maxWordCount={10} id={id} />); | ||
expect(wrapper.find(wordCountHintId).text()).toContain('You have 10 words remaining'); | ||
}); | ||
|
||
test('displays remaining words when wordCount is less than maxWordCount', () => { | ||
const wrapper = shallow(<WordCountHintMessage content='This is a sentence with 7 words' maxWordCount={10} id={id} />); | ||
expect(wrapper.find(wordCountHintId).text()).toContain('You have 3 words remaining'); | ||
}); | ||
|
||
test('displays no remaining words when wordCount is equal to maxWordCount', () => { | ||
const wrapper = shallow(<WordCountHintMessage content='This is a sentence with 10 words - 2 more' maxWordCount={10} id={id} />); | ||
expect(wrapper.find(wordCountHintId).text()).toContain('You have 0 words remaining'); | ||
}); | ||
|
||
test('displays too many words when wordCount is greater than maxWordCount', () => { | ||
const wrapper = shallow(<WordCountHintMessage content='This is a sentence with 12 words - 2 more plus 2' maxWordCount={10} id={id} />); | ||
expect(wrapper.find(wordCountHintId).text()).toContain('You have 2 words too many'); | ||
}); | ||
|
||
test('displays singular word when there is only one word remaining', () => { | ||
const wrapper = shallow(<WordCountHintMessage content='This is a sentence with 9 words i think' maxWordCount={10} id={id} />); | ||
expect(wrapper.find(wordCountHintId).text()).toContain('You have 1 word remaining'); | ||
}); | ||
|
||
test('displays singular word when there is only one word too many', () => { | ||
const wrapper = shallow(<WordCountHintMessage content='This is a sentence with 11 words - 3 more words' maxWordCount={10} id={id} />); | ||
expect(wrapper.find(wordCountHintId).text()).toContain('You have 1 word too many'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import React from 'react'; | ||
|
||
const WordCountHintMessage = ({ content, id, maxWordCount = 0 }) => { | ||
|
||
const wordCount = content?.split(/\s+/).filter(Boolean).length ?? 0; | ||
const hintId = `${id}-wordcount-hint`; | ||
|
||
let hintText = ''; | ||
|
||
const wordCountText = count => count === 1 ? 'word' : 'words'; | ||
|
||
if (wordCount > maxWordCount) { | ||
const count = wordCount - maxWordCount; | ||
hintText = `You have ${count} ${wordCountText(count)} too many`; | ||
} else { | ||
const count = maxWordCount - wordCount; | ||
hintText = `You have ${count} ${wordCountText(count)} remaining`; | ||
} | ||
|
||
return ( | ||
<div id={hintId} aria-live="polite" className="govuk-hint govuk-character-count__message"> | ||
{hintText} | ||
</div> | ||
); | ||
}; | ||
|
||
export default WordCountHintMessage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters