Skip to content

Commit

Permalink
feat: use input-message to display validation messages for invalid fi…
Browse files Browse the repository at this point in the history
…elds after form submission (#8574)

**Related Issue:** #8000

## Summary

- Replaces the native popover displayed on form elements with invalid
values after form submission.
- The `calcite-input-message` used to display the validation message is
cleared once the component's Change event fires (or Input event for
`calcite-input`, `calcite-input-number`, `calcite-input-text`, and
`calcite-text-area`).
- Non-calcite form elements will still display the native popover to
prevent a breaking change. It is up to the developer replace the native
popover with a `calcite-input-message` to prevent UI inconsistencies.
- This change may cause layout shifting after form submission due to
adding a `calcite-input-message` under calcite form elements with
invalid values.
  • Loading branch information
benelan authored Jan 17, 2024
1 parent e70f736 commit fd392fe
Show file tree
Hide file tree
Showing 9 changed files with 487 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -804,13 +804,6 @@ export class InputNumber
}
};

onFormReset(): void {
this.setNumberValue({
origin: "reset",
value: this.defaultValue,
});
}

syncHiddenFormInput(input: HTMLInputElement): void {
input.type = "number";
input.min = this.min?.toString(10) ?? "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -500,13 +500,6 @@ export class InputText
}
};

onFormReset(): void {
this.setValue({
origin: "reset",
value: this.defaultValue,
});
}

syncHiddenFormInput(input: HTMLInputElement): void {
if (this.minLength != null) {
input.minLength = this.minLength;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function testPostValidationFocusing(

await page.setContent(html`
<form>
<${inputTag} type="text" required name="${inputName}"></${inputTag}>
<${inputTag} required name="${inputName}"></${inputTag}>
</form>
<script>
const form = document.querySelector("form");
Expand All @@ -32,8 +32,8 @@ export function testPostValidationFocusing(
const hiddenInputSelector = `input[slot=${hiddenFormInputSlotName}]`;
const inputSelector = `${inputTag}[name=${inputName}]`;

expect(await isElementFocused(page, hiddenInputSelector)).toBe(true);
expect(await isElementFocused(page, inputSelector)).toBe(false);
expect(await isElementFocused(page, hiddenInputSelector)).toBe(false);
expect(await isElementFocused(page, inputSelector)).toBe(true);
expect(await input.getProperty("value")).toBe("");

const expectedValue = "12345"; // number works for both text and number types
Expand Down
7 changes: 0 additions & 7 deletions packages/calcite-components/src/components/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -890,13 +890,6 @@ export class Input
}
};

onFormReset(): void {
this.setValue({
origin: "reset",
value: this.defaultValue,
});
}

syncHiddenFormInput(input: HTMLInputElement): void {
const { type } = this;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,10 +403,6 @@ export class TextArea
//
//--------------------------------------------------------------------------

onFormReset(): void {
this.value = this.defaultValue;
}

onLabelClick(): void {
this.setFocus();
}
Expand Down
201 changes: 107 additions & 94 deletions packages/calcite-components/src/demos/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,60 +37,106 @@

<body>
<demo-dom-swapper>
<h1 style="margin: 0 auto; text-align: center">Switch</h1>
<h1 style="margin: 0 auto; text-align: center">Form</h1>

<form id="test-form">
<div class="child">
<label>Native Input<input id="native-input" name="native-input" value="" type="text" /></label>
</div>

<div class="child">
<label><input name="native-checkbox" value="checked" type="checkbox" />Native checkbox</label>
</div>

<div class="child">
<calcite-label layout="inline">
<calcite-checkbox required value="checked" name="calcite-checkbox"></calcite-checkbox>Calcite checkbox
</calcite-label>
</div>

<div class="child">
<calcite-label layout="inline">
<calcite-switch name="calcite-switch" value="checked"></calcite-switch>Calcite switch
</calcite-label>
<div class="parent">
<div class="child">
<label><input required name="native-checkbox" type="checkbox" /> Native Checkbox</label>
</div>
<div class="child">
<calcite-label layout="inline">
<calcite-checkbox required name="calcite-checkbox"></calcite-checkbox>Calcite Checkbox
</calcite-label>
</div>
</div>

<div class="parent">
<div class="child">
<label
>Native select<br />
<select name="native-select" multiple>
>Native Select<br />
<select required name="native-select" multiple>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="opel">Opel</option>
<option value="audi">Audi</option>
</select>
</label>
</div>
</div>

<div class="parent">
<div class="child">
<calcite-label
>Calcite select<br />
<calcite-select name="calcite-select" label="calcite select">
>Calcite Select<br />
<calcite-select required name="calcite-select" label="calcite select">
<calcite-option></calcite-option>
<calcite-option>high</calcite-option>
<calcite-option>medium</calcite-option>
<calcite-option>low</calcite-option>
</calcite-select></calcite-label
>
</div>
</div>

<div class="parent">
<div class="child">
<label>Native Input<input required id="native-input" name="native-input" type="text" /></label>
</div>

<div class="child">
<calcite-label>
Calcite Input
<calcite-input
required
id="calcite-input"
name="calcite-input"
type="text"
placeholder="Placeholder"
></calcite-input>
</calcite-label>
</div>
</div>

<div class="parent">
<div class="child">
<calcite-label>
Calcite Input Number
<calcite-input-number
required
id="calcite-input-number"
name="calcite-input-number"
placeholder="Placeholder"
></calcite-input-number>
</calcite-label>
</div>

<div class="child">
<calcite-label>
Calcite Input Text
<calcite-input-text
required
id="calcite-input-text"
name="calcite-input-text"
placeholder="Placeholder"
></calcite-input-text>
</calcite-label>
</div>
</div>

<div class="parent">
<div class="child">
<calcite-label>
Calcite Text Area
<calcite-text-area required id="calcite-text-area" name="calcite-text-area" placeholder="Placeholder">
</calcite-text-area>
</calcite-label>
</div>
</div>

<div class="parent">
<div class="child">
<calcite-label
>Calcite combobox<br />
>Calcite Combobox
<calcite-combobox
required
name="calcite-combobox"
label="test"
placeholder="select folder"
Expand All @@ -103,58 +149,43 @@ <h1 style="margin: 0 auto; text-align: center">Switch</h1>
<calcite-combobox-item value="3" text-label="Folder 3" icon="folder"></calcite-combobox-item>
<calcite-combobox-item value="4" text-label="Folder 4" icon="folder"></calcite-combobox-item>
<calcite-combobox-item value="5" text-label="Folder 5" icon="folder"></calcite-combobox-item>
<calcite-combobox-item
value="6"
text-label="Folder 6"
icon="folder"
></calcite-combobox-item> </calcite-combobox
></calcite-label>
<calcite-combobox-item value="6" text-label="Folder 6" icon="folder"></calcite-combobox-item>
</calcite-combobox>
</calcite-label>
</div>
</div>

<div class="parent">
<div class="child">
<calcite-label>
Input Label
<calcite-input
id="calcite-input"
required
name="calcite-input"
type="text"
placeholder="Placeholder"
></calcite-input>
Input Time Zone
<calcite-input-time-zone required name="calcite-time-time-zone"></calcite-input-time-zone>
</calcite-label>
</div>
</div>

<div class="parent">
<div class="child">
<calcite-label>
Input time picker
Input Time Picker
<calcite-input-time-picker
required
name="calcite-input-time-picker"
hour-display-format="12"
name="light"
step="1"
value="15:37"
></calcite-input-time-picker>
</calcite-label>
</div>
</div>

<div class="parent">
<div class="child">
<calcite-label>
Input date picker
<calcite-input-date-picker range name="calcite-input-date-picker"></calcite-input-date-picker>
Input Date Picker
<calcite-input-date-picker required range name="calcite-input-date-picker"></calcite-input-date-picker>
</calcite-label>
</div>
</div>

<div class="parent">
<div class="child">
<calcite-label>
segmented control
Segmented Control
<calcite-segmented-control name="calcite-segmented-control">
<calcite-segmented-control-item value="react" checked>React</calcite-segmented-control-item>
<calcite-segmented-control-item value="ember">Ember</calcite-segmented-control-item>
Expand All @@ -163,34 +194,41 @@ <h1 style="margin: 0 auto; text-align: center">Switch</h1>
</calcite-segmented-control></calcite-label
>
</div>
</div>

<div class="parent">
<div class="child">
<calcite-label layout="inline">
<calcite-radio-button name="radio" value="stencil-radio"></calcite-radio-button>
Stencil
</calcite-label>
<calcite-label layout="inline">
<calcite-radio-button name="radio" value="react-radio" checked></calcite-radio-button>
React
</calcite-label>
<calcite-label layout="inline">
<calcite-radio-button name="radio" value="ember-radio"></calcite-radio-button>
Ember
<calcite-label>
Radio Button Group
<calcite-radio-button-group required name="calcite-radio-button-group" layout="horizontal">
<calcite-label layout="inline">
<calcite-radio-button value="trees"></calcite-radio-button>
Trees
</calcite-label>
<calcite-label layout="inline">
<calcite-radio-button value="shrubs"></calcite-radio-button>
Shrubs
</calcite-label>
<calcite-label layout="inline">
<calcite-radio-button value="bushes"></calcite-radio-button>
Bushes
</calcite-label>
</calcite-radio-button-group>
</calcite-label>
</div>
</div>

<div class="parent">
<div class="child">
<calcite-rating name="rating" value="2"></calcite-rating>
<calcite-label
>Rating
<calcite-rating name="rating" value="2"></calcite-rating>
</calcite-label>
</div>
</div>

<div class="parent">
<div class="child">
<calcite-slider name="slider" min="0" max="100" value="40" step="1" label="Temperature"></calcite-slider>
<calcite-label
>Slider
<calcite-slider name="slider" min="0" max="100" value="40" step="1" label="Temperature"></calcite-slider>
</calcite-label>
</div>
</div>

Expand All @@ -199,38 +237,13 @@ <h1 style="margin: 0 auto; text-align: center">Switch</h1>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</div>
</div>

<div class="parent">
<div class="child">
<calcite-button type="submit">Submit</calcite-button>
<calcite-button type="reset">Reset</calcite-button>
</div>
</div>
</form>
<script>
window.onload = () => {
const nativeInput = document.getElementById("native-input");
const calciteInput = document.getElementById("calcite-input");
const form = document.getElementById("test-form");

// console.log({ nativeInput: nativeInput.reportValidity() });
// console.log({ calciteInput: calciteInput.reportValidity() });
// console.log({ validity: form.checkValidity(), reportValidity: form.reportValidity() });

form.addEventListener("submit", (event) => {
event.preventDefault();
const formData = new FormData(event.target);
const entries = Object.fromEntries(
Array.from(formData.keys()).map((key) => [
key,
formData.getAll(key).length > 1 ? formData.getAll(key) : formData.get(key),
]),
);
console.log(entries);
});
};
</script>
</demo-dom-swapper>
</body>
</html>
Loading

0 comments on commit fd392fe

Please sign in to comment.