Skip to content

Commit 1ca1deb

Browse files
authored
fix(Item): interface types (#791)
1 parent b6ad69a commit 1ca1deb

File tree

11 files changed

+73
-22
lines changed

11 files changed

+73
-22
lines changed

.changeset/honest-lemons-pump.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cube-dev/ui-kit": patch
3+
---
4+
5+
Actualize the interface of Item component.

.changeset/odd-wasps-breathe.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cube-dev/ui-kit": patch
3+
---
4+
5+
Make Panel placeSelf stretch by default.

.changeset/perfect-dancers-move.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cube-dev/ui-kit": patch
3+
---
4+
5+
Fix Item interface for FilterPicker.

.changeset/pretty-comics-train.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cube-dev/ui-kit": patch
3+
---
4+
5+
Add onClear callback for FilterPicker, Select, ComboBox and SearchInput.

.changeset/pretty-turtles-shake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cube-dev/ui-kit": patch
3+
---
4+
5+
Fix popover of FilterPicker to corretly flip on opening.

src/components/Item.tsx

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
1-
import { ReactElement, ReactNode } from 'react';
1+
import { ReactElement } from 'react';
22
import { Item, ItemProps } from 'react-stately';
33

4-
import { Styles } from '../tasty';
4+
import { CubeItemBaseProps } from './content/ItemBase/ItemBase';
55

6-
export interface CubeItemProps<T> extends ItemProps<T> {
7-
qa?: string;
8-
description?: ReactNode;
9-
descriptionPlacement?: 'inline' | 'block' | 'auto';
10-
icon?: ReactNode | 'checkbox';
11-
prefix?: ReactNode;
12-
suffix?: ReactNode;
13-
rightIcon?: ReactNode;
14-
styles?: Styles;
6+
export interface CubeItemProps<T>
7+
extends ItemProps<T>,
8+
Omit<CubeItemBaseProps, 'children'> {
159
onAction?: () => void;
1610
wrapper?: (item: ReactElement) => ReactElement;
1711
[key: string]: any;
1812
}
1913

20-
const _Item = Item as unknown as <T>(props: CubeItemProps<T>) => ReactElement;
14+
const _Item = Item as <T>(props: CubeItemProps<T>) => ReactElement;
2115

2216
export { _Item as Item };

src/components/fields/ComboBox/ComboBox.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ export interface CubeComboBoxProps<T>
130130
allowsCustomValue?: boolean;
131131
/** Whether the combo box is clearable using ESC keyboard button or clear button inside the input */
132132
isClearable?: boolean;
133+
/** Callback called when the clear button is pressed */
134+
onClear?: () => void;
133135
}
134136

135137
const PROP_STYLES = [...BASE_STYLES, ...OUTER_STYLES, ...COLOR_STYLES];
@@ -336,6 +338,8 @@ export const ComboBox = forwardRef(function ComboBox<T extends object>(
336338
}
337339
// Focus back to the input
338340
inputRef.current?.focus();
341+
342+
props.onClear?.();
339343
});
340344

341345
let comboBoxWidth = wrapperRef?.current?.offsetWidth;

src/components/fields/FilterPicker/FilterPicker.tsx

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ import {
1414
useState,
1515
} from 'react';
1616
import { FocusScope, Key, useKeyboard } from 'react-aria';
17-
import { Section as BaseSection, Item, ListState } from 'react-stately';
17+
import {
18+
Section as BaseSection,
19+
ListState,
20+
Item as ReactAriaItem,
21+
} from 'react-stately';
1822

1923
import { useEvent } from '../../../_internal';
2024
import { useWarn } from '../../../_internal/hooks/use-warn';
@@ -119,6 +123,8 @@ export interface CubeFilterPickerProps<T>
119123
mods?: Record<string, boolean>;
120124
/** Whether the filter picker is clearable using a clear button in the rightIcon slot */
121125
isClearable?: boolean;
126+
/** Callback called when the clear button is pressed */
127+
onClear?: () => void;
122128
}
123129

124130
const PROP_STYLES = [...BASE_STYLES, ...OUTER_STYLES, ...COLOR_STYLES];
@@ -441,7 +447,7 @@ export const FilterPicker = forwardRef(function FilterPicker<T extends object>(
441447
if (!child || typeof child !== 'object') return;
442448
const element = child as ReactElement;
443449

444-
if (element.type === Item) {
450+
if (element.type === ReactAriaItem) {
445451
const props = element.props as any;
446452
const label =
447453
props.textValue ||
@@ -523,7 +529,7 @@ export const FilterPicker = forwardRef(function FilterPicker<T extends object>(
523529
if (!child || typeof child !== 'object') return;
524530
const element = child as ReactElement;
525531

526-
if (element.type === Item) {
532+
if (element.type === ReactAriaItem) {
527533
const childKey = String(element.key);
528534
if (selectedSet.has(normalizeKeyValue(childKey))) {
529535
const props = element.props as any;
@@ -698,7 +704,7 @@ export const FilterPicker = forwardRef(function FilterPicker<T extends object>(
698704
if (sectionChild && typeof sectionChild === 'object') {
699705
const sectionElement = sectionChild as ReactElement;
700706
if (
701-
sectionElement.type === Item ||
707+
sectionElement.type === ReactAriaItem ||
702708
(sectionElement.type as any)?.displayName === 'Item'
703709
) {
704710
const clonedItem = cloneWithNormalizedKey(sectionElement);
@@ -895,7 +901,7 @@ export const FilterPicker = forwardRef(function FilterPicker<T extends object>(
895901
);
896902
};
897903

898-
const [shouldUpdatePosition, setShouldUpdatePosition] = useState(false);
904+
const [shouldUpdatePosition, setShouldUpdatePosition] = useState(true);
899905

900906
// The trigger is rendered as a function so we can access the dialog state
901907
const renderTrigger = (state) => {
@@ -943,8 +949,15 @@ export const FilterPicker = forwardRef(function FilterPicker<T extends object>(
943949
});
944950

945951
useEffect(() => {
946-
// Disable the update of the position while the popover is open (with a delay) to avoid jumping
947-
setShouldUpdatePosition(!state.isOpen);
952+
// Allow initial positioning & flipping when opening, then lock placement after transition
953+
// Popover transition is ~120ms, give it a bit more time to finalize placement
954+
if (state.isOpen) {
955+
setShouldUpdatePosition(true);
956+
const id = window.setTimeout(() => setShouldUpdatePosition(false), 160);
957+
return () => window.clearTimeout(id);
958+
} else {
959+
setShouldUpdatePosition(true);
960+
}
948961
}, [state.isOpen]);
949962

950963
// Clear button logic
@@ -971,6 +984,8 @@ export const FilterPicker = forwardRef(function FilterPicker<T extends object>(
971984

972985
triggerRef?.current?.focus?.();
973986

987+
props.onClear?.();
988+
974989
return false;
975990
});
976991

@@ -1032,7 +1047,7 @@ export const FilterPicker = forwardRef(function FilterPicker<T extends object>(
10321047
placement="bottom start"
10331048
styles={triggerStyles}
10341049
shouldUpdatePosition={shouldUpdatePosition}
1035-
shouldFlip={shouldFlip}
1050+
shouldFlip={shouldFlip && shouldUpdatePosition}
10361051
isDismissable={true}
10371052
shouldCloseOnInteractOutside={(el) => {
10381053
const menuTriggerEl = el.closest('[data-popover-trigger]');
@@ -1207,7 +1222,7 @@ export const FilterPicker = forwardRef(function FilterPicker<T extends object>(
12071222
);
12081223
}) as unknown as (<T>(
12091224
props: CubeFilterPickerProps<T> & { ref?: ForwardedRef<HTMLElement> },
1210-
) => ReactElement) & { Item: typeof Item; Section: typeof BaseSection };
1225+
) => ReactElement) & { Item: typeof ListBox.Item; Section: typeof BaseSection };
12111226

12121227
FilterPicker.Item = ListBox.Item;
12131228

src/components/fields/SearchInput/SearchInput.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export interface CubeSearchInputProps
2020
SearchFieldProps {
2121
/** Whether the search input is clearable using ESC keyboard button or clear button inside the input */
2222
isClearable?: boolean;
23+
/** Callback called when the clear button is pressed */
24+
onClear?: () => void;
2325
}
2426

2527
export const SearchInput = forwardRef(function SearchInput(
@@ -29,7 +31,7 @@ export const SearchInput = forwardRef(function SearchInput(
2931
props = castNullableStringValue(props);
3032
props = useProviderProps(props);
3133

32-
let { isClearable, validationState } = props;
34+
let { isClearable, validationState, onClear } = props;
3335

3436
let inputRef = useRef(null);
3537

@@ -57,6 +59,12 @@ export const SearchInput = forwardRef(function SearchInput(
5759
type={validationState === 'invalid' ? 'clear' : 'neutral'}
5860
theme={validationState === 'invalid' ? 'danger' : undefined}
5961
{...ariaToCubeButtonProps(clearButtonProps)}
62+
onPress={(e) => {
63+
// Call the original clear functionality
64+
clearButtonProps.onPress?.();
65+
// Call the onClear callback
66+
onClear?.();
67+
}}
6068
/>
6169
)}
6270
</>

src/components/fields/Select/Select.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ export interface CubeSelectBaseProps<T>
204204
theme?: 'default' | 'special';
205205
/** Whether the select is clearable using a clear button in the rightIcon slot */
206206
isClearable?: boolean;
207+
/** Callback called when the clear button is pressed */
208+
onClear?: () => void;
207209
}
208210

209211
export interface CubeSelectProps<T> extends CubeSelectBaseProps<T> {
@@ -351,6 +353,8 @@ function Select<T extends object>(
351353
}
352354
// Return focus to the trigger for better UX
353355
triggerRef.current?.focus?.();
356+
357+
props.onClear?.();
354358
});
355359

356360
let triggerWidth = triggerRef?.current?.offsetWidth;

0 commit comments

Comments
 (0)