Skip to content

Commit

Permalink
feat: improve useSelect hook item render (#732)
Browse files Browse the repository at this point in the history
* feat: improve useSelect hook to support custom item rendering

* chore(storybook): create a sample  for custom select item

* refactor: useSelect interface
  • Loading branch information
hamed-musallam authored Jun 3, 2024
1 parent ebdd792 commit 76ac3f4
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 9 deletions.
21 changes: 13 additions & 8 deletions src/components/hooks/useSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MenuItem } from '@blueprintjs/core';
import { ItemRenderer } from '@blueprintjs/select';
import { useState } from 'react';
import { ReactNode, useState } from 'react';

type FilterType<SourceType, Type> = Pick<
SourceType,
Expand All @@ -9,14 +9,17 @@ type FilterType<SourceType, Type> = Pick<
}[keyof SourceType]
>;

interface BaseOptions<T> {
itemTextKey: keyof FilterType<T, string>;
interface ItemOptions<T> {
renderItem?: (item: T) => ReactNode;
defaultSelectedItem?: T;
}

interface RenderOptions<T> {
interface BaseOptions<T> extends ItemOptions<T> {
itemTextKey: keyof FilterType<T, string>;
}

interface RenderOptions<T> extends ItemOptions<T> {
getItemText: (item: T) => string;
defaultSelectedItem?: T;
}

type SelectOptions<T> = BaseOptions<T> | RenderOptions<T>;
Expand Down Expand Up @@ -91,16 +94,18 @@ function getItemRenderer<T>(value: T, options: SelectOptions<T>) {
{ handleClick, handleFocus, modifiers, index },
) => {
const label = getLabel(item, options);
const { renderItem } = options;
const { active, disabled } = modifiers;
return (
<MenuItem
active={modifiers.active}
disabled={modifiers.disabled}
active={active}
disabled={disabled}
selected={selectedLabel === label}
key={index}
onClick={handleClick}
onFocus={handleFocus}
roleStructure="listoption"
text={label}
text={renderItem?.(item) || label}
/>
);
};
Expand Down
74 changes: 73 additions & 1 deletion stories/components/select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import {
MenuItem,
} from '@blueprintjs/core';
import { ItemListRenderer, Select } from '@blueprintjs/select';
import { Dispatch, Fragment, SetStateAction, useState } from 'react';
import styled from '@emotion/styled';
import {
CSSProperties,
Dispatch,
Fragment,
SetStateAction,
useState,
} from 'react';

import { Button, useOnOff, useSelect } from '../../src/components';

Expand Down Expand Up @@ -384,6 +391,71 @@ export function WithCustomStyle() {
);
}

const Row = styled.div({
display: 'flex',
flexDirection: 'row',
});

const Tag = styled.div({
borderRadius: '25px',
minWidth: '25px',
minHeight: '25px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '11px',
});

export function WithCustomRenderItem() {
const { value, ...defaultProps } = useSelect<{
label: string;
color: CSSProperties['color'];
}>({
itemTextKey: 'label',
renderItem: ({ label, color }) => (
<Row>
<Tag
style={{
backgroundColor: color,
}}
>
<span>{label.charAt(0)}</span>
</Tag>
<span style={{ flex: 1, padding: '0 5px' }}>{label}</span>
<Tag
style={{
border: `${color} 1px solid`,
padding: '0 10px',
}}
>
<span>fruits</span>
</Tag>
</Row>
),
});
return (
<>
<Select
items={[
{ label: 'Apple', color: 'greenyellow' },
{ label: 'Banana', color: 'yellow' },
{ label: 'Orange', color: 'orange' },
]}
filterable={false}
itemsEqual="label"
{...defaultProps}
>
<Button
style={{ width: '500px' }}
text={value?.label ?? 'Select a status'}
rightIcon="double-caret-vertical"
/>
</Select>
<p>Value outside component is {value?.label}.</p>
</>
);
}

export function FixedValueNoopHandle() {
const defaultProps = useSelect<{ label: string }>({ itemTextKey: 'label' });
const value = { label: 'Orange' };
Expand Down

0 comments on commit 76ac3f4

Please sign in to comment.