Skip to content

Instantly share code, notes, and snippets.

@jonidelv
Last active June 28, 2023 21:55
Show Gist options
  • Save jonidelv/c813da85320a9b6490b502a20d9781b2 to your computer and use it in GitHub Desktop.
Save jonidelv/c813da85320a9b6490b502a20d9781b2 to your computer and use it in GitHub Desktop.
// Codesandbox of example working https://codesandbox.io/s/react-playground-forked-cmqx5d?file=/index.js:0-5046
import React, { useState, useCallback, useEffect, useMemo } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const httpPatch = (url, data) => {
return new Promise((resolve) => {
setTimeout(() => {
alert(`Patching ${url} with data: ${JSON.stringify(data)}`);
resolve({ success: true });
}, 1200);
});
};
const items = [
{
value: "1",
label: "First"
},
{
value: "2",
label: "Second"
},
{
value: "3",
label: "Third"
},
{
value: "4",
label: "Fourth"
},
{
value: "5",
label: "Fifth"
}
];
function onSelect(selectedItem) {
alert(selectedItem.label);
}
async function syncWithServer(selectedItem) {
try {
await httpPatch("user", { [`menu-state-${selectedItem.value}`]: true });
} catch (e) {
console.error(e);
}
}
function App() {
return (
<div>
<div className="content">
Normal example
<Dropdown label="select an option" items={items} onSelect={onSelect} />
</div>
<div className="content">
With a value selected
<Dropdown
label="select an option"
items={items}
onSelect={onSelect}
initialSelected={items[2]}
/>
</div>
<div className="content">
Searcheable example
<Dropdown
label="search an option"
items={items}
onSelect={onSelect}
searchable
/>
</div>
<div className="content">
Sync selection to the server
<Dropdown
label="search an option"
items={items}
onSelect={useCallback((selectedItem) => {
onSelect(selectedItem);
syncWithServer(selectedItem);
}, [])}
/>
</div>
</div>
);
}
function Dropdown({
label,
items = [],
initialSelected,
searchable,
onSelect
}) {
// If we have an initialSelected item, menu should be open
const [isOpen, setIsOpen] = useState(!!initialSelected);
const [selected, setSelected] = useState(initialSelected);
const [searchInput, setSearchInput] = useState("");
const onToggle = useCallback(() => {
setIsOpen((isOpen) => !isOpen);
}, []);
const onClose = useCallback(() => {
setIsOpen(false);
}, []);
const onItemClicked = useCallback(
(item) => {
console.log("item", item);
setSelected(item);
onSelect(item);
onClose();
},
[onSelect, onClose]
);
useEffect(() => {
if (!isOpen) {
setSelected(null);
setSearchInput("");
}
}, [isOpen]);
const options = useMemo(() => {
if (searchInput && items.length) {
return items.filter((item) =>
item.label.toLowerCase().includes(searchInput.toLowerCase())
);
}
return items;
}, [items, searchInput]);
return (
<div className="dropdown" tabIndex={0}>
<button
type="button"
className="dropdown-button"
id="dropdownButton"
aria-haspopup="true"
aria-expanded={isOpen}
onClick={onToggle}
>
{label}
</button>
<div
className={`${
isOpen ? "dropdown-open" : ""
} dropdown-menu dropdown-section`}
aria-labelledby="dropdownButton"
role="menu"
>
<>
{searchable && (
<input
className="search-input"
type="text"
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
/>
)}
<ul>
{options.map((item) => (
<DropdownItem
key={item.value}
item={item}
selected={selected?.value === item?.value}
onClick={onItemClicked}
>
{item.label}
</DropdownItem>
))}
{searchable && !options.length && (
<li className="no-item">No items</li>
)}
</ul>
</>
</div>
</div>
);
}
function DropdownItem({ onClick, item, selected, children }) {
return (
<li className={selected ? "selected" : ""} onClick={() => onClick(item)}>
{children}
</li>
);
}
ReactDOM.render(<App />, document.getElementById("container"));
/*
Release note:
We’ve just released an update to our Dropdown component! It now includes a searchable option and the ability to sync selection with the server.
Props and API:
- `label`: A string that specifies the label for the dropdown button.
- `items`: An array of objects representing the options in the dropdown. Each object should have a `value` and a `label` property.
- `initialSelected`: An object representing the initially selected item in the dropdown. It should have a `value` and a `label` property.
- `searchable`: A boolean that specifies whether the dropdown should include a search input to filter the options.
- `onSelect`: A function that is called when an item is selected. It receives the selected item as an argument.
*/
window.currentUser = { id: '19', name: 'Jane', email: 'jane@chameleon.io' };
export const ActiveProfiles({ profiles, onLaunchProfile }) => {
var active = [];
for(i=0; i < profiles.length; i++) {
if(!profiles[i].disabled && profiles[i]['last_seen_time'] > new Date(new Date().getTime()-(24*60*1000)).toISOString()) { // within the last 24 hours
active.push(profiles[i]);
}
}
if(active.length == 1 && active[0].email === window.currentUser.email) {
active.length = 0;
}
return (
<div>
{active.map(function(a) { return <div onClick={() => onLaunchProfile(a.name, a.email)}>{a.name} - {a.email}</div> })}
</div>
)
}
@jonidelv
Copy link
Author

jonidelv commented Jun 28, 2023

This is how I would modify this component to utilize the Dropdown component created above.

import React, { useMemo, useCallback } from 'react';
import Dropdown from './Dropdown';

export const ActiveProfiles = ({ profiles, onLaunchProfile }) => {
  const active = useMemo(() => {
    const filteredProfiles = profiles.filter(
      profile =>
        !profile.disabled &&
        profile.last_seen_time >
          new Date(new Date().getTime() - 24 * 60 * 1000).toISOString()
    );

    if (
      filteredProfiles.length === 1 &&
      filteredProfiles[0].email === window.currentUser.email
    ) {
      return [];
    }

    return filteredProfiles;
  }, [profiles]);

  const items = active.map(({ id, name, email }) => ({
    value: id,
    label: `${name} - ${email}`,
    name,
    email
  }));

  const handleSelect = useCallback((selectedItem) => {
    onLaunchProfile(selectedItem.name, selectedItem.email);
  }, [onLaunchProfile]);

  return (
    <div>
      <Dropdown
        label="Select a profile"
        items={items}
        onSelect={handleSelect}
      />
    </div>
  );
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment