Skip to content

Commit

Permalink
Add bordered prop to ComboBox and update ComboBox's read-only s…
Browse files Browse the repository at this point in the history
…tate (#3594)

Co-authored-by: Zhihao Cui <[email protected]>
Co-authored-by: Josh Wooding <[email protected]>
  • Loading branch information
3 people authored Jul 1, 2024
1 parent fc60301 commit b199888
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/added-combobox-UI-fixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@salt-ds/core": patch
---

Updated `ComboBox` to show validation status when it's in a read-only state.
14 changes: 14 additions & 0 deletions .changeset/twelve-rockets-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@salt-ds/core": minor
---

Added `bordered` prop for `ComboBox`. When set, a full border will be applied.

```tsx
<ComboBox bordered>
<Option value="Red" />
<Option value="Blue" />
</ComboBox>
```

Added corner support for theme next.
2 changes: 2 additions & 0 deletions packages/core/src/combo-box/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export const ComboBox = forwardRef(function ComboBox<Item>(
defaultValue,
valueToString = defaultValueToString,
truncate,
bordered = false,
...rest
} = props;

Expand Down Expand Up @@ -439,6 +440,7 @@ export const ComboBox = forwardRef(function ComboBox<Item>(
inputRef={inputRef}
value={valueState}
ref={handleRef}
bordered={bordered}
{...getReferenceProps({
onBlur: handleBlur,
onFocus: handleFocus,
Expand Down
40 changes: 39 additions & 1 deletion packages/core/src/pill-input/PillInput.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* 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);
Expand All @@ -19,6 +20,9 @@
position: relative;
width: 100%;
box-sizing: border-box;
border-radius: var(--salt-palette-corner-weak, 0);
border: var(--input-border);
overflow: hidden;
}

.saltPillInput-truncate {
Expand Down Expand Up @@ -72,6 +76,7 @@
--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);
}

/* Style applied to input if `validationState="warning"` */
Expand All @@ -82,6 +87,7 @@
--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);
}

/* Style applied to input if `validationState="success"` */
Expand All @@ -92,6 +98,7 @@
--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);
}

/* Style applied to inner input component */
Expand Down Expand Up @@ -131,7 +138,8 @@
}

/* Styling when focused */
.saltPillInput-focused {
.saltPillInput-focused,
.saltPillInput-focused:hover {
--input-borderColor: var(--input-outlineColor);
--input-borderWidth: var(--salt-editable-borderWidth-active);

Expand Down Expand Up @@ -185,6 +193,25 @@
border-bottom: var(--input-borderWidth) var(--input-borderStyle) var(--input-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;
}

/* Style applied if focused or active when `bordered={true}` */
.saltPillInput-bordered.saltPillInput-focused,
.saltPillInput-bordered:active {
--input-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;
}

/* Style applied to start adornments */
.saltPillInput-startAdornmentContainer {
align-items: center;
Expand All @@ -210,13 +237,15 @@
.saltPillInput-startAdornmentContainer .saltButton ~ .saltButton {
margin-left: calc(-1 * var(--salt-spacing-50));
}

.saltPillInput-endAdornmentContainer .saltButton ~ .saltButton {
margin-left: calc(-1 * var(--salt-spacing-50));
}

.saltPillInput-startAdornmentContainer .saltButton:first-child {
margin-left: calc(var(--salt-spacing-50) * -1);
}

.saltPillInput-endAdornmentContainer .saltButton:last-child {
margin-right: calc(var(--salt-spacing-50) * -1);
}
Expand All @@ -225,6 +254,7 @@
.saltPillInput-endAdornmentContainer > .saltButton {
--saltButton-padding: calc(var(--salt-spacing-50) - var(--salt-size-border));
--saltButton-height: calc(var(--salt-size-base) - var(--salt-spacing-100));
--saltButton-borderRadius: var(--salt-palette-corner-weaker);
}

.saltPillInput-inputWrapper {
Expand All @@ -240,6 +270,14 @@
box-sizing: border-box;
}

.saltPillInput-bordered > .saltPillInput-endAdornmentContainer {
padding-top: calc(var(--salt-spacing-50) - var(--salt-size-border));
}

.saltPillInput-bordered > .saltPillInput-inputWrapper {
padding: calc(var(--salt-spacing-50) - var(--salt-size-border)) 0;
}

.saltPillInput-pillList {
display: contents;
}
Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/pill-input/PillInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ export interface PillInputProps
variant?: "primary" | "secondary";
hidePillClose?: boolean;
truncate?: boolean;
/** Styling variant with full border. Defaults to false
*/
bordered?: boolean;
}

export const PillInput = forwardRef(function PillInput(
Expand Down Expand Up @@ -104,6 +107,7 @@ export const PillInput = forwardRef(function PillInput(
validationStatus: validationStatusProp,
variant = "primary",
truncate,
bordered = false,
...other
}: PillInputProps,
ref: ForwardedRef<HTMLDivElement>
Expand Down Expand Up @@ -249,6 +253,7 @@ export const PillInput = forwardRef(function PillInput(
[withBaseName("readOnly")]: isReadOnly,
[withBaseName("truncate")]: truncate,
[withBaseName(validationStatus ?? "")]: validationStatus,
[withBaseName("bordered")]: bordered,
},
classNameProp
)}
Expand Down Expand Up @@ -325,7 +330,7 @@ export const PillInput = forwardRef(function PillInput(
required={isRequired}
/>
</div>
{!isDisabled && !isReadOnly && validationStatus && (
{!isDisabled && validationStatus && (
<StatusAdornment status={validationStatus} />
)}
{endAdornment && (
Expand Down
48 changes: 47 additions & 1 deletion packages/core/stories/combo-box/combo-box.qa.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,55 @@ export const ClosedExamples: StoryFn<QAContainerProps> = () => (
</ComboBox>
<FormFieldHelperText>This is some help text</FormFieldHelperText>
</FormField>
<FormField>
<FormFieldLabel>Default bordered example</FormFieldLabel>
<ComboBox bordered>
{first5States.map((state) => (
<Option value={state} key={state}>
{state}
</Option>
))}
</ComboBox>
<FormFieldHelperText>This is some help text</FormFieldHelperText>
</FormField>
<FormField>
<FormFieldLabel>Disabled bordered example</FormFieldLabel>
<ComboBox bordered disabled>
{first5States.map((state) => (
<Option value={state} key={state}>
{state}
</Option>
))}
</ComboBox>
<FormFieldHelperText>This is some help text</FormFieldHelperText>
</FormField>
<FormField>
<FormFieldLabel>Read-only bordered example</FormFieldLabel>
<ComboBox bordered readOnly>
{first5States.map((state) => (
<Option value={state} key={state}>
{state}
</Option>
))}
</ComboBox>
<FormFieldHelperText>This is some help text</FormFieldHelperText>
</FormField>
</QAContainer>
);

ClosedExamples.parameters = {
chromatic: { disableSnapshot: false },
chromatic: {
disableSnapshot: false,
modes: {
theme: {
themeNext: "disabled",
},
themeNext: {
themeNext: "enable",
corner: "rounded",
accent: "teal",
// Ignore headingFont given font is not loaded
},
},
},
};
14 changes: 13 additions & 1 deletion packages/core/stories/combo-box/combo-box.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const Template: StoryFn<ComboBoxProps> = (args) => {
onChange={handleChange}
onSelectionChange={handleSelectionChange}
value={value}
style={{ width: 150 }}
style={{ width: 152 }}
>
{usStates
.filter((state) =>
Expand Down Expand Up @@ -891,3 +891,15 @@ export const ClearSelection: StoryFn<ComboBoxProps> = (args) => {
</ComboBox>
);
};

export const Bordered = () => {
return (
<StackLayout>
<Template bordered />
<Template bordered variant="secondary" />
<Template bordered validationStatus="error" />
<Template bordered validationStatus="warning" />
<Template bordered validationStatus="success" />
</StackLayout>
);
};
9 changes: 9 additions & 0 deletions site/docs/components/combo-box/examples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -192,5 +192,14 @@ Use the `selectOnTab` prop to select the active option from the list on Tab key

In order to clear all the selections, you can pass a [`Button`](../button) to the `endAdornment` prop of the `ComboBox`.

</LivePreview>
<LivePreview componentName="combo-box" exampleName="Bordered">

## Bordered

To style `ComboBox` with a full border, set `bordered={true}`.

We recommend this styling when the field uses the same fill color as the background (i.e., a primary fill color on a primary background).

</LivePreview>
</LivePreviewControls>
20 changes: 20 additions & 0 deletions site/src/examples/combo-box/Bordered.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ReactElement } from "react";
import { ComboBox, Option, StackLayout } from "@salt-ds/core";
import { shortColorData } from "./exampleData";

export const Bordered = (): ReactElement => {
return (
<StackLayout>
<ComboBox bordered style={{ width: "266px" }}>
{shortColorData.map((color) => (
<Option value={color} key={color} />
))}
</ComboBox>
<ComboBox bordered variant="secondary" style={{ width: "266px" }}>
{shortColorData.map((color) => (
<Option value={color} key={color} />
))}
</ComboBox>
</StackLayout>
);
};
1 change: 1 addition & 0 deletions site/src/examples/combo-box/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export * from "./ObjectValues";
export * from "./Truncation";
export * from "./SelectOnTab";
export * from "./ClearSelection";
export * from "./Bordered";

0 comments on commit b199888

Please sign in to comment.