Skip to content

Commit

Permalink
feat(react-ui-kit): add menuListHeading inside Select [WPB-3307] (#6714)
Browse files Browse the repository at this point in the history
* feat(react-ui-kit): add menuListHeading inside Select [WPB-3307]

* feat(react-ui-kit/SelectComponents/MenuList): use separate file for styles and add aria-label for btn [WPB-3307]
  • Loading branch information
dariaoldenburg authored Jan 31, 2025
1 parent f71a2da commit 978b71b
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 4 deletions.
39 changes: 36 additions & 3 deletions packages/react-ui-kit/src/Form/Select.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ const groupOptions = [
],
},
];
const [isOpen, setIsOpen] = React.useState(false);
const [isGroupSelectOpen, setIsGroupSelectOpen] = React.useState(false);
const [isGroupSelectWithHeadingOpen, setIsGroupSelectWithHeadingOpen] = React.useState(false);

<Container>
<Columns>
Expand Down Expand Up @@ -93,12 +94,12 @@ const [isOpen, setIsOpen] = React.useState(false);
<button
className="device-toggle-button"
onClick={() => {
setIsOpen(prev => !prev);
setIsGroupSelectOpen(prev => !prev);
}}
style={{width: '100px', height: '30px'}}
>
click me
{isOpen && (
{isGroupSelectOpen && (
<Select
id="groupSelect"
menuPlacement="top"
Expand All @@ -117,6 +118,38 @@ const [isOpen, setIsOpen] = React.useState(false);
</Column>
</Columns>

<Columns>
<Column>Select With Groups and heading for all groups</Column>

<Column>
<button
className="device-toggle-button"
onClick={() => {
setIsGroupSelectWithHeadingOpen(prev => !prev);
}}
style={{width: '100px', height: '30px'}}
>
click me
{isGroupSelectWithHeadingOpen && (
<Select
id="groupSelectWithHeading"
menuPlacement="top"
menuIsOpen
controlShouldRenderValue={false}
isClearable={false}
backspaceRemovesValue={false}
hideSelectedOptions={false}
options={groupOptions}
onChange={selectedOption => console.log('Selected option', selectedOption)}
dataUieName="group-select-with-heading"
defaultValue={[groupOptions[0].options[0], groupOptions[1].options[3]]}
menuListHeading="Heading"
/>
)}
</button>
</Column>
</Columns>

<Columns>
<Column>Invalid Select</Column>

Expand Down
4 changes: 4 additions & 0 deletions packages/react-ui-kit/src/Form/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
DropdownIndicator,
IndicatorsContainer,
Menu,
MenuList,
SelectContainer,
ValueContainer,
isGroup,
Expand Down Expand Up @@ -61,6 +62,7 @@ interface SelectProps<IsMulti extends boolean, Group extends GroupBase<Option>>
isMulti?: IsMulti;
isSearchable?: boolean;
overlayMenu?: boolean;
menuListHeading?: string;
}

export const Select = <IsMulti extends boolean = false, Group extends GroupBase<Option> = GroupBase<Option>>({
Expand All @@ -78,6 +80,7 @@ export const Select = <IsMulti extends boolean = false, Group extends GroupBase<
required = false,
isSearchable = false,
overlayMenu = true,
menuListHeading,
...props
}: SelectProps<IsMulti, Group>) => {
const theme = useTheme();
Expand Down Expand Up @@ -119,6 +122,7 @@ export const Select = <IsMulti extends boolean = false, Group extends GroupBase<
Menu: Menu(dataUieName, menuCSS),
ValueContainer,
IndicatorsContainer,
...(menuListHeading && {MenuList: MenuList(menuListHeading, dataUieName)}),
}}
tabIndex={TabIndex.UNFOCUSABLE}
isDisabled={disabled}
Expand Down
40 changes: 40 additions & 0 deletions packages/react-ui-kit/src/Form/SelectComponents.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Wire
* Copyright (C) 2024 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

import {CSSObject} from '@emotion/react';

import {Theme} from '../Theme/Theme';

export const menuListHeadingContainerStyles = (theme: Theme): CSSObject => ({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
fontSize: theme.fontSizes.medium,
fontWeight: 600,
padding: '8px 16px',
});

export const menuListCloseButtonStyles: CSSObject = {
background: 'transparent',
border: 'none',
cursor: 'pointer',
padding: 0,
display: 'flex',
alignItems: 'center',
};
35 changes: 34 additions & 1 deletion packages/react-ui-kit/src/Form/SelectComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@ import {
MenuProps,
GroupBase,
OptionsOrGroups,
MenuListProps,
} from 'react-select';

import {Option} from './Select';
import {menuListCloseButtonStyles, menuListHeadingContainerStyles} from './SelectComponents.styles';

import {CheckIcon} from '../Icon';
import {CheckIcon, CloseIcon} from '../Icon';
import {ArrowDown} from '../Icon/ArrowDown';
import {Theme} from '../Layout';
import {TabIndex} from '../types/enums';

// SelectContainer
export const SelectContainer = (props: ContainerProps) => {
return (
Expand Down Expand Up @@ -134,6 +137,36 @@ export const Menu = (dataUieName: string, css?: CSSObject) => (props: MenuProps)
);
};

// eslint-disable-next-line react/display-name
export const MenuList = (menuListHeading: string, dataUieName: string) => (props: MenuListProps) => {
const {selectProps, children} = props;

const handleClose = () => {
if (selectProps && selectProps.onMenuClose) {
selectProps.onMenuClose();
}
};

return (
<components.MenuList {...props}>
<div
{...(dataUieName && {
'data-uie-name': `menu-list-${dataUieName}`,
})}
>
<div css={(theme: Theme) => menuListHeadingContainerStyles(theme)}>
{menuListHeading}
<button onClick={handleClose} css={menuListCloseButtonStyles} aria-label={`Close: ${menuListHeading}`}>
<CloseIcon width={16} height={16} />
</button>
</div>
{children}
</div>
</components.MenuList>
);
};
MenuList.displayName = 'MenuList';

export const renderValue = value => {
if (Array.isArray(value)) {
const currentValue = (i: number) => value[i].props.children;
Expand Down

0 comments on commit 978b71b

Please sign in to comment.