How can I add custom props to an Option using React Select? - javascript

My goal is to add a tooltip when the user expands the menu and hovers over the items listed. To do this, I need to add data-tip and data-for to the menu items.
The options I'm passing to React Select looks like this:
[
{ value: 1, label: 'Onw' },
{ value: 2, label: 'Two' },]
I'd like to just add the prop info there but I don't think there's a way. Do I need to customize the rendering of each option? Any tips on how to do this would be appreciated. Thanks.

You can simply wrap your custom Option inside a function then assign custom props for it
components={{ Option: (optionProps) => <Option {...optionProps} onEditItem={onEditItem}
onDeleteItem={onDeleteItem} /> }}

react-select seems to be only accepting value and label as props (and possibly other HTML attributes that an <option> accepts), so I did this with the help of a function:
const options = [
{ value: "a", label: "A", tooltip: "yellow" },
{ value: "b", label: "B", tooltip: "orange" },
{ value: "c", label: "C", tooltip: "blue" },
];
function getTooltip(value) {
return options.find(x => x.value === value).tooltip;
}
/* A custom option component as an example */
const Option = (props) => {
return (
<components.Option {...props}>
<div data-tip={getTooltip(props.value)}>
{props.label}
</div>
</components.Option>
);
};
/* Pass the custom component to <Select> */
return (
<Select options={options} components={{ Option }} />
)

Related

Dynamic Bar Labeling on Ant Design Charts

I'm trying to create an ant design chart with some custom labels on the bars based on what data they contain. Is it possible to do this dynamically? I want the labels to say "Water" and "Land", which is the type of each respective data. Here is my code which I'm playing around in the Ant Design Charts Sandbox
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import { Bar } from '#ant-design/plots';
const DemoBar = () => {
const data = [
{
year: '1991',
value: 3,
type: 'Water',
},
{
year: '1991',
value: 4,
type: 'Land',
},
];
const config = {
data: data.reverse(),
isStack: true,
xField: 'value',
yField: 'year',
seriesField: 'type',
legend: false,
label: {content: data[0].type}, // How can the content link to the data?
interactions: [{ type: 'tooltip', enable: false }]
};
return <Bar {...config} />;
};
ReactDOM.render(<DemoBar />, document.getElementById('container'));
I figured it out: Javascript lets you create a function here. Replacing the label line with this line solved it:
label: {content: ({ type }) => { return type },},
For typescript the line is:
label: { content: ({ type }: any) => { return type }, },

Combine Chart.js programmatic and legend based dataset toggling

How can I make programmatic toggling of datasets play together with the one from ChartJS' legend clicking?
Here's a repro of my problem:
Run below snippet.
Toggle fruit data off with button: just works!
Toggle fruit data back on with button: just works!
Toggle fruit off by clicking the legend item: works.
Toggle fruit back on by clicking the legend item: works.
Toggle fruit again with the button: ⚠ broken!
I expect the custom logic to play nice with ChartJS' built in feature.
const chart = new Chart(document.getElementById("chart").getContext("2d"), {
type: "bar",
data: {
labels: ["label1", "label2", "label3"],
datasets: [
{ label: "Fruit", backgroundColor: "IndianRed", data: [1,2,3] },
{ label: "Veggies", backgroundColor: "LightGreen", data: [2,5,3] },
]
}
});
function togglestuff() {
const dataset = chart.data.datasets.find(ds => ds.label === "Fruit");
dataset.hidden = !dataset.hidden;
chart.update();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.2/chart.min.js"></script>
<div><canvas id="chart" height="75px"></canvas></div>
<button id="hide" onclick="togglestuff()">
Toggle fruit programmatically
</button>
What am I doing wrong?
Turns out hidden works, but there's also a property visible which then needs to be programmatically used with setDatasetVisibility(datasetIndex, visibility) .
So, one method would be:
Get dataset index with indexOf or similar
Get metadata at that index with chart.getDatasetMeta(index)
Use metadata.visible and pass it to setDatasetVisibility(...)
chart.update() as per usual
const chart = new Chart(document.getElementById("chart").getContext("2d"), {
type: "bar",
data: {
labels: ["label1", "label2", "label3"],
datasets: [
{ label: "Fruit", backgroundColor: "IndianRed", data: [1,2,3] },
{ label: "Veggies", backgroundColor: "LightGreen", data: [2,5,3] },
]
}
});
function togglestuff() {
const dataset = chart.data.datasets.find(ds => ds.label === "Fruit");
const index = chart.data.datasets.indexOf(dataset);
const metadata = chart.getDatasetMeta(index);
chart.setDatasetVisibility(
index,
!metadata.visible,
);
chart.update();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.2/chart.min.js"></script>
<div><canvas id="chart" height="75px"></canvas></div>
<button id="hide" onclick="togglestuff()">
Toggle fruit programmatically
</button>

Check if value is undefined before render React Native

I am using (Drop Down Picker library) to display categories of my data which I get from api call. I am waiting for api to fetch the data and then plan to display the categories. The problem is that I need to reformat data first and use hooks before render, but I can't get it right.
how does the data for picker looks like:
items={[
{label: 'USA', value: 'usa'},
{label: 'UK', value: 'uk'},
{label: 'France', value: 'france'/>},
]}
my hooks:
const [country, setCountry] = useState("0");
const [categoryData, setCategoryData] = useState();
const [testingCategories, setTestingCategories] = useState({
label: null,
value: null,
});
my effect hooks and reformatting the data:
//this hook calls api and sets the data to categories data of my type
useEffect(() => {
getData(api.API_GET_DEPS, setIsLoading, setCategoryData);
}, []);
//this hooks reformats the data into acceptable format for picker
useEffect(() => {
setTestingCategories(
categoryData
? categoryData.map((item) => ({
label: item.DEPNAME, //label
value: item.DEPID, //value
}))
: [{ label: null, value: null }]
);
}, [categoryData]);
my render of drop down:
<View style={styles.container}>
{testingCategories ? (
<DropDownPicker
dropDownMaxHeight={300}
placeholder="All"
defaultValue={country}
items={testingCategories}
containerStyle={{ height: 50 }}
style={{ backgroundColor: "#fafafa" }}
itemStyle={{
justifyContent: "flex-start",
}}
dropDownStyle={{ backgroundColor: "#fafafa" }}
onChangeItem={(item) => {
console.log("changed");
}}
onOpen={() => setContainerOpacity(0.1)}
onClose={() => setContainerOpacity(1)}
labelStyle={{
fontSize: 16,
color: colors.dark,
fontWeight: "600",
}}
arrowSize={25}
arrowColor={colors.dark}
/>
) : (
<></>
)}
I get an error that drop down picker can't match the defaultValue with any label in testingCategories because it is null and haven't loaded yet. I suppose I am using setter wrong because I can't check whether testingCategories's first element was loaded. What am I doing wrong?
You have a typing mismatch between your useState definition and where you use setTestingCategories later.
In your useState, you define the initial value as a singular object:
useState({
label: null,
value: null,
});
However, what you probably want is an empty array:
useState([]);
I would also change your current line [{ label: null, value: null }] to just be an empty array [].
Then, your testingCategories flag will work, because testingCategories will initially be an array of length 0, which will fail the truthiness test.

Angular ng-options value DOES NOT update when scope changes

I'm having problems creating a select option dropdown using ng-options.
Scope structure
vm.filters = {
perPage: 10,
settings: {
perPage: {
intervals: [
{
label: 'All',
value: null
}, {
label: 10,
value: 10
}, {
label: 25,
value: 25
}, {
label: 50,
value: 50
}, {
label: 100,
value: 100
}
]
}
}
}
Dom Element (angular generates the option tags)
<select name="selectPerPage" data-ng-model="vm.filters.perPage" data-ng-options="interval.value as interval.label for interval in vm.filters.settings.perPage.intervals">
<option label="All" value="object:null">All</option>
<option label="10" value="number:10" selected="selected">10</option>
<option label="25" value="number:25">25</option>
<option label="50" value="number:50">50</option>
<option label="100" value="number:100">100</option>
</select>
When I change the value property of the first object in the intervals array from null to an actual number, scope for the option elements do not update. Why? How can I get it to update? It's almost like ng-model isn't referencing when scope updates.
If you use ng-options, you should not hard-coded options as you did in your select. You can only hard one (generally a null value).
<select ng-model="vm.filters.perPage"
ng-options="interval.value as interval.label for interval in vm.filters.settings.perPage.intervals">
<option label="All" value="">All</option>
</select>
I don't know what type interval.value is (array, object, string?), but it should be saved in vm.filters.perPage (supposing vm.filters exists).
you can try with ng-repeat, this will update the options dynamically. But unfortunately if the first element is selected when you update the data, the ng-change event won't be fired which means the value binded with ng-model won't be updated and you have to update it manually.
angular.module("app", [])
.controller("myCtrl", function($scope) {
$scope.filters = {
perPage: {
intervals: [{
label: 'All',
value: null
}, {
label: 10,
value: 10
}, {
label: 25,
value: 25
}, {
label: 50,
value: 50
}, {
label: 100,
value: 100
}]
}
};
$scope.update = function() {
$scope.filters.perPage.intervals[0].value = 1000;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="myCtrl">
<!--<select name="selectPerPage" data-ng-model="perPage" data-ng-options="interval.value as interval.label for interval in filters.perPage.intervals"> -->
<select name="selectPerPage" data-ng-model="perPage">
<option ng-repeat="interval in filters.perPage.intervals" value="{{interval.value}}">{{interval.label}}</option>
</select>
{{perPage}}
<button ng-click="update()">Update</button>
</div>

YUI 2 Populate Select with JSON

Really simple question but I can't find an answer. I know how to do this in regular javascript and I know how to do this in jquery. Right now if I have this:
var states = [{"name":"alaska","id":1},{"name":"alabama","id":2];
Is there an YUI based method to push that into a select box, to set the selected item, to get the selected item back? If so, where's the online example? If not I'll just use javascript.
Yes, there is. You can do something like this:
var buttonMenu = [
{ text: "Alaska", value: 1, onclick: { fn: onStateClick } },
{ text: "Alabama", value: 2, onclick: { fn: onStateClick } },
...];
.. and then use this in a YUI menu button configuration:
var statesButton = new YAHOO.widget.Button({ type: "menu", label: "Alabama", name: "mymenubutton", menu: buttonMenu, container: containerElement });
More details on the YUI button example page.
Hope that helps.

Categories

Resources