Skip to content

Commit

Permalink
fix(text-field): clean up text field demo
Browse files Browse the repository at this point in the history
  • Loading branch information
samrichardsontylertech committed Feb 23, 2024
1 parent 261134e commit 3c7da33
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 96 deletions.
25 changes: 13 additions & 12 deletions src/dev/pages/text-field/text-field.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,26 @@
<div>
<h3 class="forge-typography--heading2">Default</h3>
<forge-text-field>
<forge-icon slot="start" name="person"></forge-icon>
<label>First name</label>
<input type="text" id="text-field-input" />
<textarea id="text-field-textarea"></textarea>
<forge-icon slot="end" name="info_outline"></forge-icon>
<forge-icon-button slot="accessory" density="medium">
<forge-icon name="more_vert"></forge-icon>
</forge-icon-button>
<span slot="support-text-start" slot="support-text-start">Please enter your first name</span>
<span slot="support-text-end" slot="support-text-end"></span>
<input type="text" />
</forge-text-field>
</div>

<div>
<h3 class="forge-typography--heading2">Textarea</h3>
<forge-text-field>
<label>Label text</label>
<textarea></textarea>
</forge-text-field>
</h3>
</div>

<div>
<h3 class="forge-typography--heading2">Multiple inputs</h3>
<forge-text-field>
<label>First and last name</label>
<input type="text" />
<input type="text" />
<span slot="label">First and last name</span>
<input type="text" aria-label="First name" />
<input type="text" aria-label="Last name" />
</forge-text-field>
</div>

Expand Down
1 change: 0 additions & 1 deletion src/dev/pages/text-field/text-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
options: [
{ type: 'text-field', label: 'Label', id: 'opt-label', defaultValue: 'First name' },
{ type: 'text-field', label: 'Placeholder', id: 'opt-placeholder' },
{ type: 'switch', label: 'Textarea', id: 'opt-textarea' },
{ type: 'switch', label: 'Required', id: 'opt-required' },
{ type: 'switch', label: 'Optional', id: 'opt-optional' },
{ type: 'switch', label: 'Invalid', id: 'opt-invalid' },
Expand Down
6 changes: 4 additions & 2 deletions src/dev/pages/text-field/text-field.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
forge-text-field {
width: 256px;
.container {
forge-text-field {
width: 256px;
}
}
209 changes: 132 additions & 77 deletions src/dev/pages/text-field/text-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,12 @@ IconRegistry.define([
tylIconPerson
]);

const textField = document.querySelector('forge-text-field') as ITextFieldComponent;
const input = textField.querySelector('input') as HTMLInputElement;
const textarea = textField.querySelector('textarea') as HTMLTextAreaElement;
const label = textField.querySelector('label') as HTMLLabelElement;
const startEl = textField.querySelector('[slot=start]') as HTMLElement;
const endEl = textField.querySelector('[slot=end]') as HTMLElement;
const accessoryEl = textField.querySelector('[slot=accessory]') as HTMLElement;
const supportTextStartEl = textField.querySelector('[slot=support-text-start]') as HTMLElement;
const supportTextEndEl = textField.querySelector('[slot=support-text-end]') as HTMLElement;

// Hide these elements by default
startEl.remove();
textarea.remove();
endEl.remove();
accessoryEl.remove();
supportTextStartEl.remove();
supportTextEndEl.remove();
const container = document.querySelector('.container');
const textFields = container.querySelectorAll('forge-text-field') as NodeListOf<ITextFieldComponent>;

// Options
const optLabel = document.getElementById('opt-label') as HTMLInputElement;
const optPlaceholder = document.getElementById('opt-placeholder') as HTMLInputElement;
const optTextarea = document.getElementById('opt-textarea') as ISwitchComponent;
const optRequired = document.getElementById('opt-required') as ISwitchComponent;
const optOptional = document.getElementById('opt-optional') as ISwitchComponent;
const optInvalid = document.getElementById('opt-invalid') as ISwitchComponent;
Expand All @@ -54,122 +38,193 @@ const optLabelAlignment = document.getElementById('opt-label-alignment') as ISel
const optSupportTextInset = document.getElementById('opt-support-text-inset') as ISwitchComponent;

optLabel.addEventListener('input', () => {
label.textContent = optLabel.value;
textFields.forEach(textField => {
textField.querySelector('label').textContent = optLabel.value;
});
});

optPlaceholder.addEventListener('input', () => {
input.placeholder = optPlaceholder.value;
});

optTextarea.addEventListener('forge-switch-change', () => {
if (optTextarea.on) {
input.remove();
textField.prepend(textarea);
} else {
textarea.remove();
textField.prepend(input);
}
textFields.forEach(textField => {
(textField.querySelector(':is(input, textarea)') as HTMLInputElement | HTMLTextAreaElement).placeholder = optPlaceholder.value;
});
});

optRequired.addEventListener('forge-switch-change', () => {
textField.required = optRequired.on;
textFields.forEach(textField => {
textField.required = optRequired.on;
});
});

optOptional.addEventListener('forge-switch-change', () => {
textField.optional = optOptional.on;
textFields.forEach(textField => {
textField.optional = optOptional.on;
});
});

optInvalid.addEventListener('forge-switch-change', () => {
textField.invalid = optInvalid.on;
textFields.forEach(textField => {
textField.invalid = optInvalid.on;
});
});

optDisabled.addEventListener('forge-switch-change', () => {
textField.disabled = optDisabled.on;
textFields.forEach(textField => {
textField.disabled = optDisabled.on;
});
});

optDense.addEventListener('forge-switch-change', () => {
textField.dense = optDense.on;
textFields.forEach(textField => {
textField.dense = optDense.on;
});
});

optFloatLabel.addEventListener('forge-switch-change', () => {
textField.floatLabel = optFloatLabel.on;
textFields.forEach(textField => {
textField.floatLabel = optFloatLabel.on;
});
});

optShowClear.addEventListener('forge-switch-change', () => {
textField.showClear = optShowClear.on;
textFields.forEach(textField => {
textField.showClear = optShowClear.on;
});
});

optStart.addEventListener('forge-switch-change', () => {
if (optStart.on) {
textField.prepend(startEl);
} else {
startEl.remove();
}
textFields.forEach(textField => {
if (optStart.on) {
textField.append(createStartElement());
} else {
textField.querySelector('[slot=start]').remove();
}
});
});

optEnd.addEventListener('forge-switch-change', () => {
if (optEnd.on) {
textField.append(endEl);
} else {
endEl.remove();
}
textFields.forEach(textField => {
if (optEnd.on) {
textField.append(createEndElement());
} else {
textField.querySelector('[slot=end]').remove();
}
});
});

optAccessory.addEventListener('forge-switch-change', () => {
if (optAccessory.on) {
textField.append(accessoryEl);
} else {
accessoryEl.remove();
}
textFields.forEach(textField => {
if (optAccessory.on) {
textField.append(createAccessoryElement());
} else {
textField.querySelector('[slot=accessory]').remove();
}
});
});

optSupportTextStart.addEventListener('forge-switch-change', () => {
if (optSupportTextStart.on) {
textField.prepend(supportTextStartEl);
} else {
supportTextStartEl.remove();
}
textFields.forEach(textField => {
if (optSupportTextStart.on) {
textField.append(createSupportTextStartElement());
} else {
textField.querySelector('[slot=support-text-start]').remove();
}
});
});

optSupportTextEnd.addEventListener('forge-switch-change', () => {
if (optSupportTextEnd.on) {
textField.append(supportTextEndEl);
} else {
supportTextEndEl.remove();
}
textFields.forEach(textField => {
if (optSupportTextEnd.on) {
textField.append(createSupportTextEndElement());
} else {
textField.querySelector('[slot=support-text-end]').remove();
}
});
});

optDensity.addEventListener('change', () => {
textField.density = optDensity.value as FieldDensity;
textFields.forEach(textField => {
textField.density = optDensity.value as FieldDensity;
});
});

optVariant.addEventListener('change', () => {
textField.variant = optVariant.value as FieldVariant;
textFields.forEach(textField => {
textField.variant = optVariant.value as FieldVariant;
});
});

optTheme.addEventListener('change', () => {
textField.theme = optTheme.value as FieldTheme;
textFields.forEach(textField => {
textField.theme = optTheme.value as FieldTheme;
});
});

optShape.addEventListener('change', () => {
textField.shape = optShape.value as FieldShape;
textFields.forEach(textField => {
textField.shape = optShape.value as FieldShape;
});
});

optLabelPosition.addEventListener('change', () => {
textField.labelPosition = optLabelPosition.value as FieldLabelPosition;
textFields.forEach(textField => {
textField.labelPosition = optLabelPosition.value as FieldLabelPosition;
});
});

optLabelAlignment.addEventListener('change', () => {
textField.labelAlignment = optLabelAlignment.value as FieldLabelAlignment;
textFields.forEach(textField => {
textField.labelAlignment = optLabelAlignment.value as FieldLabelAlignment;
});
});

optSupportTextInset.addEventListener('change', () => {
textField.supportTextInset = optSupportTextInset.value as FieldSupportTextInset;
});

textField.addEventListener('forge-text-field-clear', evt => {
console.log(evt);
if (optPreventClear.on) {
evt.preventDefault();
}
});
textFields.forEach(textField => {
textField.supportTextInset = optSupportTextInset.value as FieldSupportTextInset;
});
});

textFields.forEach(textField => {
textField.addEventListener('forge-text-field-clear', evt => {
console.log(evt);
if (optPreventClear.on) {
evt.preventDefault();
}
});
});

function createStartElement(): HTMLElement {
const icon = document.createElement('forge-icon');
icon.name = 'person';
icon.slot = 'start';
return icon;
}

function createEndElement(): HTMLElement {
const icon = document.createElement('forge-icon');
icon.name = 'info_outline';
icon.slot = 'end';
return icon;
}

function createAccessoryElement(): HTMLElement {
const button = document.createElement('forge-icon-button');
const icon = document.createElement('forge-icon');
button.density = 'medium';
button.slot = 'accessory';
icon.name = 'more_vert';
return icon;
}

function createSupportTextStartElement(): HTMLElement {
const supportText = document.createElement('span');
supportText.slot = 'support-text-start';
supportText.textContent = 'Support text start';
return supportText;
}

function createSupportTextEndElement(): HTMLElement {
const supportText = document.createElement('span');
supportText.slot = 'support-text-end';
supportText.textContent = '7/100';
return supportText;
}
7 changes: 4 additions & 3 deletions src/lib/text-field/text-field-foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,15 @@ export class TextFieldFoundation extends BaseFieldFoundation<ITextFieldAdapter>
}

private _onClearButtonClick(evt: Event): void {
const cancelled = this._adapter.dispatchHostEvent(evt);
if (!cancelled) {
const event = new CustomEvent(TEXT_FIELD_CONSTANTS.events.CLEAR, { bubbles: true, cancelable: true, composed: true });
this._adapter.dispatchHostEvent(event);
if (!event.defaultPrevented) {
this._adapter.clearInput();
}
};

private _onValueChange(): void {
this._adapter.tryFloatLabel();
this._tryFloatLabel();
this._toggleClearButtonVisibility();
}

Expand Down
21 changes: 20 additions & 1 deletion src/lib/text-field/text-field.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getShadowElement } from '@tylertech/forge-core';
import { tick } from '@tylertech/forge-testing';
import { sendMouse } from '@web/test-runner-commands';
import { spy } from 'sinon';
import { ITextFieldComponent } from '.';
import { ITextFieldComponent, TEXT_FIELD_CONSTANTS } from '.';
import { TestHarness } from '../../test/utils/test-harness';
import { FIELD_CONSTANTS, IFieldComponent } from '../field-next';
import { ICON_BUTTON_CONSTANTS, IIconButtonComponent } from '../icon-button';
Expand Down Expand Up @@ -121,6 +121,25 @@ describe('Text field', () => {
await tick();
expect(harness.inputElement.value).to.equal('');
});

it('should emit event when clear button is pressed', async () => {
const harness = await createFixture({ showClear: true });
harness.inputElement.value = 'test';
const eventSpy = spy();
harness.element.addEventListener(TEXT_FIELD_CONSTANTS.events.CLEAR, eventSpy);
harness.clearButtonElement!.click();
await tick();
expect(eventSpy).to.have.been.called;
});

it('should not clear text field when clear button is clicked and event is canceled', async () => {
const harness = await createFixture({ showClear: true });
harness.inputElement.value = 'test';
harness.element.addEventListener(TEXT_FIELD_CONSTANTS.events.CLEAR, (evt) => evt.preventDefault());
harness.clearButtonElement!.click();
await tick();
expect(harness.inputElement.value).to.equal('test');
});
});

describe('label', () => {
Expand Down

0 comments on commit 3c7da33

Please sign in to comment.