Skip to content

Commit

Permalink
fix(textfield): respect type=text|url|tel|email|password
Browse files Browse the repository at this point in the history
  • Loading branch information
hunterloftis committed Oct 19, 2021
1 parent d7ef1ce commit 1b7a59a
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ executors:
parameters:
current_golden_images_hash:
type: string
default: 93f0d3ee8e1623975786bc8fe6a2c74a975a5986
default: feeea6c9029b60bd61d021403eac2de2421ac203
commands:
downstream:
steps:
Expand Down
4 changes: 2 additions & 2 deletions packages/action-group/src/ActionGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ export class ActionGroup extends SpectrumElement {
const slot = this.shadowRoot.querySelector('slot');
if (!slot) return;
const assignedElements = slot.assignedElements({ flatten: true });
const buttons = assignedElements.reduce((acc: any[], el) => {
const buttons = assignedElements.reduce((acc: unknown[], el) => {
if (el.matches(this._buttonSelector)) {
acc.push(el);
} else {
Expand All @@ -378,7 +378,7 @@ export class ActionGroup extends SpectrumElement {
}
return acc;
}, []);
this.buttons = buttons;
this.buttons = buttons as ActionButton[];
this.manageChildren();
this.manageSelects();
};
Expand Down
24 changes: 24 additions & 0 deletions packages/textfield/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,27 @@ The quiet style works best when a clear layout (vertical stack, table, grid) ass
<sp-field-label for="name-3">Name (quietly)</sp-field-label>
<sp-textfield id="name-3" placeholder="Enter your name" quiet></sp-textfield>
```

### Types

When inputting URLs, telephone numbers, email addresses, or passwords, specify a `type` to provide
user affordances like mobile keyboards and obscured characters:

- `url`
- `tel`
- `email`
- `password`
- `text` (default)

```html
<sp-field-label for="tel-1">Telephone</sp-field-label>
<sp-textfield
id="tel-1"
type="tel"
placeholder="Enter your phone number"
></sp-textfield>
<sp-field-label for="password-1">Password</sp-field-label>
<sp-textfield id="password-1" type="password"></sp-textfield>
```

If the `type` attribute is not specified, or if it does not match any of these values, the default type adopted is "text."
20 changes: 19 additions & 1 deletion packages/textfield/src/Textfield.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
nothing,
ifDefined,
live,
internalProperty,
} from '@spectrum-web-components/base';

import { Focusable } from '@spectrum-web-components/shared/src/focusable.js';
Expand All @@ -29,6 +30,9 @@ import '@spectrum-web-components/icons-workflow/icons/sp-icon-alert.js';
import textfieldStyles from './textfield.css.js';
import checkmarkStyles from '@spectrum-web-components/icon/src/spectrum-icon-checkmark.css.js';

const textfieldTypes = ['text', 'url', 'tel', 'email', 'password'] as const;
export type TextfieldType = typeof textfieldTypes[number];

export class TextfieldBase extends Focusable {
public static get styles(): CSSResultArray {
return [textfieldStyles, checkmarkStyles];
Expand All @@ -52,6 +56,20 @@ export class TextfieldBase extends Focusable {
@property()
public placeholder = '';

@property({ attribute: 'type', reflect: true })
private _type: TextfieldType = 'text';

@internalProperty()
get type(): TextfieldType {
return textfieldTypes.find((t) => t === this._type) ?? 'text';
}

set type(val: TextfieldType) {
const prev = this._type;
this._type = val;
this.requestUpdate('type', prev);
}

@property()
public pattern?: string;

Expand Down Expand Up @@ -204,7 +222,7 @@ export class TextfieldBase extends Focusable {
return html`
<!-- @ts-ignore -->
<input
type="text"
type=${this.type}
aria-label=${this.label || this.placeholder}
aria-invalid=${ifDefined(this.invalid || undefined)}
class="input"
Expand Down
17 changes: 17 additions & 0 deletions packages/textfield/stories/textfield.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,20 @@ export const readonly = (): TemplateResult => html`
placeholder="Enter your life story"
></sp-textfield>
`;

export const types = (): TemplateResult => html`
<sp-textfield label="Default" placeholder="default (text)"></sp-textfield>
<sp-textfield label="Text" type="text" placeholder="text"></sp-textfield>
<sp-textfield label="URL" type="url" placeholder="url"></sp-textfield>
<sp-textfield label="Tel" type="tel" placeholder="tel"></sp-textfield>
<sp-textfield
label="E-Mail"
type="email"
placeholder="email"
></sp-textfield>
<sp-textfield
label="Password"
type="password"
placeholder="password"
></sp-textfield>
`;
95 changes: 78 additions & 17 deletions packages/textfield/test/textfield.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/
import '../sp-textfield.js';
import { Textfield } from '../';
import { Textfield, TextfieldType } from '../';
import { litFixture, html, elementUpdated, expect } from '@open-wc/testing';
import { sendKeys, executeServerCommand } from '@web/test-runner-commands';

Expand Down Expand Up @@ -119,20 +119,14 @@ describe('Textfield', () => {
steps: [
{
type: 'move',
position: [
startBounds.right - 2,
startBounds.bottom - 2,
],
position: [startBounds.right - 2, startBounds.bottom - 2],
},
{
type: 'down',
},
{
type: 'move',
position: [
startBounds.right + 50,
startBounds.bottom + 50,
],
position: [startBounds.right + 50, startBounds.bottom + 50],
},
{
type: 'up',
Expand Down Expand Up @@ -161,20 +155,14 @@ describe('Textfield', () => {
steps: [
{
type: 'move',
position: [
startBounds.right - 2,
startBounds.bottom - 2,
],
position: [startBounds.right - 2, startBounds.bottom - 2],
},
{
type: 'down',
},
{
type: 'move',
position: [
startBounds.right + 50,
startBounds.bottom + 50,
],
position: [startBounds.right + 50, startBounds.bottom + 50],
},
{
type: 'up',
Expand Down Expand Up @@ -741,4 +729,77 @@ describe('Textfield', () => {
expect(el.value).to.equal('asdff');
expect(inputElement.selectionStart).to.equal(3);
});
describe('type attribute', () => {
// references:
// https://developer.mozilla.org/en-US/docs/Glossary/IDL#content_versus_idl_attributes
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#keywords-and-enumerated-attributes

it('assigns valid attributes to the property', async () => {
const types: TextfieldType[] = [
'text',
'url',
'tel',
'email',
'password',
];
for await (const t of types) {
const el = await litFixture<Textfield>(
html`
<sp-textfield type=${t}></sp-textfield>
`
);
expect(el.type).equals(t);

el.setAttribute('type', 'url');
expect(el.type).equals('url');
}
});
it('represents invalid and missing attributes as "text"', async () => {
const el1 = await litFixture<Textfield>(
html`
<sp-textfield></sp-textfield>
`
);

const el2 = await litFixture<Textfield>(
html`
<sp-textfield type="time"></sp-textfield>
`
);
expect(el1.type).equals('text');
expect(el2.type).equals('text');

el1.setAttribute('type', 'submit');
expect(el1.type).equals('text');
});
it('reflects valid property assignments', async () => {
const el = await litFixture<Textfield>(
html`
<sp-textfield type="url"></sp-textfield>
`
);

el.type = 'email';
await elementUpdated(el);

expect(el.getAttribute('type')).equals('email');
expect(el.type).equals('email');
});
it('reflects invalid assignments but sets state to "text"', async () => {
const el = await litFixture<Textfield>(
html`
<sp-textfield type="url"></sp-textfield>
`
);

// eslint-disable-next-line
// @ts-ignore
el.type = 'range';
await elementUpdated(el);

expect(el.getAttribute('type')).equals('range');
expect(el.type).equals('text');
});
});
});

0 comments on commit 1b7a59a

Please sign in to comment.