Skip to content

fix(number-field): sp-number-field UI fixes for validation and width #5326

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

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4b186c2
chore: add validation story
caseyisonit Apr 1, 2025
e712995
fix: css fix for icons rendering over stepper buttons
caseyisonit Apr 3, 2025
2110b37
fix: icon no longer covers button and works with flex box
caseyisonit Apr 3, 2025
07a656f
fix: more specific fix to avoid side effects in text-field
caseyisonit Apr 3, 2025
504e10b
chore: add validation states to the number field docs
caseyisonit Apr 3, 2025
7c9fab7
fix(number-field): add back default widths and expose mod variable
caseyisonit Apr 7, 2025
1c9768e
chore: remove hard coded widths from stories to ensure no css regress…
caseyisonit Apr 7, 2025
b7b06cf
fix: textfield spectrum config error
caseyisonit Apr 8, 2025
72fda69
chore: remove unused code
caseyisonit Apr 17, 2025
de04cb9
fix: width and icon overlap
caseyisonit Apr 22, 2025
0860c6e
chore: add validation story
caseyisonit Apr 1, 2025
2c82648
fix: css fix for icons rendering over stepper buttons
caseyisonit Apr 3, 2025
93c8d56
fix: icon no longer covers button and works with flex box
caseyisonit Apr 3, 2025
c729f0b
fix: more specific fix to avoid side effects in text-field
caseyisonit Apr 3, 2025
0d27b13
fix(number-field): add back default widths and expose mod variable
caseyisonit Apr 7, 2025
8fc16ba
fix: textfield spectrum config error
caseyisonit Apr 8, 2025
68fb75a
chore: remove unused code
caseyisonit Apr 17, 2025
c5e9490
chore: add changeset
caseyisonit Apr 22, 2025
60d8375
chore: correct story ids
caseyisonit Apr 22, 2025
86e9256
Update .changeset/violet-snails-obey.md
caseyisonit Apr 24, 2025
88f2105
fix: move line-height definition to custom css textfield
castastrophe Apr 24, 2025
60ff0ef
Merge branch 'main' into caseyisonit/swc-669-number-field-icon-overlap
caseyisonit Apr 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/violet-snails-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@spectrum-web-components/number-field': patch
'@spectrum-web-components/textfield': patch
---

The changes included resolve UI issues with number-field by proxy of textfield. The validation icons in number-field no longer overlap the infield buttons. The width of the number-field now calculates accurately and can be modified via `--mod-stepper-width` token as it was before.
12 changes: 12 additions & 0 deletions packages/number-field/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,15 @@ The `<sp-number-field>` component doesn't manage a default value by itself. This
});
});
</script>

## States

Use the `required` attribute to indicate a number field value is required. Dictate the validity or invalidity state of the text entry with the `valid` or `invalid` attributes.

```html
<sp-field-label for="number-1" required>Count</sp-field-label>
<sp-number-field id="number-1" valid value="12343"></sp-number-field>
<br />
<sp-field-label for="number-2" required>Size</sp-field-label>
<sp-number-field id="number-2" invalid value="15212"></sp-number-field>
```
2 changes: 1 addition & 1 deletion packages/number-field/src/NumberField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron200.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron50.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron75.js';
import '@spectrum-web-components/infield-button/sp-infield-button.js';
import { isAndroid, isIOS, isIPhone } from '@spectrum-web-components/shared';
import { TextfieldBase } from '@spectrum-web-components/textfield';
import styles from './number-field.css.js';
import { isAndroid, isIOS, isIPhone } from '@spectrum-web-components/shared';

export const FRAMES_PER_CHANGE = 5;
// Debounce duration for inserting a `change` event after a batch of `wheel` originating `input` events.
Expand Down
104 changes: 104 additions & 0 deletions packages/number-field/src/number-field.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,38 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/

@import url('@spectrum-web-components/infield-button/src/infield-button.css');
@import url('./spectrum-number-field.css');
@import url('./number-field-overrides.css');

:host {
Copy link
Contributor

@nikkimk nikkimk Apr 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: @caseyisonit do you want to make a note about the PR that removed this and why it is being added back in?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! great callout!

This block was removed in a fast-follow but broke the default sizing or number field along with removing the modifiable width on number-field. This is not delivered from CSS and thus needs to remain in our stylesheet.

inline-size: var(
--mod-stepper-width,
calc(
var(--mod-stepper-height, var(--spectrum-stepper-height)) *
var(
--mod-stepper-min-width-multiplier,
var(--spectrum-text-field-minimum-width-multiplier)
) +
var(
--mod-stepper-button-width,
var(--spectrum-stepper-button-width)
) +
var(
--mod-stepper-border-width,
var(--spectrum-stepper-border-width)
) * 2
)
);
}

#textfield {
inline-size: 100%;
}

.input {
font-variant-numeric: tabular-nums;
line-height: var(--spectrum-textfield-height);
}

:host([readonly]) .buttons {
Expand Down Expand Up @@ -53,9 +80,86 @@ governing permissions and limitations under the License.
);
}

:host([invalid]:not([hide-stepper])) #textfield .icon {
--mod-textfield-icon-spacing-inline-end-invalid: calc(
var(--system-infield-button-top-width) +
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wasn't sure if there was a better token to use for the width of the stepper buttons to use in the calc

var(--spectrum-textfield-icon-spacing-inline-end-invalid)
);
}

:host([valid]:not([hide-stepper])) #textfield .icon {
--mod-textfield-icon-spacing-inline-end-valid: calc(
var(--system-infield-button-top-width) +
var(--spectrum-textfield-icon-spacing-inline-end-valid)
);
}

:host(:not([hide-stepper])) #textfield {
--mod-stepper-width: calc(
var(--spectrum-stepper-height) *
var(
--mod-stepper-min-width-multiplier,
var(--spectrum-text-field-minimum-width-multiplier)
) + var(--spectrum-stepper-button-width) * 2 +
var(
--mod-stepper-border-width,
var(--spectrum-stepper-border-width)
) * 2
);
}

:host([invalid]) .input {
padding-inline-end: calc(
var(
--mod-textfield-icon-spacing-inline-start-valid,
var(--spectrum-textfield-icon-spacing-inline-start-valid)
) +
var(
--mod-textfield-icon-size-valid,
var(--spectrum-textfield-icon-size-valid)
) +
var(
--mod-textfield-icon-spacing-inline-end-valid,
var(--spectrum-textfield-icon-spacing-inline-end-valid)
) -
var(
--mod-textfield-border-width,
var(--spectrum-textfield-border-width)
)
);
}

:host([focused]:not([disabled])) #textfield:hover {
--mod-stepper-buttons-border-color-focus-hover: var(
--mod-stepper-border-color-focus-hover,
var(--spectrum-stepper-border-color-focus-hover)
);
}

:host([invalid]:not([hide-stepper])) #textfield .icon {
--mod-textfield-icon-spacing-inline-end-invalid: calc(
var(--spectrum-infield-button-width) +
var(--spectrum-textfield-icon-spacing-inline-end-invalid)
);
}

:host([valid]:not([hide-stepper])) #textfield .icon {
--mod-textfield-icon-spacing-inline-end-valid: calc(
var(--spectrum-infield-button-width) +
var(--spectrum-textfield-icon-spacing-inline-end-valid)
);
}

:host(:not([hide-stepper])) {
--mod-stepper-width: calc(
var(--spectrum-textfield-height) *
var(
--mod-stepper-min-width-multiplier,
var(--spectrum-text-field-minimum-width-multiplier)
) + var(--spectrum-infield-button-width) * 2 +
var(
--mod-stepper-border-width,
var(--spectrum-stepper-border-width)
) * 2
);
}
40 changes: 29 additions & 11 deletions packages/number-field/stories/number-field.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ governing permissions and limitations under the License.
import { html, TemplateResult } from '@spectrum-web-components/base';
import { ifDefined } from '@spectrum-web-components/base/src/directives.js';

import '@spectrum-web-components/number-field/sp-number-field.js';
import '@spectrum-web-components/field-label/sp-field-label.js';
import { spreadProps } from '../../../test/lit-helpers.js';
import '@spectrum-web-components/number-field/sp-number-field.js';
import '@spectrum-web-components/slider/sp-slider.js';
import { NumberField } from '@spectrum-web-components/number-field/src/NumberField.js';
import { spreadProps } from '../../../test/lit-helpers.js';

export default {
title: 'Number Field',
Expand Down Expand Up @@ -208,7 +209,7 @@ export const Default = (args: StoryArgs = {}): TemplateResult => {
onInput((event.target as NumberField).value)}
@change=${(event: Event) =>
onChange((event.target as NumberField).value)}
style=${ifDefined(args.quiet ? undefined : 'width: 150px')}
style=${ifDefined(args.quiet ? undefined : '')}
></sp-number-field>
`;
};
Expand Down Expand Up @@ -239,7 +240,6 @@ export const decimals = (args: StoryArgs): TemplateResult => {
</sp-field-label>
<sp-number-field
id="decimals"
style="width: 200px"
...=${spreadProps(args)}
@change=${args.onChange}
@input=${args.onInput}
Expand Down Expand Up @@ -268,7 +268,6 @@ export const germanDecimals = (args: StoryArgs): TemplateResult => {
<sp-theme lang="de" dir="${currentDir}">
<sp-number-field
id="decimals"
style="width: 200px"
...=${spreadProps(args)}
@change=${args.onChange}
@input=${args.onInput}
Expand All @@ -291,7 +290,6 @@ export const percents = (args: StoryArgs = {}): TemplateResult => {
<sp-field-label for="percents">Enter a percentage</sp-field-label>
<sp-number-field
id="percents"
style="width: 200px"
...=${spreadProps(args)}
@change=${args.onChange}
.formatOptions=${{
Expand All @@ -310,7 +308,6 @@ export const currency = (args: StoryArgs = {}): TemplateResult => {
return html`
<sp-field-label for="currency">Enter a value in Euros</sp-field-label>
<sp-number-field
style="width: 200px"
...=${spreadProps(args)}
@change=${args.onChange}
.formatOptions=${{
Expand All @@ -332,7 +329,6 @@ export const units = (args: StoryArgs): TemplateResult => {
<sp-field-label for="units">Enter a lengths in inches</sp-field-label>
<sp-number-field
id="units"
style="width: 200px"
...=${spreadProps(args)}
@change=${args.onChange}
.formatOptions=${{
Expand All @@ -353,7 +349,6 @@ export const pixels = (args: StoryArgs): TemplateResult => {
<sp-field-label for="units">Enter a lengths in pixels</sp-field-label>
<sp-number-field
id="units"
style="width: 200px"
.formatOptions=${{
style: 'unit',
unit: 'px',
Expand All @@ -374,7 +369,6 @@ export const minMax = (args: StoryArgs): TemplateResult => html`
</sp-field-label>
<sp-number-field
id="min-max"
style="width: 200px"
...=${spreadProps(args)}
@change=${args.onChange}
></sp-number-field>
Expand Down Expand Up @@ -454,6 +448,31 @@ readOnly.args = {
readonly: true,
value: '15',
};
export const validationIcons = (args: StoryArgs): TemplateResult => {
return html`
<sp-field-label for="invalidHiddenStepper">
Invalid Number Field without Stepper
</sp-field-label>
<sp-number-field
id="invalidHiddenStepper"
...=${spreadProps(args)}
@change=${args.onChange}
></sp-number-field>
<sp-field-label for="validStepper">
Valid Number Field with Stepper
</sp-field-label>
<sp-number-field id="validStepper" valid></sp-number-field>
<sp-field-label for="invalidStepper">
Invalid Number Field with Stepper
</sp-field-label>
<sp-number-field id="invalidStepper" invalid></sp-number-field>
`;
};
validationIcons.args = {
invalid: true,
value: '15',
hideStepper: true,
};

export const ScrollingContainer = (args: StoryArgs = {}): TemplateResult => {
const onChange =
Expand Down Expand Up @@ -490,7 +509,6 @@ export const ScrollingContainer = (args: StoryArgs = {}): TemplateResult => {
onInput((event.target as NumberField).value)}
@change=${(event: Event) =>
onChange((event.target as NumberField).value)}
style="width: 150px"
></sp-number-field>
<p>
This box should not scroll when the focus is inside the
Expand Down
78 changes: 0 additions & 78 deletions packages/textfield/src/spectrum-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,84 +45,6 @@ export default {
},
// Default to `size='m'` without needing the attribute
converter.classToHost('spectrum-Textfield--sizeM'),
{
find: [builder.class('spectrum-Textfield--sizeS')],
replace: [
{
replace: builder.id('textfield'),
},
{
replace: builder.attribute('size', 's'),
},
],
},
{
find: [builder.class('spectrum-Textfield--sizeL')],
replace: [
{
replace: builder.id('textfield'),
},
{
replace: builder.attribute('size', 'l'),
},
],
},
{
find: [builder.class('spectrum-Textfield--sizeXL')],
replace: [
{
replace: builder.id('textfield'),
},
{
replace: builder.attribute('size', 'xl'),
},
],
},
{
// .spectrum-Textfield--quiet:after
find: [
builder.class('spectrum-Textfield--quiet'),
builder.pseudoElement('after'),
],
// :host([quiet]) #textfield:after {
replace: [
{
replace: builder.attribute('quiet'),
},
{
replace: builder.id('textfield'),
},
{
replace: builder.pseudoElement('after'),
},
],
},
{
// .spectrum-Textfield--quiet.is-keyboardFocused:after
find: [
builder.class('spectrum-Textfield--quiet'),
builder.class('is-keyboardFocused'),
builder.pseudoElement('after'),
],
// :host([quiet][focused]) #textfield:after
replace: [
{
replace: builder.attribute('quiet'),
},
{
replace: builder.attribute('focused'),
},
{
replace: builder.combinator(' '),
},
{
replace: builder.id('textfield'),
},
{
replace: builder.pseudoElement('after'),
},
],
},
...converter.enumerateAttributes(
[
['spectrum-Textfield--sizeS', 's'],
Expand Down
Loading
Loading