Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(textfield): add rows attribute #3356

Merged
merged 9 commits into from
Jun 25, 2023
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ executors:
parameters:
current_golden_images_hash:
type: string
default: f019af2cf9d7e2692987d7e7200cb32b17a46cc1
default: d55f3744c025ef7ff20183e23afa141e98130a4e
wireit_cache_name:
type: string
default: wireit
Expand Down
6 changes: 5 additions & 1 deletion packages/textfield/src/Textfield.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ export class TextfieldBase extends ManageHelpText(Focusable) {
@property({ type: Boolean, reflect: true })
public readonly = false;

@property({ type: Number })
public rows = -1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense to normalize across the file, thanks for calling that out!


@property({ type: Boolean, reflect: true })
public valid = false;

Expand Down Expand Up @@ -217,7 +220,7 @@ export class TextfieldBase extends ManageHelpText(Focusable) {

private get renderMultiline(): TemplateResult {
return html`
${this.grows && !this.quiet
${this.grows && !this.quiet && this.rows === -1
? html`
<div id="sizer">${this.value}&#8203;</div>
`
Expand All @@ -244,6 +247,7 @@ export class TextfieldBase extends ManageHelpText(Focusable) {
?disabled=${this.disabled}
?required=${this.required}
?readonly=${this.readonly}
rows=${ifDefined(this.rows > -1 ? this.rows : undefined)}
autocomplete=${ifDefined(this.autocomplete)}
></textarea>
`;
Expand Down
9 changes: 9 additions & 0 deletions packages/textfield/src/textfield.css
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,15 @@ textarea {
box-shadow: none;
}

:host([multiline][rows]) .input {
block-size: auto;
resize: none;
}

:host([multiline][rows='1']) .input {
min-block-size: auto;
}

/* restore specificity from the original Spectrum CSS */

:host([disabled][quiet]) #textfield .input,
Expand Down
26 changes: 26 additions & 0 deletions packages/textfield/stories/textarea.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,29 @@ export const sized = (): TemplateResult => html`
</sp-help-text>
</sp-textfield>
`;

export const with5Rows = (): TemplateResult => html`
<sp-field-label for="predefinedRows">
Enter your life story with very long words...
</sp-field-label>
<sp-textfield
multiline
id="predefinedRows"
value="Line 1\nLine 2\nLine 3\nLine 4\nLine 5"
placeholder="Enter your life story"
rows="5"
></sp-textfield>
`;

export const with1Row = (): TemplateResult => html`
<sp-field-label for="predefinedRows">
Enter your life story with very long words...
</sp-field-label>
<sp-textfield
multiline
id="predefinedRows"
value="Line 1"
placeholder="Enter your life story"
rows="1"
></sp-textfield>
`;
108 changes: 108 additions & 0 deletions packages/textfield/test/textfield.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,97 @@ describe('Textfield', () => {
: null;
expect(input).to.not.be.null;
});
it('multiline with rows', async () => {
const el = await litFixture<Textfield>(
html`
<sp-textfield
placeholder="Enter your name"
multiline
rows="5"
></sp-textfield>
`
);
expect(el).to.not.equal(undefined);
const input = el.shadowRoot
? el.shadowRoot.querySelector('textarea')
: null;
expect(input).to.not.be.null;
expect(input?.getAttribute('rows')).to.equal('5');
});
it('multiline with 1 row has smaller height than multiline without explicit rows', async () => {
const oneRowEl = await litFixture<Textfield>(
html`
<sp-textfield
placeholder="Enter your name"
multiline
rows="1"
></sp-textfield>
`
);
expect(oneRowEl).to.not.equal(undefined);
const oneRowTextarea = oneRowEl.shadowRoot
? oneRowEl.shadowRoot.querySelector('textarea')
: null;
expect(oneRowTextarea).to.not.be.null;
expect(oneRowTextarea?.getAttribute('rows')).to.equal('1');

const defaultEL = await litFixture<Textfield>(
html`
<sp-textfield
placeholder="Enter your name"
multiline
></sp-textfield>
`
);
expect(defaultEL).to.not.equal(undefined);
const defaultTextarea = oneRowEl.shadowRoot
? defaultEL.shadowRoot.querySelector('textarea')
: null;
expect(defaultTextarea).to.not.be.null;
expect(defaultTextarea?.getAttribute('rows')).to.be.null;

const boundsDefaultElement = defaultTextarea?.getBoundingClientRect();
const boundsOneRowElement = oneRowTextarea?.getBoundingClientRect();
expect(boundsDefaultElement?.height).to.be.greaterThan(boundsOneRowElement?.height ?? 0);
});
it('multiline with rows does not resize', async () => {
const el = await litFixture<Textfield>(
html`
<sp-textfield
multiline
rows="5"
label="No resize control"
placeholder="No resize control"
></sp-textfield>
`
);
// Resizing only effects the block size of the host rect, so measure the `focusElement` when resizing.
const sizedElement = (el as HTMLElement & { focusElement: HTMLElement })
.focusElement;
const startBounds = sizedElement.getBoundingClientRect();
await sendMouse({
steps: [
{
type: 'move',
position: [startBounds.right - 6, startBounds.bottom - 6],
},
{
type: 'down',
},
{
type: 'move',
position: [startBounds.right + 50, startBounds.bottom + 50],
},
{
type: 'up',
},
],
});

const endBounds = sizedElement.getBoundingClientRect();
expect(endBounds.height).equals(startBounds.height);
expect(endBounds.width).equals(startBounds.width);
});
it('resizes by default', async () => {
const el = await litFixture<Textfield>(
html`
Expand Down Expand Up @@ -206,6 +297,23 @@ describe('Textfield', () => {
: null;
expect(sizer).to.not.be.null;
});
it('multiline with rows and grows does not grow', async () => {
const el = await litFixture<Textfield>(
html`
<sp-textfield
placeholder="Enter your name"
multiline
grows
rows="5"
></sp-textfield>
`
);
expect(el).to.not.equal(undefined);
const sizer = el.shadowRoot
? el.shadowRoot.querySelector('#sizer')
: null;
expect(sizer).to.be.null;
});
it('valid', async () => {
const el = await litFixture<Textfield>(
html`
Expand Down