-
Notifications
You must be signed in to change notification settings - Fork 31
GAUD-10044: Create FormElementContainerMixin mixin #7016
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
base: main
Are you sure you want to change the base?
Changes from all commits
a06a912
2df07d8
37a53b8
8b0e9b7
f767d4a
9f7d24e
910e793
8a0cbd0
59a532f
8b4bbdc
c84151f
8984fde
5a9742f
a986f1e
f7c66ca
a2dde54
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import '../../inputs/input-group.js'; | ||
| import '../../inputs/input-number.js'; | ||
| import '../../inputs/input-text.js'; | ||
| import { html, LitElement } from 'lit'; | ||
| import { FormElementContainerMixin } from '../form-element-container-mixin.js'; | ||
| import { inputLabelStyles } from '../../inputs/input-label-styles.js'; | ||
| import { inputStyles } from '../../inputs/input-styles.js'; | ||
|
|
||
| class CustomFormElementContainer extends FormElementContainerMixin(LitElement) { | ||
|
|
||
| static styles = [inputStyles, inputLabelStyles]; | ||
|
|
||
| render() { | ||
| return html` | ||
| <d2l-input-group> | ||
|
EdwinACL831 marked this conversation as resolved.
|
||
| <div> | ||
| <label for="native-input" class="d2l-input-label d2l-input-label-required">First Name</label> | ||
| <input | ||
| id="native-input" | ||
| type="text" | ||
| name="first-name" | ||
| minlength="4" | ||
| maxlength="15" | ||
| required | ||
| class="d2l-input"> | ||
| </div> | ||
| <d2l-input-text label="Middle Name" name="middle-name" minlength="4" maxlength="8"></d2l-input-text> | ||
|
EdwinACL831 marked this conversation as resolved.
|
||
| <d2l-input-text label="Last Name" name="last-name" required minlength="4" maxlength="15"></d2l-input-text> | ||
| <d2l-input-text | ||
| label="Telephone Number" | ||
| type="tel" | ||
| name="phone-number" | ||
| pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" | ||
| required | ||
| ></d2l-input-text> | ||
| <d2l-input-number label="Age" name="age" required min="18" max="23"></d2l-input-number> | ||
| </d2l-input-group> | ||
| `; | ||
| } | ||
| } | ||
|
|
||
| customElements.define('d2l-custom-form-element-container', CustomFormElementContainer); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # FormElementContainerMixin | ||
|
|
||
| Extending `FormElementContainerMixin` will allow a custom element's children (native or [custom](./form-element-mixin.md)) to participate in the parent `<d2l-form>`. | ||
|
|
||
| ## Usage | ||
|
|
||
| Simply extend the `FormElementContainerMixin`: | ||
|
|
||
| ```javascript | ||
| import { FormElementContainerMixin } from '@brightspace-ui/core/form/form-element-container-mixin.js'; | ||
|
|
||
| class MyCustomFormElementContainer extends FormElementContainerMixin(LitElement) { | ||
| render() { | ||
| html` | ||
| <d2l-input-text | ||
| label="First Name" | ||
| name="first-name" | ||
| required></d2l-input-text> | ||
| <d2l-input-text | ||
| label="Last Name" | ||
| name="last-name" | ||
| required></d2l-input-text> | ||
| `; | ||
| } | ||
| } | ||
|
|
||
| customElements.define('my-custom-form-element-container', MyCustomFormElementContainer); | ||
| ``` |
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change in combination with the changes at |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| /** | ||
| * When applied to a custom element, form elements within will participate in the form. | ||
| */ | ||
| export const FormElementContainerMixin = superClass => class extends superClass { | ||
| get isCustomFormElementContainer() { | ||
| return true; | ||
| } | ||
|
EdwinACL831 marked this conversation as resolved.
|
||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,11 @@ | ||
| import './form-element.js'; | ||
| import '../../inputs/input-text.js'; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is needed because the class defined using defineCE uses it within its render method. |
||
| import '../../status-indicator/status-indicator.js'; | ||
| import '../../tooltip/tooltip.js'; | ||
| import { defineCE, expect, fixture } from '@brightspace-ui/testing'; | ||
| import { findFormElements, flattenMap, getFormElementData, isCustomElement, isCustomFormElement, isElement, isNativeFormElement, tryGetLabelText } from '../form-helper.js'; | ||
| import { findFormElements, flattenMap, getFormElementData, isCustomElement, isCustomFormElement, isCustomFormElementContainer, isElement, isNativeFormElement, tryGetLabelText } from '../form-helper.js'; | ||
| import { html, LitElement } from 'lit'; | ||
| import { FormElementContainerMixin } from '../form-element-container-mixin.js'; | ||
|
|
||
| const buttonFixture = html`<button type="button">Add to favorites</button>`; | ||
|
|
||
|
|
@@ -69,24 +71,50 @@ const h1Fixture = html`<h1>Beetles</h1>`; | |
|
|
||
| const formElementFixture = html`<d2l-test-form-element></d2l-test-form-element>`; | ||
|
|
||
| const nestedCustomFormElementContainerTag = defineCE(class extends FormElementContainerMixin(LitElement) { | ||
| render() { | ||
| return html` | ||
| <label for="nested-native-input">Name</label> | ||
| <input | ||
| id="nested-native-input" | ||
| type="text" | ||
| name="name" | ||
| minlength="4" | ||
| maxlength="15" | ||
| required | ||
| > | ||
| <d2l-input-text | ||
| label="Telephone Number" | ||
| type="tel" | ||
| name="phone" | ||
| pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" | ||
| required | ||
| ></d2l-input-text> | ||
| `; | ||
| } | ||
| }); | ||
|
|
||
| const nestedCustomFormElementContainerFixture = `<${nestedCustomFormElementContainerTag}></${nestedCustomFormElementContainerTag}>`; | ||
|
|
||
| describe('form-helper', () => { | ||
|
|
||
| describe('elements', () => { | ||
|
|
||
| [ | ||
| { tag: 'button', fixture: buttonFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false } }, | ||
| { tag: 'fieldset', fixture: fieldsetFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false } }, | ||
| { tag: 'input', fixture: inputFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false } }, | ||
| { tag: 'object', fixture: objectFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false } }, | ||
| { tag: 'output', fixture: outputFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false } }, | ||
| { tag: 'select', fixture: selectFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false } }, | ||
| { tag: 'textarea', fixture: textareaFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false } }, | ||
| { tag: 'div', fixture: divFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: false, isCustomFormElement: false } }, | ||
| { tag: 'label', fixture: labelFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: false, isCustomFormElement: false } }, | ||
| { tag: 'form', fixture: formFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: false, isCustomFormElement: false } }, | ||
| { tag: 'h1', fixture: h1Fixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: false, isCustomFormElement: false } }, | ||
| { tag: 'd2l-status-indicator', fixture: d2lStatusIndicatorFixture, expected: { isElement: true, isCustomElement: true, isNativeFormElement: false, isCustomFormElement: false } }, | ||
| { tag: 'd2l-test-form-element', fixture: formElementFixture, expected: { isElement: true, isCustomElement: true, isNativeFormElement: false, isCustomFormElement: true } } | ||
| { tag: 'button', fixture: buttonFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false, isCustomFormElementContainer: false } }, | ||
| { tag: 'fieldset', fixture: fieldsetFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false, isCustomFormElementContainer: false } }, | ||
| { tag: 'input', fixture: inputFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false, isCustomFormElementContainer: false } }, | ||
| { tag: 'object', fixture: objectFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false, isCustomFormElementContainer: false } }, | ||
| { tag: 'output', fixture: outputFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false, isCustomFormElementContainer: false } }, | ||
| { tag: 'select', fixture: selectFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false, isCustomFormElementContainer: false } }, | ||
| { tag: 'textarea', fixture: textareaFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: true, isCustomFormElement: false, isCustomFormElementContainer: false } }, | ||
| { tag: 'div', fixture: divFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: false, isCustomFormElement: false, isCustomFormElementContainer: false } }, | ||
| { tag: 'label', fixture: labelFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: false, isCustomFormElement: false, isCustomFormElementContainer: false } }, | ||
| { tag: 'form', fixture: formFixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: false, isCustomFormElement: false, isCustomFormElementContainer: false } }, | ||
| { tag: 'h1', fixture: h1Fixture, expected: { isElement: true, isCustomElement: false, isNativeFormElement: false, isCustomFormElement: false, isCustomFormElementContainer: false } }, | ||
| { tag: 'd2l-status-indicator', fixture: d2lStatusIndicatorFixture, expected: { isElement: true, isCustomElement: true, isNativeFormElement: false, isCustomFormElement: false, isCustomFormElementContainer: false } }, | ||
| { tag: 'd2l-test-form-element', fixture: formElementFixture, expected: { isElement: true, isCustomElement: true, isNativeFormElement: false, isCustomFormElement: true, isCustomFormElementContainer: false } }, | ||
| { tag: nestedCustomFormElementContainerTag, fixture: nestedCustomFormElementContainerFixture, expected: { isElement: true, isCustomElement: true, isNativeFormElement: false, isCustomFormElement: false, isCustomFormElementContainer: true } } | ||
| ].forEach(({ tag, fixture: eleFixture, expected }) => { | ||
|
|
||
| describe(tag, () => { | ||
|
|
@@ -113,6 +141,10 @@ describe('form-helper', () => { | |
| expect(isCustomFormElement(ele)).to.equal(expected.isCustomFormElement); | ||
| }); | ||
|
|
||
| it(`${tag} should ${expected.isCustomFormElementContainer ? '' : 'not '}be a custom form element container`, () => { | ||
| expect(isCustomFormElementContainer(ele)).to.equal(expected.isCustomFormElementContainer); | ||
| }); | ||
|
|
||
| }); | ||
|
|
||
| }); | ||
|
|
@@ -336,6 +368,15 @@ describe('form-helper', () => { | |
| expect(formElements).to.include.members([fakeFormElement]); | ||
| }); | ||
|
|
||
| it('should find elements nested in the custom form element container\'s shadow DOM', async() => { | ||
| root = await fixture(nestedCustomFormElementContainerFixture); | ||
| const nestedNativeInput = root.shadowRoot.querySelector('#nested-native-input'); | ||
| const nestedCustomInput = root.shadowRoot.querySelector('d2l-input-text[name="phone"]'); | ||
| const formElements = findFormElements(root); | ||
|
|
||
| expect(formElements).to.include.members([nestedNativeInput, nestedCustomInput]); | ||
| }); | ||
|
|
||
| }); | ||
|
|
||
| describe('flattenMap', () => { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.