How to use react-popper with render props using functional component - reactjs

`
<Manager><div role="presentation"
id="popover"
className={popoverClasses}
onClick={onDismiss}
onKeyUp={onDismiss}
>{backdrop && <div className={styles.popover__backdrop} />}
<div ref={popoverRef}> --how to implement
<Reference>
{({ ref }) => {
// how can we implement ref
ref = ref; -- how to implement
const targetProps = {
ref: setReferenceRef,
// Accessibility
//event handler
onKeyUp: (keyUpEvent) => {
if (
['Enter', 'Space Bar', 'Spacebar', ' '].includes(
// key of event handler
keyUpEvent.key,
)
) {
if (!open) {
onEvent();
} else {
onReferenceClickClose();
}
}
},
[on${event[0].toUpperCase()}${event.substr(1)}]: !open
? onEvent()
: onReferenceClickClose(),
};
return (
<span
role="button"
className={styles.popover__reference}
tabIndex={tabIndex}
aria-haspopup="dialog"
{...targetProps}
>
{reference}
{refIcon}
);
}}
<Popper
placement={position}
modifiers={{
...modifiers,
preventOverflow: {
boundariesElement: boundaries,
},
}}
>
{({ ref, placement, arrowProps }) => (
{title && {title}}
{children(closePopover)}
)}
`

Related

Using Formik on IndexTable with editable fields

I am trying to come up with a list that has an editable Quantity field. Here's what I have now.
As you can see, I'm unable to map the Quantity field. The values there comes from Shopify's ResourcePicker. It does not have a quantity so I manually update the object to set a default quantity of 1. This quantity should be editable as seen in the screenshot above.
Here's the code that I currently have.
import { Button, Icon, IndexTable, TextField } from '#shopify/polaris'
import { Form, Formik } from 'formik'
import { MobilePlusMajor } from '#shopify/polaris-icons'
import { ResourcePicker } from '#shopify/app-bridge-react'
import { useEffect, useState } from 'react'
function SelectProducts({ form, dispatch }) {
const [isProductPickerOpen, setIsProductPickerOpen] = useState(false)
const [showProducts, setShowProducts] = useState(false)
const [chosenProducts, setChosenProducts] = useState([])
const productPickerOnSelectHandler = (selectPayload) => {
setIsProductPickerOpen(false)
// Add quantity to selection
const updatedSelection = selectPayload.selection.map((product) => {
const variants = product.variants.map((variant) => ({
...variant,
quantity: 1,
}))
return {
...product,
variants,
}
})
setChosenProducts(updatedSelection)
}
useEffect(() => {
if (!chosenProducts) return
dispatch({
type: 'SET_PRODUCT_SELECTION_DATA_ON_CREATE_SUBSCRIPTION',
payload: chosenProducts,
})
}, [chosenProducts, dispatch])
const productPickerOnCancelHandler = () => {
setIsProductPickerOpen(false)
}
useEffect(() => {
if (
form.productSelectionData &&
Object.keys(form.productSelectionData).length > 0
) {
setShowProducts(true)
} else {
setShowProducts(false)
}
}, [form])
return (
<>
{showProducts ? (
<Formik initialValues={form.productSelectionData}>
{({ setFieldValue, values, errors, isSubmitting }) => (
<Form>
<IndexTable
resourceName={{
singular: 'product',
plural: 'products',
}}
onSelectionChange={() => {
console.log('selection changed.')
}}
headings={[
{ title: 'Product' },
{ title: 'Quantity' },
{ title: 'Price' },
{ title: 'Subtotal' },
]}
emptyState={null}
itemCount={1}
>
{values.map((product, pIndex) =>
product.variants?.map((variant, vIndex) => (
<IndexTable.Row
id={variant.id}
key={variant.id}
position={vIndex}
>
<IndexTable.Cell fontWeight="bold">
<div onClick={(e) => e.stopPropagation()}>
{variant.displayName}
</div>
</IndexTable.Cell>
<IndexTable.Cell>
<TextField
type="number"
value={variant.quantity}
onChange={(value) => {
console.log('got in here')
setFieldValue(
`products[${pIndex}].variants[${vIndex}].quantity`,
value
)
}}
/>
</IndexTable.Cell>
<IndexTable.Cell>
<div onClick={(e) => e.stopPropagation()}>
{variant.price}
</div>
</IndexTable.Cell>
<IndexTable.Cell>
<div onClick={(e) => e.stopPropagation()}>
{Number(variant.quantity * variant.price)}
</div>
</IndexTable.Cell>
</IndexTable.Row>
))
)}
</IndexTable>
</Form>
)}
</Formik>
) : null}
<Button
icon={<Icon source={MobilePlusMajor} />}
onClick={() => {
setIsProductPickerOpen(true)
}}
>
Add products and variants
</Button>
<ResourcePicker
resourceType="Product"
open={isProductPickerOpen}
showVariants
onSelection={productPickerOnSelectHandler}
onCancel={productPickerOnCancelHandler}
/>
<p> </p>
</>
)
}
export default SelectProducts
I am not sure what to put in the setFieldValue for variant.quantity. How do I set it correctly? Or am I understanding this the wrong way?

React-table filter losing focus after each keystroke

I am attempting to copy the global filter implementation from this example: https://react-table.tanstack.com/docs/examples/filtering I have copied all the code and the filtering is working correctly. However for some reason, whenever I type a character in the input box, it loses the focus and I need to click back in the box again to keep typing.
Here is the entire table file:
import React, { useState } from "react";
import {
useTable,
useSortBy,
useGlobalFilter,
useAsyncDebounce
} from "react-table";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {
faSortUp,
faSortDown,
faCheck,
faEllipsisV
} from "#fortawesome/free-solid-svg-icons";
import { Scrollbars } from "rc-scrollbars";
const IngredientsTable = (props) => {
const { data, selectedIngredient } = props;
const [showIngredientCheckID, setShowIngredientCheckID] = useState(-1);
const columns = React.useMemo(
() => [
{
Header: "Name",
accessor: "col1" // accessor is the "key" in the data
},
{
Header: "",
accessor: "col2"
},
{
Header: "Item Number",
accessor: "col3" // accessor is the "key" in the data
},
{
Header: "EPA Number",
accessor: "col4"
},
{
Header: "Category",
accessor: "col5"
},
{
Header: "Modified",
accessor: "col6"
}
],
[]
);
// Define a default UI for filtering
const GlobalFilter = ({
preGlobalFilteredRows,
globalFilter,
setGlobalFilter
}) => {
const count = preGlobalFilteredRows.length;
const [value, setValue] = useState(globalFilter);
const onChange = useAsyncDebounce((value) => {
setGlobalFilter(value || undefined);
}, 200);
return (
<span>
Filter Ingredients:{" "}
<input
value={value || ""}
onChange={(e) => {
setValue(e.target.value);
onChange(e.target.value);
}}
placeholder={`${count} records...`}
style={{
fontSize: "1.1rem",
border: "0"
}}
/>
</span>
);
};
// Define a default UI for filtering
function DefaultColumnFilter({
column: { filterValue, preFilteredRows, setFilter }
}) {
const count = preFilteredRows.length;
return (
<input
value={filterValue || ""}
onChange={(e) => {
setFilter(e.target.value || undefined); // Set undefined to remove the filter entirely
}}
placeholder={`Search ${count} records...`}
/>
);
}
const defaultColumn = React.useMemo(
() => ({
// Let's set up our default Filter UI
Filter: DefaultColumnFilter
}),
[]
);
const filterTypes = React.useMemo(
() => ({
// Or, override the default text filter to use
// "startWith"
text: (rows, id, filterValue) => {
return rows.filter((row) => {
const rowValue = row.values[id];
return rowValue !== undefined
? String(rowValue)
.toLowerCase()
.startsWith(String(filterValue).toLowerCase())
: true;
});
}
}),
[]
);
const tableInstance = useTable(
{ columns, data, defaultColumn, filterTypes },
useGlobalFilter, // useGlobalFilter!
useSortBy
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
state,
preGlobalFilteredRows,
setGlobalFilter
} = tableInstance;
// const showUserCheck = (id) => {};
const thumbVertical = ({ style, ...props }) => {
const finalStyle = {
...style,
visibility: "hidden"
};
return <div style={finalStyle} {...props} />;
};
return (
<div className={"table-container"}>
<Scrollbars
autoHeight
autoHeightMin={0}
autoHeightMax={"calc(100vh - 40px)"}
renderThumbVertical={thumbVertical}
>
<>
<div className={"row mx-auto my-2"}>
<div className={"col-8"}>
<GlobalFilter
preGlobalFilteredRows={preGlobalFilteredRows}
globalFilter={state.globalFilter}
setGlobalFilter={setGlobalFilter}
/>
</div>
</div>
<table
{...getTableProps()}
className={
"table table-striped table-hover table-borderless ingredients-table"
}
>
<thead>
{headerGroups.map((headerGroup, i) => (
<tr {...headerGroup.getHeaderGroupProps()} key={i}>
{headerGroup.headers.map((column, thInd) => (
// Add the sorting props to control sorting. For this example
// we can add them into the header props
<th
{...column.getHeaderProps(column.getSortByToggleProps())}
key={thInd}
>
{column.render("Header")}
{/* Add a sort direction indicator */}
<span>
{column.isSorted ? (
column.isSortedDesc ? (
<FontAwesomeIcon icon={faSortDown} size={"lg"} />
) : (
<FontAwesomeIcon icon={faSortUp} size={"lg"} />
)
) : (
""
)}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, ind) => {
// console.log({ row });
prepareRow(row);
return (
<tr
{...row.getRowProps()}
onClick={(e) =>
setTheSelectedIngredient(e, row.values.col1)
}
onMouseEnter={() => setShowIngredientCheckID(row.id)}
onMouseLeave={() => setShowIngredientCheckID(-1)}
key={ind}
className={`${
selectedIngredient.name === row.values.col1
? "selected"
: ""
}`}
data-testid={"ingredient-row"}
>
{row.cells.map((cell, tdInd) => {
return (
<td {...cell.getCellProps()} key={tdInd}>
{tdInd === 0 ? (
selectedIngredient.name === row.values.col1 ? (
<>
<FontAwesomeIcon
icon={faCheck}
className={"white"}
/>{" "}
</>
) : showIngredientCheckID === row.id ? (
<>
<FontAwesomeIcon
icon={faCheck}
className={"gray"}
/>{" "}
</>
) : (
<>
<FontAwesomeIcon
icon={faCheck}
className={"clear"}
/>{" "}
</>
)
) : (
tdInd === 1 && (
<FontAwesomeIcon
icon={faEllipsisV}
className={"three-dots"}
/>
)
)}
{cell.render("Cell")}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</>
</Scrollbars>
</div>
);
};
export default IngredientsTable;
And here is a code sandbox where you can see the issue happening. https://codesandbox.io/s/relaxed-benz-co8ub?file=/src/components/pieces/IngredientsTable.js:0-7960
At the top, click where it says "100 records..." and try typing something. The focus leaves after each character. I can't figure out what would be causing that.
Just move GlobalFilter declaration outside IngredientsTable as re-rendering of parent is creating new instance of it every time, which is causing to loose focus.
Fixed CSB - https://codesandbox.io/s/bold-browser-mwuxd

serch and redirect to id in react js

i trying to make a history.push on button click
i have this search bar that will show the names of doctors when serched {suggestion.firstname}
i am trying to pass {suggestion.id } as url when cliked on the li corresponding
but here when i type and if the {suggestion.firstname} first letter comes then it automaticaly is redirecting when typing in the input field.
finddoctor is working like onchange funtion but i have written onclick funtion
function finddoctor(e) {
console.log(e);
history.push(`/detiled/${e} `);
}
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<>
<li
style={styles.listyle}
onClick={finddoctor(suggestion.id)}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("admin-panel/all-doctors-list/")
.then((res) => {
const data = res.data;
setShowSerch(data);
});
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
change it do this, and it should work.
<li
style={styles.listyle}
onClick={() => finddoctor(suggestion.id)}
key={index}
>
{suggestion.firstname}
</li>

not able to redirect in react js using history.push

i trying to make a history.push on button click
i have this search bar that will show the names of doctors when serched {suggestion.firstname}
i am trying to pass {suggestion.id } as url when cliked on the li corresponding
when clicked on li no call going to finddoctor
function finddoctor(e) {
console.log(e);
history.push(`/detiled/${e} `);
}
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<li
style={styles.listyle}
onClick={()=> finddoctor(suggestion.id)}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("admin-panel/all-doctors-list/")
.then((res) => {
const data = res.data;
setShowSerch(data);
});
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
Are you using "react-router-dom" in your project?
In this case, you should use the history object in a specific way. For example, you can get it with the useHistory hook.
import { useHistory } from "react-router-dom";
const SuggestionsList = ({
suggestions,
inputValue,
displaySuggestions,
selectedSuggestion,
}) => {
let history = useHistory();
const finddoctor = (e) => {
console.log(e);
history.push(`/detiled/${e} `)
};
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list">
{suggestions.map((suggestion, index) => {
return (
<li
onClick={() => finddoctor(suggestion.id)}
key={index}
>
{suggestion.firstname}
</li>
)
})}
</ul>
)
} else {
return <div>No suggestions available...</div>
}
}
return <></>
};

React Router 3 Breadcrumb Dropdown

I am trying to change the route based on the dropdown option in the breadcrumb.Any suggestions would be appreciated.
This is the component that is getting the first option but I am not sure how to gran the other options after the dropdown is generated.
const MenuItemViews = ({ params: { category, subCategory, item }, children }) => {
const menuItem = sideNavData.lookupItem(category, subCategory, item);
console.log(menuItem);
console.info(children);
return (
<div>
{
menuItem.name === 'Bill'
? <div>
<h2>Labels</h2>
{
!children
? <Link to={`/${category}/${subCategory}/${item}/${menuItem.childItems[0].name}`} >
<Image src={menuItem.content} />
</Link>
: children
}
</div>
: <ContentContainer>
<h1>{menuItem.name}</h1>
<Image src={menuItem.content} alt='item' />
</ContentContainer>
}
</div>
);
};
this is the component that is displaying the breadcrumbs.
const labelDropdownOptions = [
{ key: 'OptionOne', value: 'OptionOne', text: 'OptionOne' },
{ key: 'OptionTwo', value: 'OptionTwo', text: 'OptionTwo' },
{ key: 'OptionThree', value: 'OptionThree', text: 'OptionThree' },
];
class TopBar extends Component {
resolver = (key) => {
if (key === 'Home') {
return key;
}
return this.props.params[key];
}
dropdownLink = (link, key, text, index, routes) => {
console.log(routes);
if (text === 'OptionOne') {
return (
<Dropdown defaultValue={'OptionOne'} key={key} options={labelDropdownOptions} />
);
}
return <Link key={key} to={link}>{text}</Link>;
}
render() {
const { routes, params } = this.props;
return (
<TopBarHeader>
<IndexLink to='/'>
<HomeIcon><Icon name='home' /></HomeIcon>
</IndexLink>
<BreadcrumbWrapper>
<Breadcrumbs
createLink={this.dropdownLink}
params={params}
resolver={this.resolver}
routes={routes}
/>
</BreadcrumbWrapper>
</TopBarHeader>
);
}
}
I was able to do this by passing this.props.router.push into the onClick prop and specifying the value.
class TopBar extends Component {
resolver = (key) => {
if (key === 'Home') {
return key;
}
return this.props.params[key];
}
dropdownLink = (link, key, text, index, routes) => {
const category = sideNavData.lookupCategory(this.props.category);
if (link === '/TabTwo/Names/Bill/OptionOne' || link === '/TabTwo/Names/Bill/OptionTwo' || link === '/TabTwo/Names/Bill/OptionThree') {
return (
<span key={index}>
{
Object.keys(category).map((subCategory, i) => {
return (
<span key={i}>
{
Object.keys(category[subCategory]).map((item, itemIndex) => (
<span key={itemIndex}>
{
category[subCategory][item].name === 'Bill'
? <Dropdown
defaultValue={'OptionOne'}
options={category[subCategory][item].childItems}
onChange={(event, data) => { this.props.router.push(`/${this.props.category}/${subCategory}/${category[subCategory][item].name}/${data.value}`); }}
/>
: null
}
</span>
))
}
</span>
);
})
}
</span>
);
}
return <Link key={key} to={link}>{text}</Link>;
}
render() {
const { routes, params } = this.props;
return (
<TopBarHeader>
<IndexLink to='/'>
<HomeIcon><Icon name='home' /></HomeIcon>
</IndexLink>
<BreadcrumbWrapper>
<Breadcrumbs
createLink={this.dropdownLink}
params={params}
resolver={this.resolver}
routes={routes}
/>
</BreadcrumbWrapper>
</TopBarHeader>
);
}
}

Resources