diff --git a/packages/bundle/elements.ts b/packages/bundle/elements.ts index 43d81f6ad4..01f1d56a74 100644 --- a/packages/bundle/elements.ts +++ b/packages/bundle/elements.ts @@ -31,6 +31,7 @@ import '@spectrum-web-components/dialog/sp-dialog-wrapper.js'; import '@spectrum-web-components/dropdown/sp-dropdown.js'; import '@spectrum-web-components/dropzone/sp-dropzone.js'; import '@spectrum-web-components/field-group/sp-field-group.js'; +import '@spectrum-web-components/field-label/sp-field-label.js'; import '@spectrum-web-components/icon/sp-icon.js'; import '@spectrum-web-components/icons/sp-icons-large.js'; import '@spectrum-web-components/icons/sp-icons-medium.js'; diff --git a/packages/bundle/package.json b/packages/bundle/package.json index 11ab73046d..2f27cc9499 100644 --- a/packages/bundle/package.json +++ b/packages/bundle/package.json @@ -67,6 +67,7 @@ "@spectrum-web-components/dropdown": "^0.8.5", "@spectrum-web-components/dropzone": "^0.4.4", "@spectrum-web-components/field-group": "^0.0.1", + "@spectrum-web-components/field-label": "^0.0.1", "@spectrum-web-components/icon": "^0.6.3", "@spectrum-web-components/icons": "^0.4.5", "@spectrum-web-components/icons-workflow": "^0.3.6", diff --git a/packages/bundle/src/index.ts b/packages/bundle/src/index.ts index 3d402d6fd0..5a88369237 100644 --- a/packages/bundle/src/index.ts +++ b/packages/bundle/src/index.ts @@ -27,6 +27,7 @@ export * from '@spectrum-web-components/dialog'; export * from '@spectrum-web-components/dropdown'; export * from '@spectrum-web-components/dropzone'; export * from '@spectrum-web-components/field-group'; +export * from '@spectrum-web-components/field-label'; export * from '@spectrum-web-components/icon'; export * from '@spectrum-web-components/icons'; import * as UIIcons from '@spectrum-web-components/icons-ui'; diff --git a/packages/bundle/tsconfig.json b/packages/bundle/tsconfig.json index 7688866901..d9c3e39aeb 100644 --- a/packages/bundle/tsconfig.json +++ b/packages/bundle/tsconfig.json @@ -25,6 +25,7 @@ { "path": "../dropdown" }, { "path": "../dropzone" }, { "path": "../field-group" }, + { "path": "../field-label" }, { "path": "../icon" }, { "path": "../icons" }, { "path": "../icons-ui" }, diff --git a/packages/field-label/README.md b/packages/field-label/README.md new file mode 100644 index 0000000000..4b2786bc11 --- /dev/null +++ b/packages/field-label/README.md @@ -0,0 +1,129 @@ +## Description + +An `` provides accessible labelling for form elements. Use the `for` attribute to outline the `id` of an element in the same DOM tree to which it should associate itself. + +### Usage + +[![See it on NPM!](https://img.shields.io/npm/v/@spectrum-web-components/field-label?style=for-the-badge)](https://www.npmjs.com/package/@spectrum-web-components/field-label) +[![How big is this package in your project?](https://img.shields.io/bundlephobia/minzip/@spectrum-web-components/field-label?style=for-the-badge)](https://bundlephobia.com/result?p=@spectrum-web-components/field-label) + +``` +yarn add @spectrum-web-components/field-label +``` + +Import the side effectful registration of `` via: + +``` +import '@spectrum-web-components/field-label/sp-field-label.js'; +``` + +When looking to leverage the `FieldLabel` base class as a type and/or for extension purposes, do so via: + +``` +import { FieldLabel } from '@spectrum-web-components/field-label'; +``` + +## Example + +```html + + Life Story + + + + + + Birthplace + + + Choose a location: + + Istanbul + London + Maputo + Melbuorne + New York + San Fransisco + Santiago + Tokyo + + +``` + +## Side Aligned + +Using the `side-aligned` attribute will display the `` element inline with surrounding elements and the `start` and `end` values outline the alignment of the label text in the width specified. + +### Start + +Use `side-aligned="start"` to display the `` inline and to align the label text to the "start" of the flow of text: + +```html + + Life Story + + +
+
+ + Birthplace + + + Choose a location: + + Istanbul + London + Maputo + Melbuorne + New York + San Fransisco + Santiago + Tokyo + + +``` + +### End + +Use `side-aligned="end"` to display the `` inline and to align the label text to the "end" of the flow of text: + +```html + + Life Story + + +
+
+ + Birthplace + + + Choose a location: + + Istanbul + London + Maputo + Melbuorne + New York + San Fransisco + Santiago + Tokyo + + +``` diff --git a/packages/field-label/package.json b/packages/field-label/package.json new file mode 100644 index 0000000000..1491eac9a6 --- /dev/null +++ b/packages/field-label/package.json @@ -0,0 +1,59 @@ +{ + "name": "@spectrum-web-components/field-label", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/adobe/spectrum-web-components.git", + "directory": "packages/field-label" + }, + "bugs": { + "url": "https://github.com/adobe/spectrum-web-components/issues" + }, + "homepage": "https://adobe.github.io/spectrum-web-components/components/field-label", + "keywords": [ + "spectrum css", + "web components", + "lit-element", + "lit-html" + ], + "version": "0.0.1", + "description": "", + "main": "src/index.js", + "module": "src/index.js", + "type": "module", + "exports": { + "./src/": "./src/", + "./custom-elements.json": "./custom-elements.json", + "./package.json": "./package.json", + "./sp-field-label": "./sp-field-label.js", + "./sp-field-label.js": "./sp-field-label.js" + }, + "files": [ + "custom-elements.json", + "*.d.ts", + "*.js", + "*.js.map", + "/src/" + ], + "sideEffects": [ + "./sp-*.js", + "./sp-*.ts" + ], + "scripts": { + "test": "echo \"Error: run tests from mono-repo root.\" && exit 1" + }, + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@spectrum-css/fieldlabel": "^3.0.0-beta.6", + "@spectrum-web-components/textfield": "^0.5.4" + }, + "dependencies": { + "@spectrum-web-components/base": "^0.1.3", + "@spectrum-web-components/icons-ui": "^0.3.3", + "@spectrum-web-components/shared": "^0.7.4", + "tslib": "^2.0.0" + } +} diff --git a/packages/field-label/sp-field-label.ts b/packages/field-label/sp-field-label.ts new file mode 100644 index 0000000000..9bca15f20b --- /dev/null +++ b/packages/field-label/sp-field-label.ts @@ -0,0 +1,21 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { FieldLabel } from './src/FieldLabel.js'; + +customElements.define('sp-field-label', FieldLabel); + +declare global { + interface HTMLElementTagNameMap { + 'sp-field-label': FieldLabel; + } +} diff --git a/packages/field-label/src/FieldLabel.ts b/packages/field-label/src/FieldLabel.ts new file mode 100644 index 0000000000..9ffc231e44 --- /dev/null +++ b/packages/field-label/src/FieldLabel.ts @@ -0,0 +1,115 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { + html, + SpectrumElement, + CSSResultArray, + TemplateResult, + property, + PropertyValues, +} from '@spectrum-web-components/base'; +import type { Focusable } from '@spectrum-web-components/shared'; +import { AsteriskIcon } from '@spectrum-web-components/icons-ui'; +import '@spectrum-web-components/icon/sp-icon.js'; +import asterickIconStyles from '@spectrum-web-components/icon/src/spectrum-icon-asterick.css.js'; + +import styles from './field-label.css.js'; + +/** + * @element sp-field-label + */ +export class FieldLabel extends SpectrumElement { + public static get styles(): CSSResultArray { + return [styles, asterickIconStyles]; + } + + static instanceCount = 0; + + @property({ type: Boolean, reflect: true }) + public disabled = false; + + @property({ type: String }) + public id = ''; + + @property({ type: String }) + public for = ''; + + @property({ type: Boolean, reflect: true }) + public required = false; + + @property({ type: String, reflect: true, attribute: 'side-aligned' }) + public sideAligned?: 'start' | 'end'; + + private target?: HTMLElement; + + private handleClick(): void { + if (!this.target || this.disabled) return; + this.target.focus(); + } + + private async manageFor(): Promise { + if (!this.for) { + return; + } + const parent = this.getRootNode() as HTMLElement; + const target = parent.querySelector(`#${this.for}`) as Focusable; + if (typeof target.updateComplete !== 'undefined') { + await target.updateComplete; + } + this.target = target.focusElement || target; + if (this.target) { + const targetParent = this.target.getRootNode() as HTMLElement; + if (targetParent.isSameNode(parent)) { + this.target.setAttribute('aria-labelledby', this.id); + } else { + this.target.setAttribute( + 'aria-label', + (this.textContent || /* c8 ignore next */ '').trim() + ); + } + } + } + + protected render(): TemplateResult { + return html` + + `; + } + + protected firstUpdated(changes: PropertyValues): void { + super.firstUpdated(changes); + if (!this.hasAttribute('id')) { + this.setAttribute( + 'id', + `${this.tagName.toLowerCase()}-${FieldLabel.instanceCount++}` + ); + } + this.addEventListener('click', this.handleClick); + } + + protected updated(changes: PropertyValues): void { + super.updated(changes); + if (changes.has('for') || changes.has('id')) { + this.manageFor(); + } + } +} diff --git a/packages/field-label/src/field-label.css b/packages/field-label/src/field-label.css new file mode 100644 index 0000000000..0642bd5401 --- /dev/null +++ b/packages/field-label/src/field-label.css @@ -0,0 +1,13 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +@import './spectrum-field-label.css'; diff --git a/packages/field-label/src/index.ts b/packages/field-label/src/index.ts new file mode 100644 index 0000000000..f43503cd43 --- /dev/null +++ b/packages/field-label/src/index.ts @@ -0,0 +1,13 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +export * from './FieldLabel.js'; diff --git a/packages/field-label/src/spectrum-config.js b/packages/field-label/src/spectrum-config.js new file mode 100644 index 0000000000..112b21abc0 --- /dev/null +++ b/packages/field-label/src/spectrum-config.js @@ -0,0 +1,52 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +const config = { + spectrum: 'fieldlabel', + components: [ + { + name: 'field-label', + host: { + selector: '.spectrum-FieldLabel', + }, + attributes: [ + { + type: 'boolean', + selector: '.is-disabled', + name: 'disabled', + }, + { + type: 'enum', + name: 'side-aligned', + values: [ + { + name: 'start', + selector: '.spectrum-FieldLabel--left', + }, + { + name: 'end', + selector: '.spectrum-FieldLabel--right', + }, + ], + }, + ], + classes: [ + { + selector: '.spectrum-FieldLabel-requiredIcon', + name: 'requiredIcon', + }, + ], + }, + ], +}; + +export default config; diff --git a/packages/field-label/src/spectrum-field-label.css b/packages/field-label/src/spectrum-field-label.css new file mode 100644 index 0000000000..85a8c8c82e --- /dev/null +++ b/packages/field-label/src/spectrum-field-label.css @@ -0,0 +1,178 @@ +/* stylelint-disable */ /* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. + +THIS FILE IS MACHINE GENERATED. DO NOT EDIT */ +:host { + /* .spectrum-FieldLabel, + * .spectrum-Form-itemLabel */ + display: block; + box-sizing: border-box; + padding-top: var( + --spectrum-fieldlabel-padding-top, + var(--spectrum-global-dimension-size-50) + ); + padding-bottom: var( + --spectrum-fieldlabel-padding-bottom, + var(--spectrum-global-dimension-size-65) + ); + padding-left: 0; + padding-right: 0; + font-size: var( + --spectrum-fieldlabel-text-size, + var(--spectrum-global-dimension-font-size-75) + ); + font-weight: var( + --spectrum-fieldlabel-text-font-weight, + var(--spectrum-global-font-weight-regular) + ); + line-height: var( + --spectrum-fieldlabel-text-line-height, + var(--spectrum-global-font-line-height-small) + ); + vertical-align: top; + -webkit-font-smoothing: subpixel-antialiased; + -moz-osx-font-smoothing: auto; + font-smoothing: subpixel-antialiased; +} +:host([dir='ltr']) .requiredIcon { + /* [dir=ltr] .spectrum-FieldLabel-requiredIcon */ + margin-left: var( + --spectrum-fieldlabel-asterisk-gap, + var(--spectrum-global-dimension-size-25) + ); + margin-right: 0; +} +:host([dir='rtl']) .requiredIcon { + /* [dir=rtl] .spectrum-FieldLabel-requiredIcon */ + margin-right: var( + --spectrum-fieldlabel-asterisk-gap, + var(--spectrum-global-dimension-size-25) + ); + margin-left: 0; +} +.requiredIcon { + /* .spectrum-FieldLabel-requiredIcon */ + margin-top: var( + --spectrum-fieldlabel-asterisk-margin-y, + var(--spectrum-global-dimension-size-50) + ); + margin-bottom: 0; +} +:host([dir='ltr'][side-aligned='start']) { + /* [dir=ltr] .spectrum-FieldLabel--left */ + padding-left: 0; + padding-right: var( + --spectrum-fieldlabel-side-padding-x, + var(--spectrum-global-dimension-size-100) + ); +} +:host([dir='rtl'][side-aligned='start']) { + /* [dir=rtl] .spectrum-FieldLabel--left */ + padding-right: 0; + padding-left: var( + --spectrum-fieldlabel-side-padding-x, + var(--spectrum-global-dimension-size-100) + ); +} +:host([side-aligned='start']) { + /* .spectrum-FieldLabel--left */ + display: inline-block; + padding-top: var( + --spectrum-fieldlabel-side-padding-top, + var(--spectrum-global-dimension-size-100) + ); + padding-bottom: 0; +} +:host([dir='ltr'][side-aligned='start']) .requiredIcon { + /* [dir=ltr] .spectrum-FieldLabel--left .spectrum-FieldLabel-requiredIcon */ + margin-left: var( + --spectrum-fieldlabel-asterisk-gap, + var(--spectrum-global-dimension-size-25) + ); + margin-right: 0; +} +:host([dir='rtl'][side-aligned='start']) .requiredIcon { + /* [dir=rtl] .spectrum-FieldLabel--left .spectrum-FieldLabel-requiredIcon */ + margin-right: var( + --spectrum-fieldlabel-asterisk-gap, + var(--spectrum-global-dimension-size-25) + ); + margin-left: 0; +} +:host([side-aligned='start']) .requiredIcon { + /* .spectrum-FieldLabel--left .spectrum-FieldLabel-requiredIcon */ + margin-top: var(--spectrum-fieldlabel-side-asterisk-margin-y, 0); + margin-bottom: 0; +} +:host([dir='ltr'][side-aligned='end']) { + /* [dir=ltr] .spectrum-FieldLabel--right */ + text-align: right; +} +:host([dir='rtl'][side-aligned='end']) { + /* [dir=rtl] .spectrum-FieldLabel--right */ + text-align: left; +} +:host([dir='ltr'][side-aligned='end']) { + /* [dir=ltr] .spectrum-FieldLabel--right */ + padding-left: 0; + padding-right: var( + --spectrum-fieldlabel-side-padding-x, + var(--spectrum-global-dimension-size-100) + ); +} +:host([dir='rtl'][side-aligned='end']) { + /* [dir=rtl] .spectrum-FieldLabel--right */ + padding-right: 0; + padding-left: var( + --spectrum-fieldlabel-side-padding-x, + var(--spectrum-global-dimension-size-100) + ); +} +:host([side-aligned='end']) { + /* .spectrum-FieldLabel--right */ + display: inline-block; + padding-top: var( + --spectrum-fieldlabel-side-padding-top, + var(--spectrum-global-dimension-size-100) + ); + padding-bottom: 0; +} +:host { + /* .spectrum-FieldLabel, + * .spectrum-Form-itemLabel */ + color: var( + --spectrum-fieldlabel-text-color, + var(--spectrum-alias-label-text-color) + ); +} +:host([disabled]) { + /* .spectrum-FieldLabel.is-disabled, + * .spectrum-Form-itemLabel.is-disabled */ + color: var( + --spectrum-fieldlabel-text-color-disabled, + var(--spectrum-alias-text-color-disabled) + ); +} +:host([disabled]) .requiredIcon { + /* .spectrum-FieldLabel.is-disabled .spectrum-FieldLabel-requiredIcon, + * .spectrum-Form-itemLabel.is-disabled .spectrum-FieldLabel-requiredIcon */ + color: var( + --spectrum-fieldlabel-asterisk-color-disabled, + var(--spectrum-alias-text-color-disabled) + ); +} +.requiredIcon { + /* .spectrum-FieldLabel-requiredIcon */ + color: var( + --spectrum-fieldlabel-asterisk-color, + var(--spectrum-global-color-gray-600) + ); +} diff --git a/packages/field-label/stories/field-label.stories.ts b/packages/field-label/stories/field-label.stories.ts new file mode 100644 index 0000000000..2c56cfae49 --- /dev/null +++ b/packages/field-label/stories/field-label.stories.ts @@ -0,0 +1,156 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { html, TemplateResult } from '@spectrum-web-components/base'; +import '@spectrum-web-components/textfield/sp-textfield.js'; + +import '../sp-field-label.js'; + +export default { + title: 'Field Label', + component: 'sp-field-label', +}; + +export const standard = (): TemplateResult => { + return html` + Life Story + + + Life Story + + + `; +}; + +export const sideAlignStart = (): TemplateResult => { + return html` + + Life Story + + + `; +}; + +export const sideAlignEnd = (): TemplateResult => { + return html` + + Life Story + + + `; +}; + +export const required = (): TemplateResult => { + return html` + Life Story + + Life Story (Required) + +
+
+ + Life Story + + +
+
+ + Life Story + + + + Life Story + + + `; +}; + +export const picker = (): TemplateResult => { + return html` + + Select a Country with a very long label, too long in fact + + + + + Deselect + + + Select Inverse + + + Feather... + + + Select and Mask... + + + + Save Selection + + + Make Work Path + + + + `; +}; + +export const nativeInput = (): TemplateResult => { + return html` + Life Story + + `; +}; diff --git a/packages/field-label/test/benchmark/basic-test.ts b/packages/field-label/test/benchmark/basic-test.ts new file mode 100644 index 0000000000..81a0617149 --- /dev/null +++ b/packages/field-label/test/benchmark/basic-test.ts @@ -0,0 +1,19 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import '@spectrum-web-components/field-label/sp-field-label.js'; +import { html } from '@spectrum-web-components/base'; +import { measureFixtureCreation } from '../../../../test/benchmark/helpers.js'; + +measureFixtureCreation(html` + +`); diff --git a/packages/field-label/test/field-label.test.ts b/packages/field-label/test/field-label.test.ts new file mode 100644 index 0000000000..630b711ab6 --- /dev/null +++ b/packages/field-label/test/field-label.test.ts @@ -0,0 +1,138 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { fixture, elementUpdated, expect, html } from '@open-wc/testing'; + +import '@spectrum-web-components/textfield/sp-textfield.js'; +import { Textfield } from '@spectrum-web-components/textfield'; + +import '../sp-field-label.js'; +import { FieldLabel } from '..'; + +describe('FieldLabel', () => { + it('loads default field-label accessibly', async () => { + const el = await fixture( + html` +
+ Input label + +
+ ` + ); + + await elementUpdated(el); + + await expect(el).to.be.accessible(); + }); + it('loads [required] field-label accessibly', async () => { + const el = await fixture( + html` +
+ + Required input label + + +
+ ` + ); + + await elementUpdated(el); + + await expect(el).to.be.accessible(); + }); + it('loads with no "for"', async () => { + const el = await fixture( + html` + Input label + ` + ); + + await elementUpdated(el); + + await expect(el).to.be.accessible(); + }); + it('associates itself to an element whose "id" matches its "for" attribute', async () => { + const test = await fixture( + html` +
+ + +
+ ` + ); + const el = test.querySelector('sp-field-label') as FieldLabel; + const input = test.querySelector('input') as HTMLInputElement; + + await elementUpdated(el); + + expect(input.hasAttribute('aria-labelledby')); + expect(input.getAttribute('aria-labelledby')).to.equal(el.id); + }); + it('passed clicks to assiciated form element as focus', async () => { + const test = await fixture( + html` +
+ + +
+ ` + ); + const el = test.querySelector('sp-field-label') as FieldLabel; + const input = test.querySelector('input') as HTMLInputElement; + + await elementUpdated(el); + + el.click(); + await elementUpdated(el); + + expect(document.activeElement === input); + }); + it('associates itself to an element with a focueElement whose "id" matches its "for" attribute', async () => { + const test = await fixture( + html` +
+ + +
+ ` + ); + const el = test.querySelector('sp-field-label') as FieldLabel; + const input = (test.querySelector('sp-textfield') as Textfield) + .focusElement as HTMLInputElement; + + await elementUpdated(el); + + expect(input.hasAttribute('aria-label')); + expect(input.getAttribute('aria-label')).to.equal( + (el.textContent || '').trim() + ); + }); + it('passed clicks to assiciated form element with a focueElement as focus', async () => { + const test = await fixture( + html` +
+ + +
+ ` + ); + const el = test.querySelector('sp-field-label') as FieldLabel; + const input = test.querySelector('sp-textfield') as Textfield; + + await elementUpdated(el); + + el.click(); + await elementUpdated(el); + + expect(document.activeElement === input); + }); +}); diff --git a/packages/field-label/tsconfig.json b/packages/field-label/tsconfig.json new file mode 100644 index 0000000000..75919f9078 --- /dev/null +++ b/packages/field-label/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "composite": true, + "rootDir": "./" + }, + "include": ["*.ts", "src/*.ts"], + "exclude": ["test/*.ts", "stories/*.ts"], + "references": [{ "path": "../base" }] +} diff --git a/test/visual/stories.js b/test/visual/stories.js index fbbc7bbc57..8703fe83a8 100644 --- a/test/visual/stories.js +++ b/test/visual/stories.js @@ -135,6 +135,12 @@ module.exports = [ 'dropzone--default', 'field-group--horizontal', 'field-group--vertical', + 'field-label--standard', + 'field-label--side-align-start', + 'field-label--side-align-end', + 'field-label--required', + 'field-label--picker', + 'field-label--native-input', 'icon--medium', 'icon--large', 'icon--image-icon', diff --git a/yarn.lock b/yarn.lock index 3eeea2c268..ef790c1ec8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3495,6 +3495,11 @@ resolved "https://registry.yarnpkg.com/@spectrum-css/fieldgroup/-/fieldgroup-3.0.0-beta.5.tgz#2df6116a5c9131a1b30b6cc51b9fdf9bfa1a22ab" integrity sha512-P8w2ASt4Z47vsnaOxkGOKBzfXE7B/3x//awkaKUFf7fDjGUmGkuyPtUt5Pd1YxzQ57jGMpyL/8fZmFmn3UiDRw== +"@spectrum-css/fieldlabel@^3.0.0-beta.6": + version "3.0.0-beta.6" + resolved "https://registry.yarnpkg.com/@spectrum-css/fieldlabel/-/fieldlabel-3.0.0-beta.6.tgz#e72aa90f3eb03d7e5f3e7da5594c18c680206918" + integrity sha512-AeGmqMJczwTgHn/YTQ++tdTSS/4eBaFXJwW43LxJBI9U1aj68OB1dj/rmfuJCvsbr4yfmTEM+EszcLrZgEx7Xw== + "@spectrum-css/icon@^3.0.0-beta.1": version "3.0.0-beta.1" resolved "https://registry.yarnpkg.com/@spectrum-css/icon/-/icon-3.0.0-beta.1.tgz#62e18e5a83fdfc9ad638ade68e6f2e6457114495"