I would like to realize a textfield and a dropdown. When users select an option in the dropdown, the selection will be written to the textfield. And users can write whatever they want in the textfield; the dropdown list is supposed to give users some possible examples/ideas.
It's more or less like a combobox, but I find combobox is always one line (please correct me if I'm wrong). I would expect the textarea to be big and have several lines.
I have written the following code, one thing I would like to improve is that, after selecting an option, I would like the dropdown button to always show Examples of sentences rather than the selected option.
Does anyone know how to achieve this?
I'm open to other third-party components that could realize similar logics.
StackBlitz: https://stackblitz.com/edit/react-ts-yrhmfn?file=App.tsx,index.tsx
import {
FluentProvider,
webLightTheme,
} from '#fluentui/react-components';
import {
Dropdown,
Option
} from '#fluentui/react-components/unstable';
import { TextField } from '#fluentui/react';
import * as React from 'react';
export default class Grouped extends React.Component<{}, { value: any }> {
constructor(props) {
super(props);
this.state = { value: '' };
}
land = ['A long sentence', 'Another long sentence', 'Another another long sentence'];
render() {
return (
<div>
<FluentProvider
className="fluent-provider"
style={{ display: 'flex' }}
theme={webLightTheme}
>
<Dropdown
placeholder="Examples of sentences"
onOptionSelect={(e, data) => {
if (data.optionText !== undefined)
this.setState({ value: data.optionText });
}}
>
{this.land.map((option) => (
<Option key={option}>{option}</Option>
))}
</Dropdown>
</FluentProvider>
<br/>
<TextField
label="Write a sentence:"
value={this.state.value}
multiline
rows={3}
autoAdjustHeight
resizable={false}
/>
</div>
);
}
}
Well, I can solve this but it's a bit of a hack :) My professional suggestion is to consider a different third party library. I looked at the FluentUI and there doesn't seem to be a way to control the currently selected value.
If there were, you could simply set the currently selected value to undefined. Barring that, here's some code that achieve what you're asking for:
{this.state.visible && (
<Dropdown
placeholder="Select an animal"
onOptionSelect={(e, data) => {
if (data.optionText !== undefined) {
this.setState({ value: data.optionText, visible: false });
setTimeout(() => {
this.setState({ value: data.optionText, visible: true });
}, 0);
}
}}
>
{this.land.map((option) => (
<Option key={option}>{option}</Option>
))}
</Dropdown>
)}
What this code does is essentially "unload" the dropdown after a selection is made and then reload it one tick later, which resets its internal state. It's definitely a "hacky" solution, but it gets the job done.
The dropdown uses slots, so actually it's really easy to achieve. You can simply overwrite the props of the underlying button to display always the same text:
<Dropdown
button={{children: "Placeholder"}}
>
<Option>A</Option>
<Option>B</Option>
<Option>C</Option>
<Option>D</Option>
</Dropdown>
Related
I have a body of clarity forms that have been prepared for an Angular application and I'm trying to use them in React. However even getting the simplest form to work is beyond me right now, there's inadequate examples and documentation and I'm not a frontend developer. Based on the almost zero examples I can find I'm guessing very few people are using Clarity Forms in React.
Here's an example form with multiple attempts to get the CdsInput field to do something. Oddly I got onClick to work but I can't type in the field on the form when it displays.
render() {
return (
<main cds-layout="p:lg vertical gap:lg" cds-text="body">
<h1 cds-text="heading">Clarity in React</h1>
{this.state.show ? (
<CdsAlertGroup status="warning">
<CdsAlert onCloseChange={() => this.setState({ show: false })} closable>Hello World</CdsAlert>
</CdsAlertGroup>
) : (
''
)}
<CdsButton status="success" onClick={() => this.setState({ show: true })}>
Show Alert
</CdsButton>
<CdsInput type="text" id="formFields_5" placeholder="Email" defaultValue="DEFAULT TEXT!" onClick={() => this.setState({ show: false })} >
Text inside tags
</CdsInput>
</main>
);
}
So clicking on the field influences the alert so onClick works, but I can't type in the box and it's empty.
I've consulted the example React app on the Clarity Design System website, but it only shows some components and icons, it gives no example of input/select/interactive controls except buttons.
I am aware that React doesn't support WebComponents and so Clarity Forms isn't a natural fit, but when I import #cds/react am I compensating for this deficiency or do I also have to put a ref on the CdsInput ... I did try that but got too many errors about types and other things:
inputRef: React.RefObject<typeof CdsInput>;
constructor(props: any) {
super(props);
this.state = {
show: false,
};
this.inputRef = React.createRef<typeof CdsInput>();
}
componentDidMount() {
this.inputRef.current.nativeElement.then(element => {
element.focus();
});
}
render() {
return (
... <CdsInput ref={this.inputRef} >HEY THERE</CdsInput> ...
Does anyone know of a simple but functional example of a Clarity Form working in React using #cds/react that I could refer to?
I have tried to search my issue for a day and can not find a solution here. I am sorry if the question already been asked.
Here is what I am trying to achieve:
I have a dropdown in select, both are inside form.
I am using ant design in react and typescript application.
The issue is when I click on the dropdown, if the dropdown at the bottom of the form, it will not show all the elements in the dropdown; I have included images to further show the problem.
https://i.stack.imgur.com/VWYUG.png
This is what I have tried to do:
I can see all dropdown elements even the last dropdown in the form, but the issue is when I do not select an element from the dropdown and do not close it, the dropdown remain opens and floating in the page until I click something.
<Form layout=“inline”>
<div>
<Form.Item name=“ordering” label={label} style={{ minWidth: size(5) }}>
<Select
dropdownStyle={{ position: “fixed” }}
showSearch
options={screens ? getScreenOptions(screens) : []}
onChange={(e) => {
if (screens) {
onFieldsMove(fields, screen, screens[e as number]);
}
}}
getPopupContainer={(triggerNode: HTMLElement) =>
(triggerNode?.parentNode as HTMLElement) || document.body
}
/>
</Form.Item>
</div>
</Form>
);
export const getScrollParent = (node: HTMLElement | undefined): HTMLElement => {
if (!node) {
return document.body;
}
if (node.scrollHeight < node.clientHeight) {
return node;
} else {
return getScrollParent(node.parentNode as HTMLElement);
}
}; ```
Can some please explain to me the getScrollParent function?
I am sorry if I am asking basic questions, I am new developer.
[1]: https://i.stack.imgur.com/VWYUG.png
I have made autocomplete features using Downshift using react js. But the problem is when I am searching for something its input field value is disappearing when I click on the outside. Here is the sample code.
import logo from './logo.svg';
import './App.css';
import React, { useState } from "react";
import Highlighter from "react-highlight-words";
import Downshift from "downshift";
import axios from 'axios';
function App() {
const [names, setnames] = useState([{
const [searchTerm, setSearchTerm] = useState('')
const [movie, setmovie] = useState([])
fetchMovies = fetchMovies.bind(this);
inputOnChange = inputOnChange.bind(this);
function inputOnChange(event) {
if (!event.target.value) {
return;
}
fetchMovies(event.target.value);
}
function downshiftOnChange(selectedMovie) {
alert(`your favourite movie is ${selectedMovie.title}`);
}
function fetchMovies(movie) {
const moviesURL = `https://api.themoviedb.org/3/search/movie?api_key=1b5adf76a72a13bad99b8fc0c68cb085&query=${movie}`;
axios.get(moviesURL).then(response => {
setmovie(response.data.results);
// this.setState({ movies: response.data.results });
});
}
return (
<Downshift
onChange={downshiftOnChange}
itemToString={item => (item ? item.title : "")}
>
{({
selectedItem,
getInputProps,
getItemProps,
highlightedIndex,
isOpen,
inputValue,
getLabelProps
}) => (
<div>
<label
style={{ marginTop: "1rem", display: "block" }}
{...getLabelProps()}
>
Choose your favourite movie
</label>{" "}
<br />
<input
{...getInputProps({
placeholder: "Search movies",
onChange: inputOnChange
})}
/>
{isOpen ? (
<div className="downshift-dropdown">
{movie
.filter(
item =>
!inputValue ||
item.title
.toLowerCase()
.includes(inputValue.toLowerCase())
)
.slice(0, 10)
.map((item, index) => (
<div
className="dropdown-item"
{...getItemProps({ key: index, index, item })}
style={{
backgroundColor:
highlightedIndex === index ? "lightgray" : "white",
fontWeight: selectedItem === item ? "bold" : "normal"
}}
>
{item.title}
</div>
))}
</div>
) : null}
</div>
)}
</Downshift>
);
}
export default App;
This is the sample code I have written. Also, when I click shift+home, it is also not working.
Problem 1: when the user clicked the outside text field value whatever I searched this is disappearing.
Problem 2: shift + home is not working also.
Anyone has any idea how to solve this problem?
when the user clicked the outside text field value whatever I searched this is disappearing.
One way you could do it is to set the stateReducer on the Downshift component:
This function will be called each time downshift sets its internal state (or calls your onStateChange handler for control props). It allows you to modify the state change that will take place which can give you fine grain control over how the component interacts with user updates without having to use Control Props. It gives you the current state and the state that will be set, and you return the state that you want to set.
state: The full current state of downshift.
changes: These are the properties that are about to change. This also has a type property which you can learn more about in the stateChangeTypes section.
function stateReducer(state, changes) {
switch (changes.type) {
case Downshift.stateChangeTypes.mouseUp:
return {
...changes,
isOpen: true,
inputValue: state.inputValue,
};
default:
return changes;
}
}
This way if you click outside the text field the dropdown will stay open and the input value won't be reset.
For a list of all state change types see the documentation here
You might also be able to get something working using the onBlur prop on the input, but I didn't get that working.
I am looking to customize the multiselect and the way we create the display of showing selected options.
Right now, with many options selected the select component takes up a prohibitive amount of space for certain UIs. See example:
I'd like to utilize the out of the box chip display for selected options within the input, but I only want to show only a few selected options (like 3/4 max) and then add a "badge" count for the number of selected options that aren't shown in the value container in the input. The options that are selected but are past the max number of chips allowed to show in the input should show as selected within the dropdown list, while the chips that do show's values should not show in our dropdown.
I've implemented part of this with using a custom ValueContainer to show only the first few chip selections, and then adding a count of additional/"overflow" selections. I'm unsure of how I can utilize the prop hideSelectedOptions to achieve this to show selected items in the list only when my max is met without showing all of them since this prop takes a boolean.
Here's what I have so far: https://codesandbox.io/s/custom-react-select-sjtib
import React, { Component } from "react";
import Select, { components } from "react-select";
import { colourOptions } from "./docs/data";
import "./example.css";
class CustomSelect extends Component {
state = {
values: []
};
handleChange = values => {
this.setState({ values });
};
render() {
const { values } = this.state;
return (
<div>
<Select
hideSelectedOptions={values.length < 3 ? true : false}
isMulti
options={colourOptions}
onChange={this.handleChange}
value={values}
components={{ ValueContainer }}
/>
</div>
);
}
}
export default CustomSelect;
const ValueContainer = ({ children, getValue, ...props }) => {
let maxToShow = 3;
var length = getValue().length;
let displayChips = React.Children.toArray(children).slice(0, maxToShow);
let shouldBadgeShow = length > maxToShow;
let displayLength = length - maxToShow;
return (
<components.ValueContainer {...props}>
{!props.selectProps.inputValue && displayChips}
<div className="root">
{shouldBadgeShow &&
`+ ${displayLength} item${length != 1 ? "s" : ""} selected`}
</div>
</components.ValueContainer>
);
};
I would personally keep hideSelectedOptions={false} and go for styles property usage (options property to be more exact) and setting display: 'none' for the ones which shouldn't be visible:
const styles = {
option: (base, value) => {
return (shouldBeShown(value) ? { ...base } : { display: 'none'});
}
};
shouldBeShown(value) is a custom function for checking if the particular option should be shown.
In order to get option data you can use value.data.
Then you can set styles={styles} in Select component:
<Select
hideSelectedOptions={false}
isMulti
styles={styles}
onChange={this.handleChange}
options={options}
value={values}
components={{ ValueContainer }}
/>
This code works fine if the user selects something from each dropdown menu, but if they forget to make a selection, it will just use the value selected from the previous dropdown menu. Also if they don't make any selection at all and submit, it will obviously submit the default value stored in the state which is "0".
Anyone happen to have a workaround for this? Thanks.
export class Content extends Component {
constructor(props){
super(props)
this.state = {
selectedOption: 0
}
}
handleOptionChange = e => {
this.setState({
selectedOption: e.target.value
})
}
handleSubmit = e => {
e.preventDefault()
}
render() {
let snowboardItems = this.props.snowboards.map((board,index) => {
return <div><form onSubmit={this.handleSubmit}>
<li key={index} className="list_item">
<div className="content_div1">
<h3>{board.name}</h3>
<h3>$ {board.price}</h3>
<h4>{board.terrain}</h4>
<h4>Shape: {board.shape}</h4>
<p>Board Length:</p>
<select value={this.state.selectedOption} onChange={this.handleOptionChange}>
{board.length.map((item, index) =>
<option value={item} key={index}>{item}</option>
)}
</select> cm
</div>
<div className="content_div2">
<button className="content_button" type="submit" onClick={() => this.props.addToCart({board}, this.state.selectedOption)}>Add to Cart</button>
<img className="image" src={board.imageurl} />
</div>
</li>
</form>
</div>
})
This is really a case where you should separate this into two components: one to render the list of items (you could do this in the parent passing the props too), and another to render the item and possibly handle its state.
If for some reason you can't though, you'll probably want to separate each board option into its own property on state. Here's an example where state is updated dynamically:
https://codesandbox.io/embed/snowboards-pl9r5
You should always code defensively, so in the example there's a "short circuit" check to make sure that a length was selected before adding it to the cart. Also the select field is marked as required so that you can use HTML5 as another fallback validator.
You can check it by trying to add an item without a length and also selecting different options and adding them to the cart (logging them in the console).
On another note: I changed it to more specific keys because mapping multiple lists and using the index as a key will result in duplicate keys. Keys are how react knows which item is which, and you don't want to confuse react!
P.S. Way to bum me out giving a snowboard example in the summer! lol Happy hackin'