Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions .changeset/afraid-adults-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@cube-dev/ui-kit": patch
---

Replace `noCard` prop with `type` prop in ListBox component. The new `type` prop accepts three values:
- `card` (default): Standard card styling with border and margin
- `plain`: No border, no margin, no radius - suitable for embedded use
- `popover`: No border, but keeps margin and radius - suitable for overlay use
5 changes: 5 additions & 0 deletions .changeset/cuddly-meals-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cube-dev/ui-kit": patch
---

Remove the hardcoded default width for NumberInput.
5 changes: 5 additions & 0 deletions .changeset/tame-bananas-teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cube-dev/ui-kit": patch
---

Fix right padding in NumberInput.
4 changes: 1 addition & 3 deletions src/components/fields/ComboBox/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -929,9 +929,7 @@ function ComboBoxOverlay({
headingStyles={headingStyles}
stateRef={listStateRef}
size="medium"
mods={{
popover: true,
}}
type="popover"
onSelectionChange={onSelectionChange}
>
{children as any}
Expand Down
2 changes: 1 addition & 1 deletion src/components/fields/FilterListBox/FilterListBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const StyledHeaderWithoutBorder = tasty(StyledHeader, {
});

export interface CubeFilterListBoxProps<T>
extends Omit<CubeListBoxProps<T>, 'filter'>,
extends Omit<CubeListBoxProps<T>, 'filter' | 'type'>,
FieldBaseProps {
/** Placeholder text for the search input */
searchPlaceholder?: string;
Expand Down
34 changes: 34 additions & 0 deletions src/components/fields/ListBox/ListBox.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ The `mods` property accepts the following modifiers you can override:
- Array of keys for items that should be disabled
- Disabled items cannot be selected and are visually distinguished

### Visual Styling

**type** (`'card' | 'plain' | 'popover'`, default: `'card'`)
- Controls the visual styling of the ListBox container
- `card`: Standard card styling with border and margin (default)
- `plain`: No border, no margin, no radius - suitable for embedded use
- `popover`: No border, but keeps margin and radius - suitable for overlay use
- Use `plain` when embedding ListBox directly into another component
- Use `popover` when using ListBox inside overlays (Dialog, ComboBox, Picker)

### Focus and Interaction

**focusOnHover** (`boolean`, default: `true`)
Expand Down Expand Up @@ -515,6 +525,30 @@ const [selectedKey, setSelectedKey] = useState('apple');
</ListBox>
```

### Visual Type Variants

<Story of={ListBoxStories.TypeVariants} />

```jsx
{/* Card type (default) - with border and margin */}
<ListBox type="card" label="Select a fruit" selectionMode="single">
<ListBox.Item key="apple">Apple</ListBox.Item>
<ListBox.Item key="banana">Banana</ListBox.Item>
</ListBox>

{/* Plain type - no border, no margin, no radius */}
<ListBox type="plain" label="Select a fruit" selectionMode="single">
<ListBox.Item key="apple">Apple</ListBox.Item>
<ListBox.Item key="banana">Banana</ListBox.Item>
</ListBox>

{/* Popover type - no border, but keeps margin and radius */}
<ListBox type="popover" label="Select a fruit" selectionMode="single">
<ListBox.Item key="apple">Apple</ListBox.Item>
<ListBox.Item key="banana">Banana</ListBox.Item>
</ListBox>
```

### Custom Escape Handling

<Story of={ListBoxStories.EscapeKeyHandling} />
Expand Down
107 changes: 107 additions & 0 deletions src/components/fields/ListBox/ListBox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ const meta: any = {
defaultValue: { summary: 'medium' },
},
},
type: {
options: ['card', 'plain', 'popover'],
control: { type: 'radio' },
description: 'Visual type of the ListBox',
table: {
defaultValue: { summary: 'card' },
},
},
header: {
control: { type: 'text' },
description: 'Custom header content',
Expand Down Expand Up @@ -1392,3 +1400,102 @@ export const AllValuePropsExample: Story = {
},
},
};

export const TypeVariants: Story = {
render: () => (
<Space gap="4x" flow="column">
<div>
<Text preset="t3" weight="600">
Card Type (default)
</Text>
<Text preset="t4" color="#dark.60">
Standard card styling with border and margin
</Text>
<Space height="1x" />
<ListBox
type="card"
label="Select a fruit"
selectionMode="single"
defaultSelectedKey="apple"
>
{fruits.slice(0, 4).map((fruit) => (
<ListBox.Item key={fruit.key}>{fruit.label}</ListBox.Item>
))}
</ListBox>
</div>

<div>
<Text preset="t3" weight="600">
Plain Type
</Text>
<Text preset="t4" color="#dark.60">
No border, no margin, no radius - suitable for embedded use
</Text>
<Space height="1x" />
<ListBox
type="plain"
label="Select a fruit"
selectionMode="single"
defaultSelectedKey="banana"
>
{fruits.slice(0, 4).map((fruit) => (
<ListBox.Item key={fruit.key}>{fruit.label}</ListBox.Item>
))}
</ListBox>
</div>

<div>
<Text preset="t3" weight="600">
Popover Type
</Text>
<Text preset="t4" color="#dark.60">
No border, but keeps margin and radius - suitable for overlay use
</Text>
<Space height="1x" />
<ListBox
type="popover"
label="Select a fruit"
selectionMode="single"
defaultSelectedKey="cherry"
>
{fruits.slice(0, 4).map((fruit) => (
<ListBox.Item key={fruit.key}>{fruit.label}</ListBox.Item>
))}
</ListBox>
</div>

<div>
<Text preset="t3" weight="600">
Plain Type with Sections
</Text>
<Text preset="t4" color="#dark.60">
Section margins are preserved even with plain type
</Text>
<Space height="1x" />
<ListBox
type="plain"
label="Select food items"
selectionMode="single"
defaultSelectedKey="carrot"
>
<ListBox.Section title="Fruits">
<ListBox.Item key="apple">Apple</ListBox.Item>
<ListBox.Item key="banana">Banana</ListBox.Item>
</ListBox.Section>
<ListBox.Section title="Vegetables">
<ListBox.Item key="carrot">Carrot</ListBox.Item>
<ListBox.Item key="broccoli">Broccoli</ListBox.Item>
</ListBox.Section>
</ListBox>
</div>
</Space>
),
parameters: {
docs: {
description: {
story:
'The `type` prop controls the visual styling of the ListBox. Use `card` for standalone use, `plain` for embedded use without decoration, and `popover` for use inside overlays where borders are handled by the container.',
},
},
},
};
22 changes: 19 additions & 3 deletions src/components/fields/ListBox/ListBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ const ListBoxWrapperElement = tasty({
flow: 'column',
gap: 0,
position: 'relative',
radius: '1cr',
radius: {
'': '1cr',
'[data-type="popover"]': '(1cr - 1bw)',
'[data-type="plain"]': '0',
},
color: '#dark-02',
transition: 'theme',
outline: {
Expand All @@ -79,7 +83,7 @@ const ListBoxWrapperElement = tasty({
valid: '#success-text.50',
invalid: '#danger-text.50',
disabled: true,
'popover | searchable': false,
'[data-type="plain"] | [data-type="popover"] | searchable': false,
},
},
});
Expand All @@ -93,7 +97,7 @@ const ListElement = tasty({
boxSizing: 'border-box',
margin: {
'': '.5x .5x 0 .5x',
sections: '.5x .5x 0 .5x',
'[data-type="plain"]': '0',
},
height: 'max-content',
},
Expand Down Expand Up @@ -309,6 +313,15 @@ export interface CubeListBoxProps<T>
* Defaults to "No items".
*/
emptyLabel?: ReactNode;

/**
* Visual type of the ListBox styling.
* - `card` (default): Standard card styling with border and margin
* - `plain`: No border, no margin, no radius - suitable for embedded use
* - `popover`: No border, but keeps margin and radius - suitable for overlay use
* Defaults to 'card'.
*/
type?: 'card' | 'plain' | 'popover';
}

const PROP_STYLES = [...BASE_STYLES, ...OUTER_STYLES, ...COLOR_STYLES];
Expand Down Expand Up @@ -516,6 +529,7 @@ export const ListBox = forwardRef(function ListBox<T extends object>(
allValueProps,
filter,
emptyLabel = 'No items',
type = 'card',
form,
...otherProps
} = props;
Expand Down Expand Up @@ -861,6 +875,7 @@ export const ListBox = forwardRef(function ListBox<T extends object>(
qa="ListBoxWrapper"
mods={mods}
styles={styles}
data-type={type}
>
{header ? (
<StyledHeader styles={headerStyles} data-size={size}>
Expand Down Expand Up @@ -902,6 +917,7 @@ export const ListBox = forwardRef(function ListBox<T extends object>(
styles={listStyles}
aria-disabled={isDisabled || undefined}
mods={{ sections: hasSections }}
data-type={type}
style={
shouldVirtualize
? {
Expand Down
4 changes: 1 addition & 3 deletions src/components/fields/NumberInput/NumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ const StyledTextInputBase = tasty(TextInputBase, {
styles: {
textAlign: 'right',
},
wrapperStyles: {
width: 'initial 13x 100%',
},
});

const StepperContainer = tasty({
Expand All @@ -36,6 +33,7 @@ const StepperContainer = tasty({
gridRows: '1sf 1sf',
flow: 'column',
placeSelf: 'stretch',
margin: '(.5x - 1bw) left',
},
});

Expand Down
6 changes: 2 additions & 4 deletions src/components/fields/Picker/Picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import { CubeListBoxProps, ListBox } from '../ListBox/ListBox';
import type { FieldBaseProps } from '../../../shared';

export interface CubePickerProps<T>
extends Omit<CubeListBoxProps<T>, 'size' | 'tooltip'>,
extends Omit<CubeListBoxProps<T>, 'size' | 'tooltip' | 'type'>,
Omit<CubeItemProps, 'children' | 'size'>,
BasePropsWithoutChildren,
BaseStyleProps,
Expand Down Expand Up @@ -683,9 +683,7 @@ export const Picker = forwardRef(function Picker<T extends object>(
isLoading={isLoading}
stateRef={internalListStateRef}
isCheckable={isCheckable}
mods={{
popover: true,
}}
type="popover"
size="medium"
showSelectAll={showSelectAll}
selectAllLabel={selectAllLabel}
Expand Down
Loading