How do I get the input text (not just the value) from a Material for Web select component? - material-design

In the docs, it specifies how to get the index and data-value, but not the input text:
import {MDCSelect} from '#material/select';
const select = new MDCSelect(document.querySelector('.mdc-select'));
select.listen('MDCSelect:change', () => {
alert(`Selected option at index ${select.selectedIndex} with value "${select.value}"`);
});

The following assumes you have more than one MDCSelect to initiate
import {MDCSelect} from '#material/select';
const selectElements = [].slice.call(document.querySelectorAll('.mdc-select'));
selectElements.forEach((selectEl) => {
const select = new MDCSelect(selectEl);
select.listen('MDCSelect:change', (el) => {
const elText = el.target.querySelector(`[data-value="${select.value}"`).innerText;
console.log(`Selected option at index ${select.selectedIndex} with value "${select.value}" with a label of ${elText}`);
});
});

Related

How to determine if "click" or "box-select" was used with Streamlit/Plotly to return data from chart to Streamlit

I'm not a Javascript/Typescript/React dev. I'm hacking my way through this for a work project.
I'm using Streamlit, with plotly.
I'm hacking the basic code from streamlit-plotly-events.
I was trying to have the click or box-select information passed back with the data selected via the plotlyEventHandler() (see code below.) However, both this.props.args["click_event"] and this.props.args["select_event"] are true, regardless of whether you use box-select in the plotly chart, or click a single data point in the chart.
I thought of assuming if there is only one data point, then it was a click - but you can box select only one data point.
// import React, {useState,useEffect} from "react"
import React, { ReactNode } from "react"
//import React from "react"
import {
StreamlitComponentBase,
withStreamlitConnection,
Streamlit,
// ComponentProps,
} from "streamlit-component-lib"
import Plot from "react-plotly.js"
class StreamlitPlotlyEventsCapture extends StreamlitComponentBase {
public render = (): ReactNode => {
// Pull Plotly object from args and parse
const plot_obj = JSON.parse(this.props.args["plot_obj"]);
const override_height = this.props.args["override_height"];
const override_width = this.props.args["override_width"];
// Event booleans
const click_event = this.props.args["click_event"];
const select_event = this.props.args["select_event"];
const hover_event = this.props.args["hover_event"];
Streamlit.setFrameHeight(override_height);
return (
<Plot
data={plot_obj.data}
layout={plot_obj.layout}
config={plot_obj.config}
frames={plot_obj.frames}
onClick={click_event ? this.plotlyEventHandler : function(){}}
onSelected={select_event ? this.plotlyEventHandler : function(){}}
onHover={hover_event ? this.plotlyEventHandler : function(){}}
style={{width: override_width, height: override_height}}
className="stPlotlyChart"
/>
)
}
/** Click handler for plot. */
private plotlyEventHandler = (data: any) => {
// Build array of points to return
var clickedPoints: Array<any> = [];
//const util = require('util')//#33333 used with util.inspect(arrayItem) below
// I dont know why we can't directly use "this.variables" in the clickedPoints.push
// but we can't, so we create the variables here.
var wasClicked = this.props.args["click_event"];
var wasSelected = this.props.args["select_event"];
var wasHovered = this.props.args["hover_event"];
data.points.forEach(function (arrayItem: any) {
// console.log(util.inspect(arrayItem, {maxArrayLength: null, depth:null }))
clickedPoints.push({
// I dont know why we can't directly use "this.variables" here, but we can't
// so we use the variables created above.
clicked:wasClicked,
selected:wasSelected,
hovered:wasHovered,
x: arrayItem.x,
y: arrayItem.y,
curveNumber: arrayItem.curveNumber,
pointNumber: arrayItem.pointNumber,
pointIndex: arrayItem.pointIndex
})
});
// Return array as JSON to Streamlit
Streamlit.setComponentValue(JSON.stringify(clickedPoints))
}
}
export default withStreamlitConnection(StreamlitPlotlyEventsCapture)

Calculate sum of values in Cypress

I am using BDD/Cucumber with Cypress. I want to calculate the sum of some rows of table.
This is my step definition:
And("I add up all the total on hand",()=>{
const sumOnHand = itemListPage.totalOnHandAmsterdam()+itemListPage.totalOnHandDelft()
cy.log(sumOnHand)
})
And this is my js page:
totalOnHandAmsterdam() {
cy.get(':nth-child(2) > .dx-grandtotal > span').invoke('text').then(text =>{
const ttOnHandAmst = text
return ttOnHandAmst;
})
}
totalOnHandDelft() {
cy.get(':nth-child(11) > .dx-grandtotal > span').invoke('text').then(text =>{
const ttOnHandDelft = text
return ttOnHandDelft;
})
}
But this is the output of the calculation:
Any ideas on how can I sum up this value is appreciated.
You can't use the results of totalOnHandAmsterdam() and totalOnHandDelft() directly in a summation because
they don't return anything (the return inside .then(text => does not return the value from the function).
Cypress commands don't return values, they add the values to the command queue
You can do it like this
totalOnHandAmsterdam() {
return cy.get(':nth-child(2) > .dx-grandtotal > span')
.invoke('text').then(parseInt)
}
totalOnHandDelft() {
return cy.get(':nth-child(11) > .dx-grandtotal > span')
.invoke('text').then(parseInt)
}
And("I add up all the total on hand", () => {
itemListPage.totalOnHandAmsterdam().then(ams => // get value on command queue
itemListPage.totalOnHandDelft().then(delft => // get other value
const sumOnHand = ams + delft;
cy.log(sumOnHand)
})
})
})
The key to accessing command return values is using .then() after the command.
It's annoying but necessary because Cypress ensures that the web page has received data from the server before evaluating the element text.
Since the test runs faster than web page fetches data, it can easily evaluate the text before the page is fully populated.
You have to convert your texts to numbers and then add it. You can simply add + in front of the number to convert them into Integers. Also I have added trim() in case your strings have any unwanted spaces.
And('I add up all the total on hand', () => {
const sumOnHand =
+itemListPage.totalOnHandAmsterdam().trim() + +itemListPage.totalOnHandDelft().trim()
cy.log(sumOnHand)
})
You could set the function results as aliases.
Since the code is asynchronous, access it within cy.then().
totalOnHandAmsterdam() {
cy.get(':nth-child(2) > .dx-grandtotal > span')
.invoke('text')
.then(parseInt)
.as('amsterdamTotal') // alias will set this.amsterdamTotal
}
totalOnHandDelft() {
return cy.get(':nth-child(11) > .dx-grandtotal > span')
.invoke('text')
.then(parseInt)
.as('defltTotal') // alias will set this.delftTotal
}
And("I add up all the total on hand", function() { // use function() to access "this"
po.totalOnHandAmsterdam()
po.totalOnHandDelft()
cy.then(() => {
const sumOnHand = this.amsterdamTotal + this.defltTotal;
cy.log(sumOnHand)
})
})

Svelte Store. Spread syntax is not merging - just adding

I am trying to add some brocolli to my basket in the svelte store I have created. My code adds the brocooli to the basket but then duplicates the baskets and adds a whole new basket to my store. Not sure if the problem is caused by my lack of understanding of javascript or svelte.
Desired result
Basket 1 OrangePineapple Basket 2 BananaApplePlumwalnuthazelnutnutmegbroccoli
ACTUAL RESULT
Basket 1 OrangePineapple Basket 2 BananaApplePlumwalnuthazelnutnutmeg Basket 2 BananaApplePlumwalnuthazelnutnutmegbroccoli
Link to svelte codebox where you can view and run code
https://svelte.dev/repl/80d428000a3f425da798cec3450a59d4?version=3.46.2
if you click the button you see that my basket is duplicating. I am just trying to add the brocooli to the basket.
code below
import { writable } from 'svelte/store';
export const storeBaskets = writable([
{
"name": "Basket 1",
"items": ["Orange", "Pineapple"]
},
{
"name": "Basket 2",
"items": ["Banana", "Apple","Plum","walnut","hazelnut","nutmeg"]
}
])
//Local functions
export const add = (item,basketIndex) => {
storeBaskets.update(val => {
const newItems = [...val[basketIndex].items, item]
const newBasket = {'name':val[basketIndex].name,'items':newItems}
val = [...val,newBasket]
return val
})
val = [...val,newBasket]
With this line you're copying the previous store value and adding the newBasket "on top". That's how the spread operator works with arrays
let arr = [1,2,3]
let n = 4
let arr2 = [...arr, n]
console.log(arr2) // [ 1 , 2 , 3 , 4 ]
I wonder if you might have thought of the different behaviour when spreading an object, where an already existing entry might be overriden if the key already exists
let obj = {key: 'value'}
let key = 'newValue'
let obj2 = {...obj, key}
console.log(obj2) // { key: "newValue" }
To make your code working you could replace the line by val[basketIndex] = newBasket
export const add = (item,basketIndex) => {
storeBaskets.update(val => {
const newItems = [...val[basketIndex].items, item]
const newBasket = {'name':val[basketIndex].name,'items':newItems}
val[basketIndex] = newBasket
return val
})
}
Or, instead of spreading, simply push the new value directly to the according nested array in just one line
export const add = (item,basketIndex) => {
storeBaskets.update(val => {
val[basketIndex].items.push(item)
return val
})
}
You might not need to spread, because it's an array, you'r spreading the existing items of the array and then adding the new basket to it. You can map and replace by basketIndex, like:
export const add = (item,basketIndex) => {
storeBaskets.update(val => {
const newItems = [...val[basketIndex].items, item]
const newBasket = {'name':val[basketIndex].name,'items':newItems}
return val.map((basket, i) => i === basketIndex ? newBasket : basket)
})
}
(Working example)

Filter items in Redux state with searchbox

I have a list of objects in my redux state. I am trying to filter them using a searchbox and dispatching an action on every change. My state does update when I type but doesn't go back (doesn't show all contents) when I delete. I believe that I'm modifying the state and so when search bar is empty again, there is nothing left to filter.
header.js
export const Header = () =>{
const locationsDropdown = useSelector(selectAllLocations);
const departmentsDropdown = useSelector(selectAllDepartments);
const employees = useSelector(selectAllEmployees)
const dispatch = useDispatch()
const [searchField, setSearchField] = useState("")
const handleChange = (e) => {
setSearchField(e.target.value)
dispatch(listFiltered(searchField))
console.log(employees)
}
snippet from employeeSlice.js (reducer action)
listFiltered:{
reducer(state, action) {
//filters the array but when search is deleted, items don't come back
return state.filter(item => item.fname.includes(action.payload))
}
}
If I try to use this function in header.js (where search field is located), everything works well.
const filtered = employees.filter(item => {
const itemName = item.fname
return itemName.includes(searchField)
})
Problem is that header.js component is not responsible for rendering items where needed and I don't how (if possible) to export 'filtered' result to other components

ES6 : Object restructuration for mailchimp api

I want to construct a object base on an array and another object.
The goal is to send to mailchimp api my users interests, for that, I've got :
//Array of skills for one user
const skillsUser1 = ["SKILL1", "SKILL3"]
//List of all my skills match to mailchimp interest group
const skillsMailchimpId = {
'SKILL1': 'list_id_1',
'SKILL2': 'list_id_2',
'SKILL3': 'list_id_3',
}
//Mapping of user skill to all skills
const outputSkills = skillsUser1.map((skill) => skillsMailchimpId[skill]);
console.log(outputSkills);
The problem is after, outputSkill get me an array :
["ID1", "ID3"]
But what the mailchimp api need, and so what I need : :
{ "list_id_1": true,
"list_id_2": false, //or empty
"list_id_3" : true
}
A simple way would be this (see comments in code for explanation):
// Array of skills for one user
const skillsUser1 = ["SKILL1", "SKILL3"]
// List of all my skills match to mailchimp interest group
const skillsMailchimpId = {
'SKILL1': 'list_id_1',
'SKILL2': 'list_id_2',
'SKILL3': 'list_id_3',
}
// Create an output object
const outputSkills = {};
// Use `Object.entries` to transform `skillsMailchimpId` to array
Object.entries(skillsMailchimpId)
// Use `.forEach` to add properties to `outputSkills`
.forEach(keyValuePair => {
const [key, val] = keyValuePair;
outputSkills[val] = skillsUser1.includes(key);
});
console.log(outputSkills);
The basic idea is to loop over skillsMailchimpId instead of skillsUser.
But that is not very dynamic. For your production code, you probably want to refactor it to be more flexible.
// Array of skills for one user
const skillsUser1 = ["SKILL1", "SKILL3"]
// List of all my skills match to mailchimp interest group
const skillsMailchimpId = {
'SKILL1': 'list_id_1',
'SKILL2': 'list_id_2',
'SKILL3': 'list_id_3',
}
// Use `Object.entries` to transform `skillsMailchimpId` to array
const skillsMailchimpIdEntries = Object.entries(skillsMailchimpId);
const parseUserSkills = userSkills => {
// Create an output object
const outputSkills = {};
// Use `.forEach` to add properties to `outputSkills`
skillsMailchimpIdEntries.forEach(([key, val]) => {
outputSkills[val] = userSkills.includes(key);
});
return outputSkills;
}
// Now you can use the function with any user
console.log(parseUserSkills(skillsUser1));

Resources