Skip to content

Commit

Permalink
feat(pie-textarea): DSW-2139 label and character count (#1666)
Browse files Browse the repository at this point in the history
* feat(pie-textarea): DSW-2132 add textarea form functionality

* docs(pie-textarea): DSW-2132 update README.md file

* fix(pie-textarea): DSW-2132 remove defaultValue prop and update story

* feat(pie-textarea): DSW-2132 add component tests

* fix(pie-textarea): DSW-2132 change textarea width and lint fix

* add label and char count to template

* restrict length in lifecycle hooks

* fix(pie-textarea): DSW-2132 add handleResize method to handleInput

* feat(pie-textarea): DSW-2132 prevent form submission

* test(pie-textarea): DSW-2132 add test to prevent form submission on Enter

* tests

* tests

* tests

* test diagnostic log

* try to fix test

* tmp remove pasting test

* remove unused param in test

* rtl visual test

* remove unnecessary super call from firstupdated

* ensure textarea value is trimmed to avoid height bugs on resize

* visual test rtl

---------

Co-authored-by: leksaBoiko <[email protected]>
Co-authored-by: leksaBoiko <[email protected]>
Co-authored-by: Ben Siggery <[email protected]>
  • Loading branch information
4 people authored Aug 6, 2024
1 parent d4be4fc commit 576945a
Show file tree
Hide file tree
Showing 11 changed files with 301 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/cuddly-jobs-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"pie-storybook": minor
---

[Added] - Character count and label to pie-textarea
5 changes: 5 additions & 0 deletions .changeset/rotten-pens-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@justeattakeaway/pie-form-label": minor
---

[Added] - data test id to leading and trailing label content
5 changes: 5 additions & 0 deletions .changeset/seven-fishes-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@justeattakeaway/pie-textarea": minor
---

[Added] - Character count and label
18 changes: 18 additions & 0 deletions apps/pie-storybook/stories/pie-textarea.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const Template = ({
name,
autocomplete,
autoFocus,
label,
maxLength,
}: TextareaProps) => {
const [, updateArgs] = UseArgs();

Expand Down Expand Up @@ -60,6 +62,8 @@ const Template = ({
?autoFocus="${autoFocus}"
?readonly="${readonly}"
?required="${required}"
maxLength="${ifDefined(maxLength)}"
label="${ifDefined(label)}"
@input="${onInput}"
@change="${onChange}">
</pie-textarea>
Expand Down Expand Up @@ -135,6 +139,20 @@ const textareaStoryMeta: TextareaStoryMeta = {
summary: 'off',
},
},
label: {
description: 'The label for the textarea field.',
control: 'text',
defaultValue: {
summary: defaultProps.label,
},
},
maxLength: {
description: 'The maximum number of characters allowed in the textarea field.',
control: 'number',
defaultValue: {
summary: 0,
},
},
},
args: defaultArgs,
parameters: {
Expand Down
4 changes: 2 additions & 2 deletions packages/components/pie-form-label/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ export class PieFormLabel extends RtlMixin(LitElement) implements FormLabelProps
for=${ifDefined(this.for)}>
<div>
${isRTL ? this._renderOptionalLabel() : nothing}
<span class="c-formLabel-leading"><slot></slot></span>
<span class="c-formLabel-leading" data-test-id="pie-form-label-leading"><slot></slot></span>
${!isRTL ? this._renderOptionalLabel() : nothing}
</div>
${trailing ? html`<span class="c-formLabel-trailing">${trailing}</span>` : nothing}
${trailing ? html`<span class="c-formLabel-trailing" data-test-id="pie-form-label-trailing">${trailing}</span>` : nothing}
</label>`;
}

Expand Down
1 change: 1 addition & 0 deletions packages/components/pie-textarea/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"cem-plugin-module-file-extensions": "0.0.5"
},
"dependencies": {
"@justeattakeaway/pie-form-label": "0.13.6",
"@justeattakeaway/pie-webc-core": "0.24.0",
"lodash.throttle": "4.1.1"
},
Expand Down
14 changes: 13 additions & 1 deletion packages/components/pie-textarea/src/defs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,23 @@ export interface TextareaProps {
* If true, the textarea is required to have a value before submitting the form. If there is no value, then the component validity state will be invalid.
*/
required?: boolean;

/**
* The label text for the textarea field.
*/
label?: string;

/**
* The maximum number of characters allowed in the textarea field.
* If the `label` property is not set, this property will have no effect.
*/
maxLength?: number;
}

/**
* The default values for the `TextareaProps` that are required (i.e. they have a fallback value in the component).
*/
type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name'| 'autocomplete'>>;
type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength'>>;

/**
* Default values for optional properties that have default fallback values in the component.
Expand All @@ -68,6 +79,7 @@ export const defaultProps: DefaultProps = {
disabled: false,
size: 'medium',
resize: 'auto',
label: '',
value: '',
autoFocus: false,
readonly: false,
Expand Down
46 changes: 40 additions & 6 deletions packages/components/pie-textarea/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import {
LitElement, html, unsafeCSS, PropertyValues,
LitElement, html, unsafeCSS, PropertyValues, nothing,
} from 'lit';

import { property, query } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import throttle from 'lodash.throttle';

import {
validPropertyValues, RtlMixin, defineCustomElement, FormControlMixin, wrapNativeEvent,
} from '@justeattakeaway/pie-webc-core';

import { ifDefined } from 'lit/directives/if-defined.js';
import styles from './textarea.scss?inline';
import {
TextareaProps, defaultProps, sizes, resizeModes,
} from './defs';

import '@justeattakeaway/pie-form-label';

// Valid values available to consumers
export * from './defs';

Expand Down Expand Up @@ -42,6 +45,12 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
@validPropertyValues(componentSelector, resizeModes, defaultProps.resize)
public resize = defaultProps.resize;

@property({ type: String })
public label = defaultProps.label;

@property({ type: Number })
public maxLength: TextareaProps['maxLength'];

@property({ type: Boolean })
public readonly = defaultProps.readonly;

Expand Down Expand Up @@ -96,6 +105,7 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
}

protected firstUpdated (): void {
this.restrictInputLength();
this._internals.setFormValue(this.value);

this._textarea.addEventListener('keydown', this.handleKeyDown);
Expand All @@ -105,14 +115,25 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
this._throttledResize();
}

protected updated (changedProperties: PropertyValues<this>) {
if (this.resize === 'auto' && (changedProperties.has('resize') || changedProperties.has('size'))) {
this.handleResize();
private restrictInputLength () {
if (this.label.length && this.maxLength && this.value.length > this.maxLength) {
const trimmedValue = this.value.slice(0, this.maxLength);
// Ensures that the internal text area is correctly trimmed and synced with our value.
// The live() directive does not solve this for us.
this._textarea.value = trimmedValue;
this.value = trimmedValue;
}
}

protected updated (changedProperties: PropertyValues<this>) {
if (changedProperties.has('value')) {
this.restrictInputLength();
this._internals.setFormValue(this.value);
}

if (this.resize === 'auto' && (changedProperties.has('resize') || changedProperties.has('size'))) {
this.handleResize();
}
}

/**
Expand All @@ -121,6 +142,7 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
*/
private handleInput = (event: InputEvent) => {
this.value = (event.target as HTMLTextAreaElement).value;
this.restrictInputLength();
this._internals.setFormValue(this.value);

this.handleResize();
Expand All @@ -146,6 +168,14 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
this._textarea.removeEventListener('keydown', this.handleKeyDown);
}

renderLabel (label: string, maxLength?: number) {
const characterCount = maxLength ? `${this.value.length}/${maxLength}` : undefined;

return label?.length
? html`<pie-form-label for="${componentSelector}" trailing=${ifDefined(characterCount)}>${label}</pie-form-label>`
: nothing;
}

render () {
const {
disabled,
Expand All @@ -157,6 +187,8 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
readonly,
value,
required,
label,
maxLength,
} = this;

return html`
Expand All @@ -165,7 +197,10 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
data-test-id="pie-textarea-wrapper"
data-pie-size="${size}"
data-pie-resize="${resize}">
${this.renderLabel(label, maxLength)}
<textarea
id="${componentSelector}"
data-test-id="${componentSelector}"
name=${ifDefined(name)}
autocomplete=${ifDefined(autocomplete)}
.value=${live(value)}
Expand All @@ -175,7 +210,6 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
?disabled=${disabled}
@input=${this.handleInput}
@change=${this.handleChange}
data-test-id="pie-textarea"
></textarea>
</div>`;
}
Expand Down
Loading

0 comments on commit 576945a

Please sign in to comment.