Skip to content

Commit

Permalink
[PBNTR-529] Children for MultiLevelSelect (#3661)
Browse files Browse the repository at this point in the history
[Runway Story](https://runway.powerhrg.com/backlog_items/PBNTR-529)

This PR:
- ✅ Creates MultiLevelSelect.Options subcomponent
- ✅ Backwards compatible so none subcomponent structure also works
- ✅ Render children next to Checkbox/Radios

<img width="855" alt="Screenshot 2024-10-01 at 9 41 45 PM"
src="https://github.com/user-attachments/assets/20ff1dac-0173-4e0e-8396-ed43d70c530c">

<img width="825" alt="Screenshot 2024-10-01 at 9 42 02 PM"
src="https://github.com/user-attachments/assets/f5fa5c6f-e4fd-4409-a53f-5ef838dc53d3">
  • Loading branch information
nidaqg authored Oct 4, 2024
1 parent 1ec84ae commit caa3c8a
Show file tree
Hide file tree
Showing 10 changed files with 568 additions and 229 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createContext } from "react";

const MultiLevelSelectContext = createContext<any>({});

export default MultiLevelSelectContext;
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,4 @@ const MultiLevelSelectDefault = (props) => {
)
};

export default MultiLevelSelectDefault;
export default MultiLevelSelectDefault;
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React from "react";
import MultiLevelSelect from "../_multi_level_select";
import Badge from "../../pb_badge/_badge";

const treeData = [
{
label: "Power Home Remodeling",
value: "Power Home Remodeling",
id: "powerhome1",
expanded: true,
children: [
{
label: "People",
value: "People",
id: "people1",
expanded: true,
status: "active",
children: [
{
label: "Talent Acquisition",
value: "Talent Acquisition",
id: "talent1",
},
{
label: "Business Affairs",
value: "Business Affairs",
id: "business1",
status: "active",
variant: "primary",

children: [
{
label: "Initiatives",
value: "Initiatives",
id: "initiative1",
},
{
label: "Learning & Development",
value: "Learning & Development",
id: "development1",
status: "Inactive",
},
],
},
{
label: "People Experience",
value: "People Experience",
id: "experience1",
},
],
},
{
label: "Contact Center",
value: "Contact Center",
id: "contact1",
status: "Inactive",
variant: "error",
children: [
{
label: "Appointment Management",
value: "Appointment Management",
id: "appointment1",
},
{
label: "Customer Service",
value: "Customer Service",
id: "customer1",
},
{
label: "Energy",
value: "Energy",
id: "energy1",
},
],
},
],
},
];

const MultiLevelSelectWithChildren = (props) => {
return (
<div>
<MultiLevelSelect
id="multiselect-with-children"
onSelect={(selectedNodes) =>
console.log("Selected Items", selectedNodes)
}
treeData={treeData}
{...props}
>
<MultiLevelSelect.Options>
{(item) => (
<Badge
marginLeft="sm"
text={item.status}
variant={item.status === "active" ? "success" : "warning"}
/>
)}
</MultiLevelSelect.Options>
</MultiLevelSelect>
</div>
);
};

export default MultiLevelSelectWithChildren;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The MultiLevelSelect also provides a subcomponent structure which can be used to render children to the right of the Checkboxes and their labels. As seen in the code snippet below, these children have access to the current item being iterated over which can be used for conditional rendering.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React from "react";
import MultiLevelSelect from "../_multi_level_select";
import Badge from "../../pb_badge/_badge";

const treeData = [
{
label: "Power Home Remodeling",
value: "Power Home Remodeling",
id: "powerhome1",
expanded: true,
children: [
{
label: "People",
value: "People",
id: "people1",
expanded: true,
status: "active",
children: [
{
label: "Talent Acquisition",
value: "Talent Acquisition",
id: "talent1",
},
{
label: "Business Affairs",
value: "Business Affairs",
id: "business1",
status: "active",
variant: "primary",

children: [
{
label: "Initiatives",
value: "Initiatives",
id: "initiative1",
},
{
label: "Learning & Development",
value: "Learning & Development",
id: "development1",
status: "Inactive",
},
],
},
{
label: "People Experience",
value: "People Experience",
id: "experience1",
},
],
},
{
label: "Contact Center",
value: "Contact Center",
id: "contact1",
status: "Inactive",
variant: "error",
children: [
{
label: "Appointment Management",
value: "Appointment Management",
id: "appointment1",
},
{
label: "Customer Service",
value: "Customer Service",
id: "customer1",
},
{
label: "Energy",
value: "Energy",
id: "energy1",
},
],
},
],
},
];

const MultiLevelSelectWithChildrenWithRadios = (props) => {
return (
<div>
<MultiLevelSelect
id="multiselect-with-children"
onSelect={(selectedNodes) =>
console.log("Selected Items", selectedNodes)
}
treeData={treeData}
variant="single"
{...props}
>
<MultiLevelSelect.Options>
{(item) => (
<Badge
marginLeft="sm"
text={item.status}
variant={item.status === "active" ? "success" : "warning"}
/>
)}
</MultiLevelSelect.Options>
</MultiLevelSelect>
</div>
);
};

export default MultiLevelSelectWithChildrenWithRadios;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The MultiLevelSelect subcomponent structure is also available in the 'Single Select' variant. In this variant, the children will be rendered to the right of the Radios and their labels.
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ examples:
- multi_level_select_return_all_selected: Return All Selected
- multi_level_select_selected_ids_react: Selected Ids
- multi_level_select_color: With Pills (Custom Color)
- multi_level_select_with_children: Checkboxes With Children
- multi_level_select_with_children_with_radios: Single Select With Children

Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export { default as MultiLevelSelectSingleChildrenOnly } from './_multi_level_se
export { default as MultiLevelSelectReturnAllSelected } from './_multi_level_select_return_all_selected.jsx'
export { default as MultiLevelSelectSelectedIdsReact } from "./_multi_level_select_selected_ids_react.jsx"
export { default as MultiLevelSelectColor } from './_multi_level_select_color.jsx'
export { default as MultiLevelSelectWithChildren } from './_multi_level_select_with_children.jsx'
export { default as MultiLevelSelectWithChildrenWithRadios } from './_multi_level_select_with_children_with_radios.jsx'
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import React, {useContext} from "react";
import classnames from "classnames";
import MultiLevelSelectContext from "./context";
import { globalProps, GlobalProps } from "../utilities/globalProps";
import {
buildAriaProps,
buildCss,
buildDataProps,
buildHtmlProps,
} from "../utilities/props";
import Checkbox from "../pb_checkbox/_checkbox";
import Radio from "../pb_radio/_radio";
import CircleIconButton from "../pb_circle_icon_button/_circle_icon_button";
import Body from "../pb_body/_body";

type MultiLevelSelectOptionsProps = {
aria?: { [key: string]: string },
children?: React.ReactNode | ((item: any) => React.ReactNode),
className?: string,
dark?: boolean,
data?: { [key: string]: string },
htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
} & GlobalProps;

const MultiLevelSelectOptions = ({
children,
items,
...props
}: MultiLevelSelectOptionsProps) => {
const {
variant,
inputName,
renderNestedOptions,
isTreeRowExpanded,
handleToggleClick,
handleRadioButtonClick,
handledropdownItemClick,
filterItem,
} = useContext(MultiLevelSelectContext)

const {
aria = {},
className,
data = {},
htmlOptions = {},
} = props;

const ariaProps = buildAriaProps(aria);
const dataProps = buildDataProps(data);
const htmlProps = buildHtmlProps(htmlOptions);
const classes = classnames(
buildCss("pb_multi_level_select_options"),
globalProps(props),
className
);

return (
<ul
{...ariaProps}
{...dataProps}
{...htmlProps}
className={classes}
>
{Array.isArray(items) &&
items.map((item: { [key: string]: any }) => {
return (
<div key={item.id}>
<li className={"dropdown_item"}
data-name={item.id}
>
<div className="dropdown_item_checkbox_row">
{!item.parent_id && !item.children ? null : (
<div
key={
isTreeRowExpanded(item)
? "chevron-down"
: "chevron-right"
}
>
<CircleIconButton
className={
item.children && item.children.length > 0
? ""
: "toggle_icon"
}
icon={
isTreeRowExpanded(item)
? "chevron-down"
: "chevron-right"
}
onClick={(event: React.MouseEvent) =>
handleToggleClick(item.id, event)
}
variant="link"
/>
</div>
)}
{variant === "single" ? (
item.hideRadio ? (
<Body>{item.label}</Body>
) : (
<Radio
checked={item.checked}
id={`${item.id}-${item.label}`}
label={item.label}
name={inputName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
handleRadioButtonClick(e)
}
padding={item.children ? "none" : "xs"}
type="radio"
value={item.label}
/>
)
) : (
<Checkbox id={item.id}
text={item.label}
>
<input
checked={item.checked}
name={item.label}
onChange={(e) => {
handledropdownItemClick(e, !item.checked);
}}
type="checkbox"
value={item.label}
/>
</Checkbox>
)}
{/* Render children next to the checkbox */}
{children && (
typeof children === "function" ? children(item) : children
)}
</div>
{isTreeRowExpanded(item) &&
item.children &&
item.children.length > 0 &&
(variant === "single" || !filterItem) && (
<div>{renderNestedOptions(item.children)}</div>
)}
</li>
</div>
);
})}
</ul>
);
};

export default MultiLevelSelectOptions;

0 comments on commit caa3c8a

Please sign in to comment.