How to add a changeable icon to component in storybook? - reactjs

I have a component - a simple list with an icon, header and links. I need to add an icon in front of the header. So that you can choose which icon will be in the Storybook. I can't find it in the documentation. And I can't figure out how to do it. If you know how, please help me.
// ItemsList.tsx
import React, { ReactElement, ReactNode } from 'react';
import { myIcon1 } from 'src/icons/Icon1';
import { myIcon2 } from 'src/icons/Icon2';
interface ItemsList {
icon: ReactElement;
header: string;
children?: ReactNode;
}
export const ItemsList = ({ icon, header, children }: ItemsList) => (
<div>
{icon} // Have no idea how to add one of imported icons here??
<div>{header}</div>
<ul>{children}</ul>
</div>
);
My Story File:
// ItemsList.story.jsx
import { ItemsList } from './itemsList';
export default {
title: 'ItemsList',
component: ItemsList,
argTypes: {
icon: {
control: { type: 'radio', options: ['no icon', 'icon 1', 'icon 2'] },
},
header: {
type: 'string',
defaultValue: 'Header',
},
children: {
type: 'array',
name: 'label',
defaultValue: ['link 1', 'link 2', 'link 3'],
},
},
};
const Template = (args) => <ItemsList {...args} />;
export const Default = Template.bind({});
Default.args = {
icon: ????,
header: 'Header',
children: ['link 1', 'link 2', 'link 3'],
};

Related

Ant Design: Correct type of Menu onClick event parameter

I am trying to use a navigation component from Antd (https://ant.design/components/menu), but it's giving me this error:
Parameter 'e' implicitly has an 'any' type.
I have tried doing e: React.MouseEvent<HTMLElement> but then the error shifts to e.key: Property 'key' does not exist on type 'MouseEvent<HTMLInputElement, MouseEvent>'.
Following is my App.tsx:
import { AppstoreOutlined, MailOutlined, SettingOutlined } from '#ant-design/icons';
import type { MenuProps } from 'antd';
import { Menu } from 'antd';
const items: MenuProps['items'] = [
{
label: 'Navigation One',
key: 'mail',
icon: <MailOutlined />,
},
{
label: 'Navigation Two',
key: 'app',
icon: <AppstoreOutlined />,
disabled: true,
},
{
label: 'Navigation Three - Submenu',
key: 'SubMenu',
icon: <SettingOutlined />,
children: [
{
type: 'group',
label: 'Item 1',
children: [
{
label: 'Option 1',
key: 'setting:1',
},
{
label: 'Option 2',
key: 'setting:2',
},
],
},
{
type: 'group',
label: 'Item 2',
children: [
{
label: 'Option 3',
key: 'setting:3',
},
{
label: 'Option 4',
key: 'setting:4',
},
],
},
],
},
{
label: (
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
Navigation Four - Link
</a>
),
key: 'alipay',
},
];
const App: React.FC = () => {
const [current, setCurrent] = useState('mail');
const onClick: MenuProps['onClick'] = (e) => {
console.log('click ', e);
setCurrent(e.key);
};
return <Menu onClick={(onClick)} selectedKeys={[current]} mode="horizontal" items={items} />;
};
export default App;
I am new to this and have already spent many hours trying to fix this, any help is highly appreciated! Thank you!
The correct type of e is the MenuInfo interface defined in the react-component library (rc-menu). Unfortunately, this type isn't exported. As a workaround, you can use the following code to indirectly reference the MenuInfo interface:
const onClick: MenuProps['onClick'] = (e: Parameters<MenuProps['onClick']>[0]) => {
The Parameters utility type returns an array type containing the parameter types of the specified function, and the [0] indexer returns the type of the first (and only) parameter.

Vite error when defining a component in an object: Cannot read properties of undefined

I have a list of options in a React component which I am mapping through to make some UI. The component is being used in QwikJS, which supports React components and uses Vite. However, when I add an icon value in my list of options Vite breaks during the build.
import { IconHome, IconMail, IconPencilPlus } from '#tabler/icons'
interface OptionProps {
id: number
text: string
icon: React.ReactElement
url?: string
action?: () => void
}
export const Command = qwikify$(() => {
...
const options: OptionProps[] = [
{
id: 1,
text: 'Home',
icon: <IconHome className='h-5 w-5' stroke={1} />,
url: '/',
action: undefined,
},
{
id: 2,
text: 'Join the waitlist',
icon: <IconPencilPlus className='h-5 w-5' stroke={1} />,
url: undefined,
action: () => {
setShowWaitlist(true)
},
},
]
...
}
I can't work out how to define the icon. Vite keeps giving me the error:
[vite] Internal server error: Cannot read properties of undefined (reading 'IconHome')
I've used the same code in Next (this is Qwik). Can anyone see what I'm doing wrong? - would like to render the icon when I map through the options, ie:
{options.map((option, i) => (
<div>
<div>{option.icon}</div>
<div>{option.text}</div>
<div>
))}
import { IconHome, IconMail, IconPencilPlus } from '#tabler/icons'
interface OptionProps {
id: number
text: string
icon: () => React.ReactElement
url?: string
action?: () => void
}
export const Command = qwikify$(() => {
...
const options: OptionProps[] = [
{
id: 1,
text: 'Home',
icon: () => <IconHome className='h-5 w-5' stroke={1} />,
url: '/',
action: undefined,
},
{
id: 2,
text: 'Join the waitlist',
icon: () => <IconPencilPlus className='h-5 w-5' stroke={1} />,
url: undefined,
action: () => {
setShowWaitlist(true)
},
},
]
...
}
{options.map((option, i) => (
<div>
<div>{option.icon()}</div>
<div>{option.text}</div>
<div>
))}

Ant design 4.20 new Menu item style

Ant Design deprecated <Menu.Item> in 4.20.0, so I am converting it to a new format.
When I have the following code, how do I convert it into the new format?
<Menu>
<Menu.Item key="1" className={css.first} onClick={onFirst}>item 1</Menu.Item>
<Menu.Item key="2" className={css.second} onClick={onSecond}>item 2</Menu.Item>
</Menu>;
Ant Design v4.21.7 you can convert it like that
import React, { useState } from 'react';
import 'antd/dist/antd.css';
import './index.css';
import { Menu } from 'antd';
const App = () => {
const [current, setCurrent] = useState();
const onFirst = (e) => {
console.log('click 1', e);
setCurrent(e.key);
};
const onSecond = (e) => {
console.log('click 2', e);
setCurrent(e.key);
};
const items = [
{
label: 'Navigation One',
children: [
{
label: 'item 1',
onClick: onFirst,
key: '1',
className: 'first',
},
{
label: 'item 2',
onClick: onSecond,
key: '2',
className: 'second',
},
],
},
];
return <Menu selectedKeys={[current]} mode="horizontal" items={items} />;
};
export default App;

how to use icon in Ant-design/icons with V4

I have tried to make a menu, so i create a menuList to config the menu with getMenuNodes(), but Ant framework has been upgraded from v3 to v4 which the icon method has been changed. they are now using icon={<PieChartOutlined />} to instead of icon='PieChartOutlined', everything is working well, the icon area shows the word <PieChartOutlined />right now. i do not know why it happened, please help me to solve this problem.
left-navigation.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import logo from '../../assets/images/logo.png';
import './index.less';
import { Menu } from 'antd';
import { PieChartOutlined } from '#ant-design/icons';
import menuList from '../../config/menuConfig';
const { SubMenu } = Menu;
export default class LeftNav extends Component {
getMenuNodes = menuList => {
return menuList.map(item => {
if (!item.children) {
return (
<Menu.Item key={item.key} icon={item.icon}>
<Link to={item.key}>{item.title}</Link>
</Menu.Item>
);
} else {
return (
<SubMenu key={item.key} icon={item.icon} title={item.title}>
{this.getMenuNodes(item.children)}
</SubMenu>
);
}
});
};
render() {
return (
<div className="left-nav">
<Link to="./" className="left-nav-header">
<img src={logo} alt="" />
<h1>Backend System</h1>
</Link>
<Menu
mode="inline"
theme="dark"
>
{this.getMenuNodes(menuList)}
</Menu>
</div>
);
}
}
menuList.js
const menuList = [
{
title: 'Home',
key: '/home',
icon: '<PieChartOutlined />',
},
{
title: 'Item',
key: '/products',
icon: '<PieChartOutlined />',
children: [
{
title: 'Category Control',
key: '/category',
icon: '<PieChartOutlined />',
},
{
title: 'Product Control',
key: '/product',
icon: '<PieChartOutlined />',
},
],
},
{
title: 'User Control',
key: '/user',
icon: '<PieChartOutlined />',
},
{
title: 'Role Control',
key: '/role',
icon: '<PieChartOutlined />',
},
{
title: 'Diagram',
key: '/charts',
icon: '<PieChartOutlined />',
children: [
{
title: 'Bar',
key: '/charts/bar',
icon: '<PieChartOutlined />',
},
{
title: 'Line',
key: '/charts/line',
icon: '<PieChartOutlined />',
},
{
title: 'Pie',
key: '/charts/pie',
icon: '<PieChartOutlined />',
},
],
},
];
export default menuList;
You are passing a string of '<PieChartOutlined />', you need to pass the component directly.
import { PieChartOutlined } from '#ant-design/icons';
and:
{
title: 'Product Control',
key: '/product',
icon: <PieChartOutlined />,
},
You'll need to install ant-design/icons if you haven't already:
npm install --save #ant-design/icons
Since reason performance on the previous version, antd team was apply tree-shaking to use icon. More detail, you can check https://ant.design/docs/react/migration-v4

How do you set the value of a TextField from Dropdown Selection?

In the following code, I was curious as to how you would set the value of the following TextField.
For this example, how do I set the TextField to the selected item in the Dropdown?
If the user selects "TOP LEVEL" in the Dropdown, then I want to populate the TextField to be "TOP LEVEL". The Dropdown is called ChildComponent
import * as React from "react";
import ChildComponent from './Operations/ChildComponent';
import { DropdownMenuItemType, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import { TextField} from 'office-ui-fabric-react/lib/TextField';
export interface ParentComponentState {
selectedItem?: { key: string | number | undefined };
value: {key: string};
}
export default class ParentComponent extends React.Component<{}, ParentComponentState> {
constructor(props, context) {
super(props, context);
}
public state: ParentComponentState = {
selectedItem: undefined,
value: undefined,
};
render(){
const { selectedItem } = this.state;
const options: IDropdownOption[] = [
{ key: 'blank', text: '' },
{ key: 'topLevelMake', text: 'Parents', itemType: DropdownMenuItemType.Header },
{ key: 'topLevel', text: 'TOP LEVEL' },
{ key: 'make', text: 'MAKE ITEM' },
{ key: 'divider_1', text: '-', itemType: DropdownMenuItemType.Divider },
{ key: 'Purchased', text: 'Purchases', itemType: DropdownMenuItemType.Header },
{ key: 'rawMaterial', text: 'RAW MATERIAL' },
{ key: 'buyItem', text: 'BUY ITEM' },
];
return(
<div>
<ChildComponent
options={options}
selectedKey={selectedItem ? selectedItem.key : undefined}
onChange={this._onChange}
/>
<TextField
name="textTest"
label={"Test"}
/>
</div>
);
}
private _onChange = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
this.setState({ selectedItem: item });
let opValue = item.text;
console.log(event);
console.log(opValue);
};
}
After inserting Muhammad's logic, here is the error I am getting. Do I need to add an onChange event for the TextField? and then put "this.state.selectedItem" in the handleChange event? Do I need to make a new child component and have the TextField rollup to ParentComponent?
You just need to assign that state in the value prop for the textField as you have the selectedItem in your state
<TextFieid
label={"Test"}
styles={{ root: { width: 300 } }}
value={this.state.selectedItem}
/>
import * as React from "react";
import ChildComponent from './Operations/ChildComponent';
import { DropdownMenuItemType, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import { TextField} from 'office-ui-fabric-react/lib/TextField';
export interface ParentComponentState {
selectedItem?: { key: string | number | undefined };
value?;
}
export default class ParentComponent extends React.Component{
constructor(props) {
super(props);
this.state = {
value: '',
};
}
public state: ParentComponentState = {
selectedItem: undefined,
};
handleChange = (event) => {
this.setState({
value: event.target.value,
})
};
render(){
const { selectedItem } = this.state;
const options: IDropdownOption[] = [
{ key: 'blank', text: '' },
{ key: 'topLevelMake', text: 'Parents', itemType: DropdownMenuItemType.Header },
{ key: 'topLevel', text: 'TOP LEVEL' },
{ key: 'make', text: 'MAKE ITEM' },
{ key: 'divider_1', text: '-', itemType: DropdownMenuItemType.Divider },
{ key: 'Purchased', text: 'Purchases', itemType: DropdownMenuItemType.Header },
{ key: 'rawMaterial', text: 'RAW MATERIAL' },
{ key: 'buyItem', text: 'BUY ITEM' },
];
return(
<div>
<ChildComponent
options={options}
selectedKey={selectedItem ? selectedItem.key : undefined}
onChange={this._onChange}
/>
<TextField
name="textTest"
label={"Test"}
onChange={this.handleChange}
value={this.state.value}
/>
</div>
);
}
private _onChange = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
this.setState({ selectedItem: item });
this.setState({value: item.text})
let opValue = item.text;
console.log(event);
console.log(opValue);
};
}

Resources