Skip to content

Commit 55f4626

Browse files
feat: floating labels - more screen tests + styling fixes (#2762)
## Description: Closes: #2528 This contains the rest of the screen tests for the floating labels, as well as styling fixes from design review and found during the making of the more tests. ## Definition of Reviewable: - [x] Documentation is created/updated - [x] Stories (features, a11y) are created/updated - [x] relevant tickets are linked
1 parent ede26bc commit 55f4626

17 files changed

Lines changed: 1305 additions & 393 deletions

.changeset/wild-badgers-stand.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@solid-design-system/components': minor
3+
'@solid-design-system/docs': minor
4+
---
5+
6+
- Adding more screenshot tests related to the attribute `floating-label` for the components `sd-input`, `sd-select`, `sd-textarea` and `sd-datepicker`.
7+
- Fixed small styling issues related to text sizing, label placement, and `sd-input` use cases for different types of inputs.
8+
- Lastly, removed the 'sm' size when using floating labels, defaulting this size to be the same as 'md' size.

packages/components/src/components/combobox/combobox.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,11 @@ export default class SdCombobox extends SolidElement implements SolidFormControl
10231023
this.emit('sd-after-hide');
10241024
}
10251025

1026+
@watch(['size', 'floatingLabel'])
1027+
handleSizeChange() {
1028+
this.size = this.floatingLabel && this.size === 'sm' ? 'md' : this.size;
1029+
}
1030+
10261031
async show() {
10271032
if (this.open || this.disabled || this.visuallyDisabled) {
10281033
this.open = false;
@@ -1240,7 +1245,7 @@ export default class SdCombobox extends SolidElement implements SolidFormControl
12401245
'absolute left-4 z-20 pointer-events-none transition-all duration-200',
12411246
hasIconLeft ? floatingLabelHorizontalAlignmentWithIconLeft : 'left-4',
12421247
!isFloatingLabelActive
1243-
? 'top-1/2 -translate-y-1/2 text-base'
1248+
? 'top-1/2 -translate-y-1/2'
12441249
: this.size === 'lg'
12451250
? 'top-2 text-xs'
12461251
: 'top-1 text-xs',

packages/components/src/components/datepicker/datepicker.ts

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,11 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
426426
}
427427
}
428428

429+
@watch(['size', 'floatingLabel'])
430+
handleSizeChange() {
431+
this.size = this.floatingLabel && this.size === 'sm' ? 'md' : this.size;
432+
}
433+
429434
get validity() {
430435
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
431436
const defaultValidity: ValidityState = { valid: true } as ValidityState;
@@ -1957,6 +1962,37 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
19571962
this.visuallyDisabled && 'cursor-not-allowed'
19581963
)}
19591964
>
1965+
${hasLabel && this.floatingLabel
1966+
? html`
1967+
<label
1968+
id="label"
1969+
part="form-control-floating-label"
1970+
class=${cx(
1971+
'absolute left-4 z-20 pointer-events-none transition-all duration-200',
1972+
!isFloatingLabelActive
1973+
? 'top-1/2 -translate-y-1/2'
1974+
: this.size === 'lg'
1975+
? 'top-2 text-xs'
1976+
: 'top-1 text-xs',
1977+
isFloatingLabelActive && 'mt-1'
1978+
)}
1979+
for="input"
1980+
>
1981+
<span
1982+
class=${cx(
1983+
'leading-none',
1984+
(this.visuallyDisabled || this.disabled) && 'text-neutral-500',
1985+
isFloatingLabelActive &&
1986+
!this.visuallyDisabled &&
1987+
!this.disabled &&
1988+
'form-control--filled__floating-label-color-text'
1989+
)}
1990+
>
1991+
${this.label}
1992+
</span>
1993+
</label>
1994+
`
1995+
: null}
19601996
<div
19611997
part="border"
19621998
class=${cx(
@@ -1969,39 +2005,7 @@ export default class SdDatepicker extends SolidElement implements SolidFormContr
19692005
? 'rounded-bl-none rounded-br-none'
19702006
: 'rounded-tl-none rounded-tr-none')
19712007
)}
1972-
>
1973-
${hasLabel && this.floatingLabel
1974-
? html`
1975-
<label
1976-
id="label"
1977-
part="form-control-floating-label"
1978-
class=${cx(
1979-
'absolute left-4 z-20 pointer-events-none transition-all duration-200',
1980-
!isFloatingLabelActive
1981-
? 'top-1/2 -translate-y-1/2 text-base'
1982-
: this.size === 'lg'
1983-
? 'top-2 text-xs'
1984-
: 'top-1 text-xs',
1985-
isFloatingLabelActive && 'mt-1'
1986-
)}
1987-
for="input"
1988-
>
1989-
<span
1990-
class=${cx(
1991-
'leading-none',
1992-
(this.visuallyDisabled || this.disabled) && 'text-neutral-500',
1993-
isFloatingLabelActive &&
1994-
!this.visuallyDisabled &&
1995-
!this.disabled &&
1996-
'form-control--filled__floating-label-color-text'
1997-
)}
1998-
>
1999-
${this.label}
2000-
</span>
2001-
</label>
2002-
`
2003-
: null}
2004-
</div>
2008+
></div>
20052009
<sd-popup
20062010
@sd-current-placement=${this.handleCurrentPlacement}
20072011
class=${cx(

packages/components/src/components/input/input.ts

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,13 @@ export default class SdInput extends SolidElement implements SolidFormControl {
9797
* @internal
9898
*/
9999
@state() showValidStyle = false;
100+
100101
/** @internal */
101102
@state() showInvalidStyle = false;
102103

104+
/** @internal */
105+
@state() isClickableIconFocused = false;
106+
103107
/**
104108
* The type of input. Works the same as a native `<input>` element, but only a subset of types are supported. Defaults
105109
* to `text`.
@@ -361,6 +365,14 @@ export default class SdInput extends SolidElement implements SolidFormControl {
361365
this.passwordVisible = !this.passwordVisible;
362366
}
363367

368+
private handleClickableIconFocusIn() {
369+
this.isClickableIconFocused = true;
370+
}
371+
372+
private handleClickableIconFocusOut() {
373+
this.isClickableIconFocused = false;
374+
}
375+
364376
private isDecrementDisabled() {
365377
if (this.disabled || this.readonly) {
366378
return true;
@@ -422,6 +434,11 @@ export default class SdInput extends SolidElement implements SolidFormControl {
422434
this.formControlController.updateValidity();
423435
}
424436

437+
@watch(['size', 'floatingLabel'])
438+
handleSizeChange() {
439+
this.size = this.floatingLabel && this.size === 'sm' ? 'md' : this.size;
440+
}
441+
425442
/** Sets focus on the input. */
426443
focus(options?: FocusOptions) {
427444
this.input.focus(options);
@@ -520,7 +537,8 @@ export default class SdInput extends SolidElement implements SolidFormControl {
520537
const hasTooltip = !!slots['tooltip'];
521538
const hasIconLeft = slots['left'];
522539
const hasValue = this.value !== null && String(this.value).length > 0;
523-
const isFloatingLabelActive = this.floatingLabel && hasLabel && (this.hasFocus || hasValue);
540+
const isFloatingLabelActive =
541+
this.floatingLabel && hasLabel && (this.hasFocus || hasValue || this.isClickableIconFocused);
524542

525543
// Hierarchy of input states:
526544
const inputState = this.disabled
@@ -615,6 +633,7 @@ export default class SdInput extends SolidElement implements SolidFormControl {
615633
part="base"
616634
class=${cx(
617635
'px-4 flex flex-row items-center rounded-default transition-colors ease-in-out duration-medium hover:duration-fast',
636+
this.floatingLabel && 'has-floating-label',
618637
// States
619638
!this.disabled && !this.readonly && !this.visuallyDisabled ? 'hover:bg-neutral-200' : '',
620639
this.readonly ? 'bg-neutral-100' : 'bg-white',
@@ -699,7 +718,7 @@ export default class SdInput extends SolidElement implements SolidFormControl {
699718
'pointer-events-none leading-none',
700719
!this.readonly && 'transition-all duration-medium ease-out',
701720
!isFloatingLabelActive || (!hasValue && (this.readonly || this.visuallyDisabled))
702-
? 'text-base'
721+
? textSize
703722
: 'text-xs',
704723
(this.visuallyDisabled || this.disabled) && 'text-neutral-500',
705724
isFloatingLabelActive &&
@@ -741,9 +760,16 @@ export default class SdInput extends SolidElement implements SolidFormControl {
741760
<button
742761
aria-label=${this.localize.term(this.passwordVisible ? 'hidePassword' : 'showPassword')}
743762
part="password-toggle-button"
744-
class=${cx('flex items-center sd-interactive', iconMarginLeft)}
763+
class=${cx(
764+
'flex items-center sd-interactive',
765+
iconMarginLeft,
766+
this.floatingLabel && !isFloatingLabelActive && 'hide-password-toggle'
767+
)}
745768
type="button"
746769
@click=${this.handlePasswordToggle}
770+
@mousedown=${this.handleClickableIconFocusIn}
771+
@focus=${() => this.handleClickableIconFocusIn()}
772+
@blur=${this.handleClickableIconFocusOut}
747773
>
748774
${this.passwordVisible
749775
? html`
@@ -831,6 +857,9 @@ export default class SdInput extends SolidElement implements SolidFormControl {
831857
aria-hidden="true"
832858
${longPress({ start: () => this.handleStepDown(), end: () => this.handleChange() })}
833859
tabindex="-1"
860+
@mousedown=${this.handleClickableIconFocusIn}
861+
@focus=${() => this.handleClickableIconFocusIn()}
862+
@blur=${this.handleClickableIconFocusOut}
834863
>
835864
<slot name="decrement-number-stepper">
836865
<sd-icon
@@ -922,6 +951,8 @@ export default class SdInput extends SolidElement implements SolidFormControl {
922951
/* Hides clock icon for time type. */
923952
input[type='time']::-webkit-calendar-picker-indicator {
924953
background: none;
954+
display: none;
955+
-webkit-appearance: none;
925956
}
926957
927958
details summary::-webkit-details-marker,
@@ -940,6 +971,21 @@ export default class SdInput extends SolidElement implements SolidFormControl {
940971
.stepper-button[disabled] sd-icon {
941972
@apply text-neutral-500;
942973
}
974+
975+
.hide-password-toggle {
976+
display: none;
977+
}
978+
979+
.has-floating-label input[type='date']:not(.has-value),
980+
.has-floating-label input[type='time']:not(.has-value),
981+
.has-floating-label input[type='datetime-local']:not(.has-value) {
982+
color: transparent;
983+
}
984+
.has-floating-label input[type='date']:focus,
985+
.has-floating-label input[type='time']:focus,
986+
.has-floating-label input[type='datetime-local']:focus {
987+
color: inherit;
988+
}
943989
`
944990
];
945991
}

packages/components/src/components/select/select.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,11 @@ export default class SdSelect extends SolidElement implements SolidFormControl {
825825
this.value = incomingValue;
826826
}
827827
828+
@watch(['size', 'floatingLabel'])
829+
handleSizeChange() {
830+
this.size = this.floatingLabel && this.size === 'sm' ? 'md' : this.size;
831+
}
832+
828833
/** Shows the listbox. */
829834
async show() {
830835
if (this.open || this.disabled || this.visuallyDisabled) {
@@ -983,7 +988,7 @@ export default class SdSelect extends SolidElement implements SolidFormControl {
983988
class=${cx(
984989
'absolute left-4 z-20 pointer-events-none transition-all duration-200',
985990
!isFloatingLabelActive
986-
? 'top-1/2 -translate-y-1/2 text-base'
991+
? 'top-1/2 -translate-y-1/2'
987992
: this.size === 'lg'
988993
? 'top-2 text-xs'
989994
: 'top-1 text-xs',
@@ -994,7 +999,6 @@ export default class SdSelect extends SolidElement implements SolidFormControl {
994999
<span
9951000
class=${cx(
9961001
'leading-none',
997-
(this.visuallyDisabled || this.disabled) && 'text-neutral-500',
9981002
isFloatingLabelActive &&
9991003
!this.visuallyDisabled &&
10001004
!this.disabled &&
@@ -1103,7 +1107,7 @@ export default class SdSelect extends SolidElement implements SolidFormControl {
11031107
${this.multiple && this.useTags && this.tags && this.tags.length > 0
11041108
? html` <div
11051109
part="tags"
1106-
class=${cx('flex-grow flex flex-wrap items-center gap-1 min-w-0', this.floatingLabel && 'pt-6')}
1110+
class=${cx('flex-grow flex flex-wrap items-center gap-1 min-w-0', this.floatingLabel && 'pt-4')}
11071111
>
11081112
${this.tags}
11091113
</div>`

packages/components/src/components/textarea/textarea.ts

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,11 @@ export default class SdTextarea extends SolidElement implements SolidFormControl
246246
this.setTextareaHeight();
247247
}
248248

249+
@watch(['size', 'floatingLabel'])
250+
handleSizeChange() {
251+
this.size = this.floatingLabel && this.size === 'sm' ? 'md' : this.size;
252+
}
253+
249254
/** Sets focus on the textarea. */
250255
focus(options?: FocusOptions) {
251256
this.textarea.focus(options);
@@ -388,6 +393,33 @@ export default class SdTextarea extends SolidElement implements SolidFormControl
388393
: null}
389394
390395
<div part="form-control-input" class=${cx('relative h-full w-full', this.disabled && 'cursor-not-allowed')}>
396+
${hasLabel && this.floatingLabel
397+
? html`
398+
<label
399+
id="label"
400+
part="form-control-floating-label"
401+
class=${cx(
402+
'absolute left-4 z-20 pointer-events-none transition-all duration-200',
403+
textSize,
404+
!isFloatingLabelActive ? 'top-2.5' : 'top-2 text-xs'
405+
)}
406+
for="input"
407+
>
408+
<span
409+
class=${cx(
410+
'leading-none',
411+
(this.visuallyDisabled || this.disabled) && 'text-neutral-500',
412+
isFloatingLabelActive &&
413+
!this.visuallyDisabled &&
414+
!this.disabled &&
415+
'form-control--filled__floating-label-color-text'
416+
)}
417+
>
418+
${this.label}
419+
</span>
420+
</label>
421+
`
422+
: null}
391423
<div
392424
part="border"
393425
class=${cx(
@@ -404,39 +436,7 @@ export default class SdTextarea extends SolidElement implements SolidFormControl
404436
default: 'form-control-color-border'
405437
}[textareaState]
406438
)}
407-
>
408-
${hasLabel && this.floatingLabel
409-
? html`
410-
<label
411-
id="label"
412-
part="form-control-floating-label"
413-
class=${cx(
414-
'absolute left-4 z-20 pointer-events-none transition-all duration-200',
415-
textSize,
416-
!isFloatingLabelActive
417-
? 'top-2.5 text-base'
418-
: this.size === 'lg'
419-
? 'top-2 text-xs'
420-
: 'top-1 text-xs'
421-
)}
422-
for="input"
423-
>
424-
<span
425-
class=${cx(
426-
'leading-none',
427-
(this.visuallyDisabled || this.disabled) && 'text-neutral-500',
428-
isFloatingLabelActive &&
429-
!this.visuallyDisabled &&
430-
!this.disabled &&
431-
'form-control--filled__floating-label-color-text'
432-
)}
433-
>
434-
${this.label}
435-
</span>
436-
</label>
437-
`
438-
: null}
439-
</div>
439+
></div>
440440
<div
441441
part="base"
442442
class=${cx(
@@ -461,9 +461,9 @@ export default class SdTextarea extends SolidElement implements SolidFormControl
461461
class=${cx(
462462
'ps-4 flex-grow focus:outline-none bg-transparent placeholder:text-neutral-700 resize-none group-has-[sd-icon]:pe-8',
463463
{
464-
sm: this.floatingLabel ? 'pb-1' : 'py-1',
465-
md: this.floatingLabel ? 'pb-1' : 'py-1',
466-
lg: this.floatingLabel ? 'pb-2' : 'py-2'
464+
sm: isFloatingLabelActive ? 'pb-1' : 'py-2',
465+
md: isFloatingLabelActive ? 'pb-1' : 'py-2',
466+
lg: isFloatingLabelActive ? 'pb-2' : 'py-2'
467467
}[this.size],
468468
this.disabled && 'cursor-not-allowed',
469469
textSize

0 commit comments

Comments
 (0)