Skip to content

Commit 9710be0

Browse files
feat(fuselage): Flexibility to render custom option inside Options (#647)
* improve: flexibility to render custom option * Match code style Co-authored-by: Tasso Evangelista <tasso.evangelista@rocket.chat>
1 parent 836fa94 commit 9710be0

File tree

7 files changed

+122
-67
lines changed

7 files changed

+122
-67
lines changed

packages/fuselage/src/components/Options/CheckOption.tsx

+8-4
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ import Option from './Option';
55

66
type CheckOptionProps = ComponentProps<typeof Option>;
77

8-
export const CheckOption = memo(
9-
({ selected, children: label, ...options }: CheckOptionProps) => (
8+
export const CheckOption = memo(function CheckOption({
9+
selected,
10+
children: label,
11+
...options
12+
}: CheckOptionProps) {
13+
return (
1014
<Option label={label as string} selected={selected} {...options}>
1115
<CheckBox checked={selected} />
1216
</Option>
13-
)
14-
);
17+
);
18+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React, { ReactNode } from 'react';
2+
3+
type OptionHeaderProps = {
4+
children: ReactNode;
5+
};
6+
7+
const OptionHeader = ({ children }: OptionHeaderProps) => (
8+
<div className='rcx-option__header'>{children}</div>
9+
);
10+
11+
export default OptionHeader;

packages/fuselage/src/components/Options/Option/OptionIcon.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import React, { ComponentProps } from 'react';
33
import { Icon } from '../../Icon';
44
import OptionColumn from './OptionColumn';
55

6-
const OptionIcon = ({
7-
name,
8-
}: {
6+
type OptionIconProps = {
97
name: ComponentProps<typeof Icon>['name'];
10-
}) => (
8+
};
9+
10+
const OptionIcon = ({ name }: OptionIconProps) => (
1111
<OptionColumn>
1212
<Icon size='x20' rcx-option__icon name={name} />
1313
</OptionColumn>

packages/fuselage/src/components/Options/Option/index.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,28 @@ import OptionColumn from './OptionColumn';
44
import OptionContent from './OptionContent';
55
import OptionDescription from './OptionDescription';
66
import OptionDivider from './OptionDivider';
7+
import OptionHeader from './OptionHeader';
78
import OptionIcon from './OptionIcon';
89
import OptionMenu from './OptionMenu';
910
import OptionSkeleton from './OptionSkeleton';
1011
import OptionTitle from './OptionTitle';
1112

1213
export default Object.assign(Option, {
14+
/** @deprecated */
1315
Description: OptionDescription,
16+
/** @deprecated */
1417
Skeleton: OptionSkeleton,
18+
/** @deprecated */
1519
Avatar: OptionAvatar,
20+
/** @deprecated */
1621
Menu: OptionMenu,
22+
/** @deprecated */
1723
Icon: OptionIcon,
24+
/** @deprecated */
1825
Divider: OptionDivider,
26+
/** @deprecated */
1927
Column: OptionColumn,
28+
/** @deprecated */
2029
Content: OptionContent,
2130
});
2231

@@ -29,3 +38,4 @@ export { OptionIcon };
2938
export { OptionMenu };
3039
export { OptionSkeleton };
3140
export { OptionTitle };
41+
export { OptionHeader };

packages/fuselage/src/components/Options/Options.stories.tsx

+16-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import { ComponentStory, ComponentMeta } from '@storybook/react';
99
import React, { createRef } from 'react';
1010

1111
import { Options, OptionType } from '.';
12-
import { Box, Menu } from '..';
13-
import { CheckOption } from '../OptionsPaginated';
14-
import Option from './Option';
12+
import { Box, Menu, Divider } from '..';
13+
import { CheckOption } from './CheckOption';
14+
import Option, { OptionHeader } from './Option';
1515

1616
const options: OptionType[] = [
1717
[1, 'a teste 1'],
@@ -87,3 +87,16 @@ CustomEmpty.args = {
8787
options: [],
8888
customEmpty: 'Custom empty placeholder',
8989
};
90+
91+
export const CustomRender: ComponentStory<typeof Options> = (args) => (
92+
<Box position='relative' maxWidth={250}>
93+
<Options {...args} ref={createRef()}>
94+
<Option>Option Example</Option>
95+
<Divider />
96+
<OptionHeader>Title</OptionHeader>
97+
<CheckOption icon='magnifier'>CheckOption Example</CheckOption>
98+
<CheckOption>CheckOption Example</CheckOption>
99+
<CheckOption>CheckOption Example With Ellipsis</CheckOption>
100+
</Options>
101+
</Box>
102+
);

packages/fuselage/src/components/Options/Options.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export const Options = forwardRef(
5151
renderItem: OptionComponent = Option,
5252
onSelect,
5353
customEmpty,
54+
children,
5455
...props
5556
}: OptionsProps,
5657
ref: Ref<HTMLElement>
@@ -77,7 +78,7 @@ export const Options = forwardRef(
7778

7879
const optionsMemoized = useMemo(
7980
() =>
80-
options.map(([value, label, selected], i) => (
81+
options?.map(([value, label, selected], i) => (
8182
<OptionComponent
8283
role='option'
8384
label={label}
@@ -94,6 +95,7 @@ export const Options = forwardRef(
9495
)),
9596
[options, multiple, cursor, onSelect]
9697
);
98+
9799
return (
98100
<Box rcx-options {...props} ref={ref}>
99101
<Tile padding={0} paddingBlock={'x12'} paddingInline={0} elevation='2'>
@@ -114,8 +116,10 @@ export const Options = forwardRef(
114116
: undefined
115117
}
116118
>
117-
{!options.length && <EmptyComponent customEmpty={customEmpty} />}
118-
{optionsMemoized}
119+
{options?.length ? optionsMemoized : children}
120+
{!options?.length && !children && (
121+
<EmptyComponent customEmpty={customEmpty} />
122+
)}
119123
</Tile>
120124
</Scrollable>
121125
</Tile>

packages/fuselage/src/components/Options/styles.scss

+66-53
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ $variants: (
99
'primary': colors.primary(700),
1010
);
1111

12+
%column {
13+
flex: 0 0 auto;
14+
15+
margin-inline: lengths.margin(2);
16+
}
17+
1218
.rcx-option {
1319
@include clickable;
1420
@include typography.use-font-scale(p2);
@@ -35,24 +41,33 @@ $variants: (
3541
margin-inline: lengths.margin(-2);
3642
}
3743

38-
&:hover,
39-
&--focus {
40-
background: colors.neutral(100);
44+
&__icon {
45+
color: inherit;
4146
}
4247

43-
&--selected {
44-
background: colors.neutral(500);
48+
&__avatar {
49+
@extend %column;
4550
}
4651

47-
&:hover &__menu-wrapper,
48-
&.focus-within &__menu-wrapper,
49-
&:focus-within &__menu-wrapper {
50-
display: flex;
51-
align-items: center;
52+
&__content {
53+
@include typography.use-text-ellipsis;
54+
@extend %column;
55+
flex: 1 1 100%;
5256

53-
width: lengths.size(28);
57+
text-align: start;
5458

55-
opacity: 1;
59+
white-space: nowrap;
60+
}
61+
62+
&__header {
63+
@include typography.use-font-scale(micro);
64+
65+
padding-block: lengths.padding(8);
66+
padding-inline: lengths.padding(16);
67+
68+
text-transform: uppercase;
69+
70+
font-weight: 400;
5671
}
5772

5873
&__menu-wrapper {
@@ -65,58 +80,56 @@ $variants: (
6580
opacity: 0;
6681
}
6782

68-
@each $name, $color in $variants {
69-
&--#{$name} {
70-
color: theme('option-color-variant-#{$name}', $color);
71-
}
72-
}
73-
}
74-
75-
%column {
76-
flex: 0 0 auto;
77-
78-
margin-inline: lengths.margin(2);
79-
}
83+
&.rcx-option__column {
84+
@extend %column;
85+
display: flex;
8086

81-
.rcx-option__icon {
82-
color: inherit;
83-
}
87+
justify-content: center;
88+
align-items: center;
8489

85-
.rcx-option__avatar {
86-
@extend %column;
87-
}
90+
min-width: lengths.size(20);
91+
min-height: lengths.size(20);
92+
}
8893

89-
.rcx-option__content {
90-
@include typography.use-text-ellipsis;
91-
@extend %column;
92-
flex: 1 1 100%;
94+
&.rcx-option__description {
95+
@include typography.use-font-scale(p2);
96+
@extend %column;
97+
display: inline;
9398

94-
text-align: start;
99+
color: colors.foreground(hint);
100+
}
95101

96-
white-space: nowrap;
97-
}
102+
&:hover,
103+
&--focus {
104+
background: colors.neutral(100);
105+
}
98106

99-
.rcx-option__column {
100-
@extend %column;
101-
display: flex;
107+
&--selected {
108+
background: colors.neutral(500);
109+
}
102110

103-
justify-content: center;
104-
align-items: center;
111+
&:hover &__menu-wrapper,
112+
&.focus-within &__menu-wrapper,
113+
&:focus-within &__menu-wrapper {
114+
display: flex;
115+
align-items: center;
105116

106-
min-width: lengths.size(20);
107-
min-height: lengths.size(20);
108-
}
117+
width: lengths.size(28);
109118

110-
.rcx-option__description {
111-
@include typography.use-font-scale(p2);
112-
@extend %column;
113-
display: inline;
119+
opacity: 1;
120+
}
114121

115-
color: colors.foreground(hint);
122+
@each $name, $color in $variants {
123+
&--#{$name} {
124+
color: theme('option-color-variant-#{$name}', $color);
125+
}
126+
}
116127
}
117128

118-
.rcx-options:hover {
119-
.rcx-option--focus:not(.rcx-option--selected):not(:hover) {
120-
background: initial;
129+
.rcx-options {
130+
&:hover {
131+
.rcx-option--focus:not(.rcx-option--selected):not(:hover) {
132+
background: initial;
133+
}
121134
}
122135
}

0 commit comments

Comments
 (0)