How to add custom components into Ant Design stepper? - reactjs

I'm new to AntD and having a little trouble with the stepper component - specifically, how to add a custom component into each of the steps.
For example,
const steps = [
{
title: 'First',
content: 'First-content',
},
{
title: 'Second',
content: 'Second-content',
},
{
title: 'Last',
content: 'Last-content',
},
];
For simplicity, if I were to use the Autocomplete component would it be just:
{
title: 'First',
content: '<Autocomplete />',
},
No luck so far. Any advice is appreciated.

There is no content in Steps.Step.
You may be trying to render a custom component in Steps, then you need to provide ReactNode and not a string type:
<Steps>
<Steps.Step> title="Finished" description={<AutoComplete/>} />
</Steps>
Its all mentioned in the docs, I believe what you need is the basics of React.

Maybe you found this official example Switch Step, there is a steps variable like this:
const steps = [
{
title: 'First',
content: 'First-content',
},
{
title: 'Second',
content: 'Second-content',
},
{
title: 'Last',
content: 'Last-content',
},
];
This is a user-defined variable, NOT pre-defined by antd, you can use whatever property even data structure you want. You can assign the ReactNode to content property like:
const steps = [
{
title: 'First',
content: <Autocomplete />,
},
// ...
]
And render the content based on current step state:
<div className="steps-content">{steps[current].content}</div>
There is NO content prop for the Steps.Step component. This is another way different from the accepted answer.

Related

How to append html element in array of objects ( specific key value) in React Js

I want to use Google material icon like <span className="material-icons">settings</span> in my object value. Below is sample code (array of objects). Have to add above span as value of 'icon' key?
Google material icon reference link
let buttonData = [
{
label: "",
classNames: "headerButton",
icon: faBrush,
title: "Design",
onClick: (e) => {
this.setState({ ActiveButton: "Design" });
},
dropdownList: [["Design"]],
contextMenuStyle: "listGroupBottom",
contextListStyle: "lisItemBottom",
},
{ }, {} ]
Tried below code in icon
icon: '<span className="material-icons">settings</span>'
icon: "<span className="material-icons">settings</span>"

Trouble setting state with onClick function only with ImageMapper component (react-img-mapper)

I'm trying to create an interactive image map using the ImageMapper component from react-img-mapper (based on react-image-mapper) in my ReactJS app. I want to be able to click an "area" on ImageMapper map, and have it update clickedAreas state according to the function clickArea.
In the app, I render a list with checkmarks, and the checkmarks become green when the corresponding list item is included in clickedAreas state.
I call clickArea via onClick on list items themselves and it works as expected, but when I call it via the ImageMapper onClick prop it doesn't, and I'm having trouble figuring out why.
Here's a screen recording showing the differences in action.
NOTE: the ImageMapper onClick seems to be using the initially set state (useState([])), because when I give it an array with an element initially, it just keeps using that initial state with that element.
Here's a screenshot regarding the onClick prop from react-img-mapper documentation
Here's a screenshot of my code:
Here's my code also:
import React, { useState } from "react"
import ImageMapper from 'react-img-mapper';
function ImageMapQQ(props) {
const URL = "../images/qqmap.png"
const MAP = {
name: "qq-map",
areas: [
{ id: "NW1/4NW1/4", title: "NW1/4NW1/4", name: "NW1/4NW1/4", shape: "rect", coords: [0,0,50,50] },
{ id: "NE1/4NW1/4", title: "NE1/4NW1/4", name: "NE1/4NW1/4", shape: "rect", coords: [50,0,100,50] },
{ id: "NW1/4NE1/4", title: "NW1/4NE1/4", name: "NW1/4NE1/4", shape: "rect", coords: [100,0,150,50] },
{ id: "NE1/4NE1/4", title: "NE1/4NE1/4", name: "NE1/4NE1/4", shape: "rect", coords: [150,0,200,50] },
{ id: "SW1/4NW1/4", title: "SW1/4NW1/4", name: "SW1/4NW1/4", shape: "rect", coords: [0,50,50,100] },
{ id: "SE1/4NW1/4", title: "SE1/4NW1/4", name: "SE1/4NW1/4", shape: "rect", coords: [50,50,100,100] },
{ id: "SW1/4NE1/4", title: "SW1/4NE1/4", name: "SW1/4NE1/4", shape: "rect", coords: [100,50,150,100] },
{ id: "SE1/4NE1/4", title: "SE1/4NE1/4", name: "SE1/4NE1/4", shape: "rect", coords: [150,50,200,100] },
{ id: "NW1/4SW1/4", title: "NW1/4SW1/4", name: "NW1/4SW1/4", shape: "rect", coords: [0,100,50,150] },
{ id: "NE1/4SW1/4", title: "NE1/4SW1/4", name: "NE1/4SW1/4", shape: "rect", coords: [50,100,100,150] },
{ id: "NW1/4SE1/4", title: "NW1/4SE1/4", name: "NW1/4SE1/4", shape: "rect", coords: [100,100,150,150] },
{ id: "NE1/4SE1/4", title: "NE1/4SE1/4", name: "NE1/4SE1/4", shape: "rect", coords: [150,100,200,150] },
{ id: "SW1/4SW1/4", title: "SW1/4SW1/4", name: "SW1/4SW1/4", shape: "rect", coords: [0,150,50,200] },
{ id: "SE1/4SW1/4", title: "SE1/4SW1/4", name: "SE1/4SW1/4", shape: "rect", coords: [50,150,100,200] },
{ id: "SW1/4SE1/4", title: "SW1/4SE1/4", name: "SW1/4SE1/4", shape: "rect", coords: [100,150,150,200] },
{ id: "SE1/4SE1/4", title: "SE1/4SE1/4", name: "SE1/4SE1/4", shape: "rect", coords: [150,150,200,200] },
]
}
const areas = MAP.areas
const [clickedAreas, setClickedAreas] = useState([])
function clickArea(id) {
console.log("clickedAreas: ", clickedAreas)
if (clickedAreas.includes(id)) {
console.log("includes")
setClickedAreas(() => clickedAreas.filter(el => el != id))
}
else {
console.log("doesn't include")
setClickedAreas(() => [...clickedAreas, id])
}
}
return (
<>
<ImageMapper src={URL} map={MAP}
stayMultiHighlighted={true}
toggleHighlighted={true}
fillColor={"rgba(150, 213, 255, 0.6)"}
onClick={(area) => clickArea(area.id)} // THIS ONCLICK ISN'T WORKING CORRECTLY
/>
<div className="bold">AREAS</div>
{areas.map(area =>
<>
<div className="d-flex pointer" onClick={() => clickArea(area.id)}> {/* THIS ONCLICK IS WORKING CORRECTLY */}
<i className={`fas fa-check-circle ${clickedAreas.includes(area.id) ? "text-green" : "text-black opacity-10"}`}></i>
<div key={area.id}>{area.id}</div>
</div>
</>
)
}
</>
)
}
export default ImageMapQQ
I still don't know why it doesn't work as originally written, and would still like more insight on that if anyone's got it.
Ultimately, the ImageMapper onClick just keeps using the initial state (empty array) whenever a click occurs -- I can tell this because I have it console.log the current clickedAreas state at the start of the clickArea function that's called when the image map is clicked.
It seems to update the state (because the corresponding checkmark turns green), but it seems to always start from the initial state every time the ImageMapper onClick function is called (console.log shows empty array every time).
That all being said, I HAVE found a workaround to make it work as desired.
Instead of the ImageMapper onClick calling the clickArea function that later sets state, I changed it to setting a different state immediately (areaId), triggering a useEffect that then calls the clickArea function that later sets the state I actually wanted to be working with, lol. Hacky, but works.
(Added the nanoid in there to make it unique upon every click, so that the setAreaId function within ImageMapper onClick would always run -- if I just had area.id, it wouldn't run if that area.id was already the areaId state.... aka, I couldn't get the clickArea function to run to remove it from the clickedAreas state)
The small red circles 1-3 are the new parts of the code:

How to store a component in an object in typescript?

So I import multiple icons in react app and store them in an array of objects, it looks like below:
export const icons = [
{
title: 'Home',
link: 'home',
icon: <AiOutlineHome className="icon"/>,
},
{
title: 'Favorite',
link: 'favorite',
icon: <BsHeart className="icon"/>,
},
{
title: 'Search',
link: 'search',
icon: <IoSearchOutline className="icon"/>,
},
{
title: 'Playing',
link: 'playing',
icon: <MdOutlineMovieCreation className="icon"/>,
},
{
title: 'Upcoming',
link: 'upcoming',
icon: <BsCalendarDate className="icon"/>,
},
{
title: 'Popular',
link: 'popular',
icon: <HiOutlineFire className="icon"/>,
},
{
title: 'Popular',
link: 'popular',
icon: <AiOutlineStar className="icon"/>,
},
]
Then I import them in another component and map through them to display all icons. So when file is icons.js it works alright, but when I change it to icons.ts I get an error: 'AiOutlineHome' refers to a value, but is being used as a type here. Did you mean 'typeof AiOutlineHome'? with all icons. Is there some way to solve it or should I just stick to using javascript?
You need to name the extension to .tsx if you have JSX syntax in it.

difference between knobs and controls in Storybook?

I am new to the storybook. When I go through the documentation and videos about the storybook I read about knobs Addon. Knobs addon and control looks similar. What is the difference between those two things?
Controls were introduced with Storybook version 6. They replace knobs for most of the use cases. However, there may be some edge cases were you still want to use knobs for dynamic values. For example, see this Github discussion on this topic: https://github.com/storybookjs/storybook/issues/11984
controls addon is a companion to the docs addon so it interfaces with the ArgsTable which by itself is designed to automatically extract your components' propTypes & defaultProps (although I found this not to work)
So, with Knobs you define each prop (which you wish to be dynamic) yourself, manually, and this requires some more manual sync when your component changes and also more work, and also Knobs variables definitions might be scattered all across your story's file, where controls are all defined in one place, though the same "order" can also be done with Knobs, it does not enforces it (for good reasons).
If you want to have an interactive propTypes documentation for your components, then I suggest using controls with addon-docs, and I've been using knobs for years, but that's it, it's time to upgrade.
If, for some reason, your component's propTypes where not auto-detected (in the story) then you can define then (with controls) like so:
import Alert from './';
export default {
title: 'General/Alert',
component: Alert,
parameters: {
controls: { expanded: true }, // Show full documentation for each property
},
argTypes: {
type: {
description: 'Alert.Types',
defaultValue: Alert.Types.WARNING,
table: {
type: {
summary: 'string',
},
defaultValue: {
summary: Alert.Types.WARNING,
},
},
options: Alert.Types,
control: {
type: 'select', // for selecting between the array of options above
},
},
title: {
defaultValue: '',
table: {
type: {
summary: 'string',
},
},
description: 'An optional title',
control: {
type: 'text',
},
},
onClose: {
table: {
type: {
summary: 'func',
},
},
description: '× button click callback',
control: { type: null },
},
children: {
description: 'The message body (mandatory)',
type : {
required: true,
},
table: {
type: {
summary: 'node',
},
},
control: { type: null },
},
},
}
//...export your story...
Notes:
How to migrate dynamic knobs to controls?

Antd: set default value / setFieldsValue for Cascader when opening Modal

I have Antd's Cascader like this:
When I click on Edit button it open modal as below.
const showModal = (record) => {
console.log('record', record); // this is current record which has existing value to be displayed in cascader
form.setFieldsValue(record);
setVisible(true);
};
how do I feed initial values or existing values to Cascader?
<Cascader
defaultValue={['zhejiang', 'hangzhou', 'xihu']}
options={propertyOptions}
loadData={loadCompanies}
onChange={onChange}
changeOnSelect
/>
using defaultValue prop like does not seem to work.
When I load propertyOptions, it loads with values like
property1,
property2
When I click on property1 it loads children dynamically.
so selected value is e.g. property1 > company1
but When I click on Edit modal it has only parent values. (children won't be there as I load them on parent click).
When I click on modal it has the whole item data so maybe I can use it to show like hardcoded value or something?
UPDATE:
This is how I fill propertyOptions
function setPropertiesAsOptions(propertiesQuerySnapshot) {
propertiesQuerySnapshot.forEach((doc) => {
options.push({
value: doc.id,
label: doc.data().propertyName,
isLeaf: false,
});
});
setPropertyOptions(options);
}
Is there propertyOptions has value names like zhejiang or other?
In your case this data should work properly as options
const options = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
},
],
},
],
},
];
So it's mean that value name should be the same as params in defaultValue

Resources