Skip to content

Commit afaf639

Browse files
author
Hector Arce De Las Heras
committed
Introduce Thumb Feature in PillSelector Component and Refactor PillUnControlled
This commit introduces a new feature in the PillSelector component and refactors the PillUnControlled component. Changes include: PillUnControlled now uses the useImperativeHandle hook to propagate the ref to the parent. A new measuredRef and a function updateMeasurements have been added to PillSelectorControlled to update the measurements of the thumb. The useImperativeHandle and useEffect hooks were updated to use measuredRef. A new thumb property has been added to the PillSelectorStyles type in pillSelectorTheme.ts. These changes introduce a visual representation of the selected pill in the PillSelector component, enhancing the user experience.
1 parent e0e3875 commit afaf639

File tree

5 files changed

+90
-9
lines changed

5 files changed

+90
-9
lines changed

src/components/pillSelector/__tests__/pillSelector.test.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { axe } from 'jest-axe';
66
import { renderProvider } from '@/tests/renderProvider/renderProvider.utility';
77
import { ROLES } from '@/types';
88

9+
import * as stylesHook from '../../../hooks/useStyles/useStyles';
910
import { PillSelectorUnControlled } from '../pillSelectorUnControlled';
1011
import { IPillSelectorUnControlled } from '../types';
1112

@@ -26,6 +27,28 @@ const mockProps: IPillSelectorUnControlled = {
2627
dataTestId: 'dataTestId',
2728
};
2829

30+
const mockStyles = {
31+
container: {
32+
display: 'flex',
33+
flex_direction: 'row',
34+
max_width: 'fit-content',
35+
},
36+
pill: {
37+
border_left: '0',
38+
border_right: '0',
39+
},
40+
firstPill: {
41+
border_right: '0',
42+
},
43+
lastPill: {
44+
border_left: '0',
45+
},
46+
thumb: {
47+
background_color: 'red',
48+
},
49+
['DEFAULT']: {},
50+
};
51+
2952
describe('PillSelector component', () => {
3053
it('Render', async () => {
3154
const { container } = renderProvider(
@@ -97,4 +120,18 @@ describe('PillSelector component', () => {
97120
fireEvent.focus(secondtPillInput);
98121
expect(secondPill).toHaveFocus();
99122
});
123+
124+
it('Render thumb', () => {
125+
jest.spyOn(stylesHook, 'useStyles').mockImplementation(() => mockStyles);
126+
renderProvider(<PillSelectorUnControlled {...mockProps} />);
127+
128+
const thumb = screen.getByTestId(`${mockProps.dataTestId}Thumb`);
129+
130+
expect(thumb).toBeInTheDocument();
131+
132+
const firstPillInput = screen.getByDisplayValue(mockProps.pills[0].value.toString());
133+
fireEvent.click(firstPillInput);
134+
135+
expect(firstPillInput).toBeChecked();
136+
});
100137
});

src/components/pillSelector/pillSelector.styled.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ export const PillSelectorWrapper = styled.div<PillSelectorStylesProp>`
2525
${({ isSelected, styles }) => isSelected && getStyles(styles?.pill?.selected)}
2626
}
2727
`;
28+
29+
export const ThumbStyled = styled.div<PillSelectorStylesProp>`
30+
${({ styles }) => getStyles(styles?.thumb)};
31+
`;

src/components/pillSelector/pillSelectorControlled.tsx

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,48 @@ const PillSelectorControlledComponent = React.forwardRef(
1717
): JSX.Element => {
1818
const styles = useStyles<PillSelectorStyles, V>(PILL_SELECTOR_STYLES, props.variant, ctv);
1919
const id = useId('name');
20+
const measuredRef = React.useRef<HTMLDivElement | null>(null);
21+
22+
// update measurements of the thumb
23+
const updateMeasurements = () => {
24+
// get the index of the selected pill
25+
const index =
26+
props.pills.findIndex(
27+
pill => pill.value.toString() === props.pillSelected?.[0].toString()
28+
) + 1;
29+
30+
// get the size and the position of the thumb from the selected pill
31+
if (measuredRef.current) {
32+
const thumb = measuredRef.current.childNodes[0] as HTMLElement;
33+
const selectedPill = measuredRef.current.childNodes[index] as HTMLElement;
34+
35+
thumb.style.width = `${selectedPill?.clientWidth}px`;
36+
thumb.style.height = `${selectedPill?.getBoundingClientRect().height}px`;
37+
thumb.style.left = `calc(${selectedPill?.getBoundingClientRect().left}px - ${window?.getComputedStyle(selectedPill)?.paddingLeft})`;
38+
}
39+
};
40+
41+
React.useImperativeHandle(
42+
ref,
43+
() => {
44+
return measuredRef.current as HTMLDivElement;
45+
},
46+
[]
47+
);
48+
49+
React.useEffect(() => {
50+
if (!props.multiSelect && measuredRef?.current && props.pillSelected?.[0] && styles?.thumb) {
51+
updateMeasurements();
52+
}
53+
}, [props.multiSelect, measuredRef, props.pills, props.pillSelected?.[0]]);
54+
2055
return (
21-
<PillSelectorStandAlone ref={ref} name={`pillSelector${id}`} styles={styles} {...props} />
56+
<PillSelectorStandAlone
57+
ref={measuredRef}
58+
name={`pillSelector${id}`}
59+
styles={styles}
60+
{...props}
61+
/>
2262
);
2363
}
2464
);

src/components/pillSelector/pillSelectorStandAlone.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Pill } from '@/components/pill';
44
import { useRoveFocus } from '@/hooks';
55
import { ROLES } from '@/types';
66

7-
import { PillSelectorWrapper } from './pillSelector.styled';
7+
import { PillSelectorWrapper, ThumbStyled } from './pillSelector.styled';
88
import type { IPillSelectorStandAlone } from './types';
99
import { keyLeftMove, keyRightMove } from './utils';
1010

@@ -46,8 +46,12 @@ const PillSelectorStandAloneComponent = (
4646
role={ROLES.RADIOGROUP}
4747
styles={props.styles}
4848
>
49+
{!props.multiSelect && props.styles?.thumb && (
50+
<ThumbStyled data-testid={`${dataTestId}Thumb`} styles={props.styles} />
51+
)}
4952
{props.pills.length > 1 && props.pills.length <= maxPills
5053
? props.pills.map((pill, index) => {
54+
const pillSelected = props.pillSelected?.includes(pill.value.toString());
5155
return (
5256
(props.pillSize || pill?.size) && (
5357
<Pill
@@ -56,14 +60,9 @@ const PillSelectorStandAloneComponent = (
5660
focus={focus === index}
5761
multiSelect={props.multiSelect}
5862
name={props.name}
59-
selected={props.pillSelected?.includes(pill.value.toString())}
63+
selected={pillSelected}
6064
size={props.pillSize || ''}
61-
tabIndex={
62-
props.pillSelected?.includes(pill.value.toString()) ||
63-
(!isPillSelected && index === 0)
64-
? 0
65-
: -1
66-
}
65+
tabIndex={pillSelected || (!isPillSelected && index === 0) ? 0 : -1}
6766
{...pill}
6867
value={pill.value.toString()}
6968
variant={props.pillVariant}

src/components/pillSelector/types/pillSelectorTheme.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type PillSelectorStyles = {
99
pill?: PillStyleType;
1010
firstPill?: PillStyleType;
1111
lastPill?: PillStyleType;
12+
thumb?: CommonStyleType;
1213
};
1314

1415
export type PillSelectorThemeType<V extends string | number | symbol> = {

0 commit comments

Comments
 (0)