Skip to content

Commit

Permalink
Merge pull request #1030 from glints-dev/feature/add-select-options-s…
Browse files Browse the repository at this point in the history
…ublabel

feat: Add sublabel to select component options
  • Loading branch information
farhanabi authored Jun 6, 2024
2 parents f951dea + 3054a29 commit ed070a6
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 6 deletions.
5 changes: 3 additions & 2 deletions src/@next/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface Option {
disabled?: boolean;
id?: string;
label: string | React.ReactNode;
sublabel?: React.ReactNode;
value: string;
}

Expand Down Expand Up @@ -55,7 +56,7 @@ export const Menu = ({
return (
<StyledMenu>
{options?.map((option: Option) => {
const { value, label, disabled, id } = option;
const { value, label, sublabel, disabled, id } = option;
const randomId = nextId('glints-menu-option');
const menuOptionId = id ? id : randomId;
const isSelected = selectedValues?.includes(value);
Expand All @@ -69,7 +70,7 @@ export const Menu = ({
onClick={onClick}
allowMultiple={allowMultiple}
>
<MenuOptionLabel label={label} />
<MenuOptionLabel label={label} sublabel={sublabel} />
</MenuOption>
);
})}
Expand Down
27 changes: 23 additions & 4 deletions src/@next/Menu/components/MenuOptionLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
import React from 'react';
import { Typography } from '../../Typography';
import { Neutral } from '../../utilities/colors';
import { MenuOptionLabelContainer } from './MenuOptionLabelStyle';

interface MenuOptionLabelProps {
label: React.ReactNode;
sublabel?: React.ReactNode;
}

export const MenuOptionLabel = ({ label, sublabel }: MenuOptionLabelProps) => {
if (!sublabel) {
return (
<Typography as="span" variant="body1" color={Neutral.B18}>
{label}
</Typography>
);
}

export const MenuOptionLabel = ({ label }: { label: React.ReactNode }) => {
return (
<Typography as="span" variant="body1" color={Neutral.B18}>
{label}
</Typography>
<MenuOptionLabelContainer>
<Typography as="span" variant="body1" color={Neutral.B18}>
{label}
</Typography>
<Typography as="span" variant="subtitle2" color={Neutral.B40}>
{sublabel}
</Typography>
</MenuOptionLabelContainer>
);
};
10 changes: 10 additions & 0 deletions src/@next/Menu/components/MenuOptionLabelStyle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import styled from 'styled-components';

export const MenuOptionLabelContainer = styled.div<{
sublabel?: React.ReactNode;
}>`
display: flex;
flex-direction: column;
align-items: center;
padding: 4px 0;
`;
110 changes: 110 additions & 0 deletions src/@next/Select/Select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ const countries = [
{ label: 'Vietnam', value: 'VIETNAM' },
];

const skills = [
{ label: 'Fishing', sublabel: 'Memancing', value: 'fishing' },
{ label: 'Cooking', sublabel: 'Memasak', value: 'cooking' },
{ label: 'Swimming', sublabel: 'Renang', value: 'swimming' },
{ label: 'Flying', sublabel: 'Terbang', value: 'flying' },
];

const options = [
{
active: false,
Expand Down Expand Up @@ -1425,3 +1432,106 @@ WithCustomPrefix.parameters = {
},
},
};

const OptionsWithSublabelTemplate: Story<SelectProps> = args => {
return <SearchableSelect data={skills} {...args} />;
};

export const OptionsWithSublabel = OptionsWithSublabelTemplate.bind({});

OptionsWithSublabel.args = {};

OptionsWithSublabel.parameters = {
docs: {
source: {
code: `
const skills = [
{ label: 'Fishing', sublabel: 'Memancing', value: 'fishing' },
{ label: 'Cooking', sublabel: 'Memasak', value: 'cooking' },
{ label: 'Swimming', sublabel: 'Renang', value: 'swimming' },
{ label: 'Flying', sublabel: 'Terbang', value: 'flying' },
];
const [inputValue, setInputValue] = useState('');
const [selectedOptions, setSelectedOptions] = useState([]);
const [isSearchEmpty, setIsSearchEmpty] = useState(false);
const [options, setOptions] = useState(countries);
const handleInputChange = (value: string) => {
setInputValue(value);
if (value === '') {
setOptions(countries);
return;
}
const filterRegex = new RegExp(value, 'i');
const filterOptions = options.filter((option: Option) =>
(option.label as string).match(filterRegex)
);
setOptions(filterOptions);
};
const handleSelect = ({ value }: { value: string }) => {
if (selectedOptions.includes(value)) {
setSelectedOptions(selectedOptions.filter(option => option !== value));
} else {
setSelectedOptions([...selectedOptions, value]);
}
};
const removeTag = useCallback(
tag => () => {
const options = [...selectedOptions];
options.splice(options.indexOf(tag), 1);
setSelectedOptions(options);
},
[selectedOptions]
);
const tagsMarkup = selectedOptions.map(option => (
<StyledTag
key={\`option-\${option}\`}
onRemove={removeTag(option)}
textColor={Blue.S99}
>
{option}
</StyledTag>
));
useEffect(() => {
if (options.length === 0) {
setIsSearchEmpty(true);
}
if (options.length > 0 && isSearchEmpty === true) {
setIsSearchEmpty(false);
}
}, [isSearchEmpty, options]);
return (
<div>
<Select
allowMultiple
disabled
onSelect={handleSelect}
options={options}
selectedValues={selectedOptions}
width="600px"
searchableProps={{
inputValue,
onInputChange: (value: string) => handleInputChange(value),
}}
label="Label"
hasError
helpText={<InlineError text="Error message" />}
/>
<div style={{ paddingTop: space8 }}>{tagsMarkup}</div>
</div>
);
`,
},
},
};
11 changes: 11 additions & 0 deletions test/e2e/select/select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,3 +339,14 @@ test('Select - Async searchable single select with custom popover placeholder',
'select-async-searchable-single-select-option-list-with-custom-custom-placeholder.png'
);
});

test('Select - with options sublabel', async ({ page }) => {
const selectPage = new SelectPage(page);
await selectPage.gotoWithSublabelOptionPage();

await selectPage.activatorTextInput.waitFor();
await selectPage.activatorTextInput.focus();
await expect(selectPage.canvas).toHaveScreenshot(
'select-with-sublabel-option.png'
);
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions test/e2e/select/selectPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,9 @@ export class SelectPage extends StoryBookPage {
this.setPath('?path=/story/next-select--with-custom-prefix');
await this.goto(args);
}

async gotoWithSublabelOptionPage(args?: Args) {
this.setPath('?path=/story/next-select--options-with-sublabel');
await this.goto(args);
}
}

0 comments on commit ed070a6

Please sign in to comment.