Skip to content

Commit

Permalink
ComboBox fixes (#3993)
Browse files Browse the repository at this point in the history
  • Loading branch information
joshwooding authored Aug 16, 2024
1 parent b1d730d commit 3045c38
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 79 deletions.
7 changes: 7 additions & 0 deletions .changeset/twelve-glasses-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@salt-ds/core": patch
---

- Fixed ComboBox breaking when `inputRef` is used.
- Fixed ComboBox having an incorrect focus ring color when validation is applied.
- Fixed ComboBox having incorrect active styling.
2 changes: 1 addition & 1 deletion packages/core/src/combo-box/ComboBox.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
.saltComboBox-focused {
outline: var(--salt-focused-outline);
outline: var(--saltPillInput-outline, var(--salt-focused-outlineWidth) var(--salt-focused-outlineStyle) var(--pillInput-outlineColor));
}
4 changes: 3 additions & 1 deletion packages/core/src/combo-box/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export const ComboBox = forwardRef(function ComboBox<Item>(
onOpenChange,
onChange,
open,
inputRef: inputRefProp,
inputProps: inputPropsProp,
variant = "primary",
onKeyDown,
Expand Down Expand Up @@ -106,6 +107,7 @@ export const ComboBox = forwardRef(function ComboBox<Item>(
const disabled = Boolean(disabledProp) || formFieldDisabled;
const readOnly = Boolean(readOnlyProp) || formFieldReadOnly;
const inputRef = useRef<HTMLInputElement>(null);
const handleInputRef = useForkRef(inputRef, inputRefProp);

const listControl = useComboBox<Item>({
open,
Expand Down Expand Up @@ -447,7 +449,7 @@ export const ComboBox = forwardRef(function ComboBox<Item>(
}}
aria-activedescendant={activeState?.id}
variant={variant}
inputRef={inputRef}
inputRef={handleInputRef}
value={valueState}
ref={handleRef}
bordered={bordered}
Expand Down
142 changes: 71 additions & 71 deletions packages/core/src/pill-input/PillInput.css
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
/* Style applied to the root element */
.saltPillInput {
--input-border: none;
--input-borderColor: var(--salt-editable-borderColor);
--input-borderStyle: var(--salt-editable-borderStyle);
--input-outlineColor: var(--salt-focused-outlineColor);
--input-borderWidth: var(--salt-size-border);
--pillInput-border: none;
--pillInput-borderColor: var(--salt-editable-borderColor);
--pillInput-borderStyle: var(--salt-editable-borderStyle);
--pillInput-outlineColor: var(--salt-focused-outlineColor);
--pillInput-borderWidth: var(--salt-size-border);

align-items: center;
background: var(--saltInput-background, var(--input-background));
color: var(--saltInput-color, var(--salt-content-primary-foreground));
background: var(--saltPillInput-background, var(--pillInput-background));
border-radius: var(--salt-palette-corner-weak, 0);
border: var(--pillInput-border);
color: var(--saltPillInput-color, var(--salt-content-primary-foreground));
display: inline-flex;
font-family: var(--salt-text-fontFamily);
font-size: var(--saltInput-fontSize, var(--salt-text-fontSize));
line-height: var(--saltInput-lineHeight, var(--salt-text-lineHeight));
min-height: var(--saltInput-minHeight, var(--salt-size-base));
min-width: var(--saltInput-minWidth, 4em);
padding-left: var(--saltInput-paddingLeft, var(--salt-spacing-100));
padding-right: var(--saltInput-paddingRight, var(--salt-spacing-100));
font-size: var(--saltPillInput-fontSize, var(--salt-text-fontSize));
line-height: var(--saltPillInput-lineHeight, var(--salt-text-lineHeight));
min-height: var(--saltPillInput-minHeight, var(--salt-size-base));
min-width: var(--saltPillInput-minWidth, 4em);
padding-left: var(--saltPillInput-paddingLeft, var(--salt-spacing-100));
padding-right: var(--saltPillInput-paddingRight, var(--salt-spacing-100));
position: relative;
width: 100%;
box-sizing: border-box;
border-radius: var(--salt-palette-corner-weak, 0);
border: var(--input-border);
overflow: hidden;
}

Expand All @@ -34,71 +34,71 @@
}

.saltPillInput:hover {
--input-borderStyle: var(--salt-editable-borderStyle-hover);
--input-borderColor: var(--salt-editable-borderColor-hover);
--pillInput-borderStyle: var(--salt-editable-borderStyle-hover);
--pillInput-borderColor: var(--salt-editable-borderColor-hover);

background: var(--saltInput-background-hover, var(--input-background-hover));
background: var(--saltPillInput-background-hover, var(--pillInput-background-hover));
cursor: var(--salt-editable-cursor-hover);
}

.saltPillInput:active {
--input-borderColor: var(--salt-editable-borderColor-active);
--input-borderStyle: var(--salt-editable-borderStyle-active);
--input-borderWidth: var(--salt-editable-borderWidth-active);
--pillInput-borderColor: var(--salt-editable-borderColor-active);
--pillInput-borderStyle: var(--salt-editable-borderStyle-active);
--pillInput-borderWidth: var(--salt-editable-borderWidth-active);

background: var(--saltInput-background-active, var(--input-background-active));
background: var(--saltPillInput-background-active, var(--pillInput-background-active));
cursor: var(--salt-editable-cursor-active);
}

/* Class applied if `variant="primary"` */
.saltPillInput-primary {
--input-background: var(--salt-editable-primary-background);
--input-background-active: var(--salt-editable-primary-background-active);
--input-background-hover: var(--salt-editable-primary-background-hover);
--input-background-disabled: var(--salt-editable-primary-background-disabled);
--input-background-readonly: var(--salt-editable-primary-background-readonly);
--pillInput-background: var(--salt-editable-primary-background);
--pillInput-background-active: var(--salt-editable-primary-background-active);
--pillInput-background-hover: var(--salt-editable-primary-background-hover);
--pillInput-background-disabled: var(--salt-editable-primary-background-disabled);
--pillInput-background-readonly: var(--salt-editable-primary-background-readonly);
}

/* Class applied if `variant="secondary"` */
.saltPillInput-secondary {
--input-background: var(--salt-editable-secondary-background);
--input-background-active: var(--salt-editable-secondary-background-active);
--input-background-hover: var(--salt-editable-secondary-background-active);
--input-background-disabled: var(--salt-editable-secondary-background-disabled);
--input-background-readonly: var(--salt-editable-secondary-background-readonly);
--pillInput-background: var(--salt-editable-secondary-background);
--pillInput-background-active: var(--salt-editable-secondary-background-active);
--pillInput-background-hover: var(--salt-editable-secondary-background-active);
--pillInput-background-disabled: var(--salt-editable-secondary-background-disabled);
--pillInput-background-readonly: var(--salt-editable-secondary-background-readonly);
}

/* Style applied to input if `validationState="error"` */
.saltPillInput-error,
.saltPillInput-error:hover {
--input-background: var(--salt-status-error-background);
--input-background-active: var(--salt-status-error-background);
--input-background-hover: var(--salt-status-error-background);
--input-borderColor: var(--salt-status-error-borderColor);
--input-outlineColor: var(--salt-status-error-borderColor);
--input-background-readonly: var(--salt-status-error-background);
--pillInput-background: var(--salt-status-error-background);
--pillInput-background-active: var(--salt-status-error-background);
--pillInput-background-hover: var(--salt-status-error-background);
--pillInput-borderColor: var(--salt-status-error-borderColor);
--pillInput-outlineColor: var(--salt-status-error-borderColor);
--pillInput-background-readonly: var(--salt-status-error-background);
}

/* Style applied to input if `validationState="warning"` */
.saltPillInput-warning,
.saltPillInput-warning:hover {
--input-background: var(--salt-status-warning-background);
--input-background-active: var(--salt-status-warning-background);
--input-background-hover: var(--salt-status-warning-background);
--input-borderColor: var(--salt-status-warning-borderColor);
--input-outlineColor: var(--salt-status-warning-borderColor);
--input-background-readonly: var(--salt-status-warning-background);
--pillInput-background: var(--salt-status-warning-background);
--pillInput-background-active: var(--salt-status-warning-background);
--pillInput-background-hover: var(--salt-status-warning-background);
--pillInput-borderColor: var(--salt-status-warning-borderColor);
--pillInput-outlineColor: var(--salt-status-warning-borderColor);
--pillInput-background-readonly: var(--salt-status-warning-background);
}

/* Style applied to input if `validationState="success"` */
.saltPillInput-success,
.saltPillInput-success:hover {
--input-background: var(--salt-status-success-background);
--input-background-active: var(--salt-status-success-background);
--input-background-hover: var(--salt-status-success-background);
--input-borderColor: var(--salt-status-success-borderColor);
--input-outlineColor: var(--salt-status-success-borderColor);
--input-background-readonly: var(--salt-status-success-background);
--pillInput-background: var(--salt-status-success-background);
--pillInput-background-active: var(--salt-status-success-background);
--pillInput-background-hover: var(--salt-status-success-background);
--pillInput-borderColor: var(--salt-status-success-borderColor);
--pillInput-outlineColor: var(--salt-status-success-borderColor);
--pillInput-background-readonly: var(--salt-status-success-background);
}

/* Style applied to inner input component */
Expand All @@ -112,12 +112,12 @@
flex: 1;
font: inherit;
height: 100%;
letter-spacing: var(--saltInput-letterSpacing, 0);
letter-spacing: var(--saltPillInput-letterSpacing, 0);
margin: 0;
min-width: 0;
overflow: hidden;
padding: 0;
text-align: var(--input-textAlign);
text-align: var(--pillInput-textAlign);
width: 100%;
}

Expand All @@ -140,31 +140,31 @@
/* Styling when focused */
.saltPillInput-focused,
.saltPillInput-focused:hover {
--input-borderColor: var(--input-outlineColor);
--input-borderWidth: var(--salt-editable-borderWidth-active);
--pillInput-borderColor: var(--pillInput-outlineColor);
--pillInput-borderWidth: var(--salt-editable-borderWidth-active);

outline: var(--saltInput-outline, var(--salt-focused-outlineWidth) var(--salt-focused-outlineStyle) var(--input-outlineColor));
outline: var(--saltPillInput-outline, var(--salt-focused-outlineWidth) var(--salt-focused-outlineStyle) var(--pillInput-outlineColor));
}

/* Style applied if `readOnly={true}` */
.saltPillInput.saltPillInput-readOnly {
--input-borderColor: var(--salt-editable-borderColor-readonly);
--input-borderStyle: var(--salt-editable-borderStyle-readonly);
--input-borderWidth: var(--salt-size-border);
--pillInput-borderColor: var(--salt-editable-borderColor-readonly);
--pillInput-borderStyle: var(--salt-editable-borderStyle-readonly);
--pillInput-borderWidth: var(--salt-size-border);

background: var(--input-background-readonly);
background: var(--pillInput-background-readonly);
cursor: var(--salt-editable-cursor-readonly);
}

/* Styling when focused if `disabled={true}` */
.saltPillInput-focused.saltPillInput-disabled {
--input-borderWidth: var(--salt-size-border);
--pillInput-borderWidth: var(--salt-size-border);
outline: none;
}

/* Styling when focused if `readOnly={true}` */
.saltPillInput-focused.saltPillInput-readOnly {
--input-borderWidth: var(--salt-size-border);
--pillInput-borderWidth: var(--salt-size-border);
}

/* Style applied to selected input if `disabled={true}` */
Expand All @@ -176,40 +176,40 @@
.saltPillInput.saltPillInput-disabled,
.saltPillInput.saltPillInput-disabled:hover,
.saltPillInput.saltPillInput-disabled:active {
--input-borderColor: var(--salt-editable-borderColor-disabled);
--input-borderStyle: var(--salt-editable-borderStyle-disabled);
--input-borderWidth: var(--salt-size-border);
--pillInput-borderColor: var(--salt-editable-borderColor-disabled);
--pillInput-borderStyle: var(--salt-editable-borderStyle-disabled);
--pillInput-borderWidth: var(--salt-size-border);

background: var(--input-background-disabled);
background: var(--pillInput-background-disabled);
cursor: var(--salt-editable-cursor-disabled);
color: var(--saltInput-color-disabled, var(--salt-content-primary-foreground-disabled));
color: var(--saltPillInput-color-disabled, var(--salt-content-primary-foreground-disabled));
}

.saltPillInput-activationIndicator {
left: 0;
bottom: 0;
width: 100%;
position: absolute;
border-bottom: var(--input-borderWidth) var(--input-borderStyle) var(--input-borderColor);
border-bottom: var(--pillInput-borderWidth) var(--pillInput-borderStyle) var(--pillInput-borderColor);
}

/* Style applied if `bordered={true}` */
.saltPillInput.saltPillInput-bordered {
--input-border: var(--salt-size-border) var(--salt-container-borderStyle) var(--input-borderColor);
--input-borderWidth: 0;
--pillInput-border: var(--salt-size-border) var(--salt-container-borderStyle) var(--pillInput-borderColor);
--pillInput-borderWidth: 0;
}

/* Style applied if focused or active when `bordered={true}` */
.saltPillInput-bordered.saltPillInput-focused,
.saltPillInput-bordered:active {
--input-borderWidth: var(--salt-editable-borderWidth-active);
--pillInput-borderWidth: var(--salt-editable-borderWidth-active);
}

/* Styling when focused if `disabled={true}` or `readOnly={true}` when `bordered={true}` */
.saltPillInput-bordered.saltPillInput-readOnly,
.saltPillInput-bordered.saltPillInput-disabled:hover,
.saltPillInput-bordered.saltPillInput-disabled.saltPillInput-focused {
--input-borderWidth: 0;
--pillInput-borderWidth: 0;
}

/* Style applied to start adornments */
Expand Down
17 changes: 17 additions & 0 deletions packages/core/src/pill-input/PillInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { clsx } from "clsx";
import {
type ChangeEvent,
type ComponentPropsWithoutRef,
type FocusEvent,
type ForwardedRef,
type InputHTMLAttributes,
type KeyboardEvent,
Expand Down Expand Up @@ -140,6 +141,7 @@ export const PillInput = forwardRef(function PillInput(
const isReadOnly = readOnlyProp || formFieldReadOnly;
const validationStatus = formFieldValidationStatus ?? validationStatusProp;

const [focused, setFocused] = useState(false);
const [focusedPillIndex, setFocusedPillIndex] = useState(-1);

const isEmptyReadOnly = isReadOnly && !defaultValueProp && !valueProp;
Expand All @@ -148,6 +150,8 @@ export const PillInput = forwardRef(function PillInput(
const {
"aria-describedby": inputDescribedBy,
"aria-labelledby": inputLabelledBy,
onBlur,
onFocus,
onChange,
required: inputPropsRequired,
onKeyDown: inputOnKeyDown,
Expand Down Expand Up @@ -238,6 +242,16 @@ export const PillInput = forwardRef(function PillInput(
inputRef.current?.focus();
};

const handleBlur = (event: FocusEvent<HTMLInputElement>) => {
onBlur?.(event);
setFocused(false);
};

const handleFocus = (event: FocusEvent<HTMLInputElement>) => {
onFocus?.(event);
setFocused(true);
};

const inputStyle = {
"--input-textAlign": textAlign,
...style,
Expand All @@ -249,6 +263,7 @@ export const PillInput = forwardRef(function PillInput(
withBaseName(),
withBaseName(variant),
{
[withBaseName("focused")]: !isDisabled && focused,
[withBaseName("disabled")]: isDisabled,
[withBaseName("readOnly")]: isReadOnly,
[withBaseName("truncate")]: truncate,
Expand Down Expand Up @@ -321,7 +336,9 @@ export const PillInput = forwardRef(function PillInput(
ref={handleInputRef}
role={role}
tabIndex={isDisabled ? -1 : 0}
onBlur={handleBlur}
onChange={handleChange}
onFocus={!isDisabled ? handleFocus : undefined}
onKeyDown={handleKeyDown}
placeholder={placeholder}
value={value}
Expand Down
19 changes: 13 additions & 6 deletions site/src/examples/combo-box/Disabled.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { ComboBox, Option } from "@salt-ds/core";
import { ComboBox, Option, StackLayout } from "@salt-ds/core";
import type { ReactElement } from "react";
import { shortColorData } from "./exampleData";

export const Disabled = (): ReactElement => {
return (
<ComboBox style={{ width: "266px" }} defaultSelected={["Yellow"]} disabled>
{shortColorData.map((color) => (
<Option value={color} key={color} />
))}
</ComboBox>
<StackLayout style={{ width: "266px" }}>
<ComboBox defaultSelected={["Yellow"]} disabled>
{shortColorData.map((color) => (
<Option value={color} key={color} />
))}
</ComboBox>
<ComboBox defaultSelected={["Yellow", "Green"]} disabled multiselect>
{shortColorData.map((color) => (
<Option value={color} key={color} />
))}
</ComboBox>
</StackLayout>
);
};

0 comments on commit 3045c38

Please sign in to comment.