Bind combobox to sharepoint list - reactjs

This code shows how to use a combobox in a Sharepoint online webpart using react. The sample data is hard coded. How can I bind the combo box to a Sharepoint list?
```
import * as React from 'react';
import { ComboBox, Fabric, IComboBoxOption, mergeStyles,
SelectableOptionMenuItemType, Toggle } from 'office-ui-fabric-
react/lib/index';
const INITIAL_OPTIONS: IComboBoxOption[] = [
{ key: 'Header1', text: 'First heading', itemType:
SelectableOptionMenuItemType.Header },
{ key: 'A', text: 'Option A' },
{ key: 'B', text: 'Option B' },
{ key: 'C', text: 'Option C' },
{ key: 'D', text: 'Option D' },
{ key: 'divider', text: '-',
itemType: SelectableOptionMenuItemType.Divider },
{ key: 'Header2', text: 'Second heading', itemType:
SelectableOptionMenuItemType.Header },
{ key: 'E', text: 'Option E' },
{ key: 'F', text: 'Option F', disabled: true },
{ key: 'G', text: 'Option G' },
{ key: 'H', text: 'Option H' },
{ key: 'I', text: 'Option I' },
{ key: 'J', text: 'Option J' }
];
const wrapperClassName = mergeStyles({
display: 'flex',
selectors: {
'& > *': { marginRight: '20px' },
'& .ms-ComboBox': { maxWidth: '300px' }
}
});
export interface IComboBoxTogglesExampleState {
autoComplete: boolean;
allowFreeform: boolean;
}
// tslint:disable:jsx-no-lambda
export class ComboBoxTogglesExample extends React.Component<{},
IComboBoxTogglesExampleState> {
public state: IComboBoxTogglesExampleState = {
autoComplete: false,
allowFreeform: true
};
public render(): JSX.Element {
const state = this.state;
return (
<Fabric className={wrapperClassName}>
<ComboBox
label="ComboBox with toggleable freeform/auto-complete"
key={'' + state.autoComplete + state.allowFreeform /*key causes re-
render when toggles change*/}
allowFreeform={state.allowFreeform}
autoComplete={state.autoComplete ? 'on' : 'off'}
options={INITIAL_OPTIONS}
/>
<Toggle
label="Allow freeform"
checked={state.allowFreeform}
onChange={(ev: React.MouseEvent<HTMLElement>, checked?: boolean) =>
{
this.setState({ allowFreeform: !!checked });
}}
/>
<Toggle
label="Auto-complete"
checked={state.autoComplete}
onChange={(ev: React.MouseEvent<HTMLElement>, checked?: boolean) =>
{
this.setState({ autoComplete: !!checked });
}}
/>
</Fabric>
);
}
}
```
This is for a Sharepoint Online webpart using the React Framework.
I have a list of 60 cost centers and I want them to appear in a combo box with autocomplete switched on.

Sample demo(many test scenarios, just checking the logic you need):
export interface IListItem{
Id: number;
Title: string;
Body : {
Body_p1: string;
Body_p2: string;
};
}
export interface IReactExState{
controlName:string,
ControlValue:string,
windowsID:number,
showPanel:boolean,
items: IListItem[],
Options:IComboBoxOption[],
selectionDetails: {},
showModal: boolean,
autoComplete: boolean,
allowFreeform: boolean
}
export default class OfficeFabric extends React.Component<IOfficeFabricProps, IReactExState> {
private _selection: Selection;
private _allItems: IListItem[];
private _columns: IColumn[];
public constructor(props: IOfficeFabricProps,state: IReactExState){
super(props);
this._selection = new Selection({
onSelectionChanged: () => this.setState({ selectionDetails: this._getSelectionDetails() })
});
this._allItems = [
{
Id : 0,
Title : "test0",
Body : {
Body_p1: "test0_p1",
Body_p2: "test0_p2"
},
},
{
Id : 1,
Title : "test1",
Body : {
Body_p1: "test1_p1",
Body_p2: "test1_p2"
}
}
];
this._columns = [
{ key: 'Id', name: 'Id', fieldName: 'Id', minWidth: 100, maxWidth: 200, isResizable: true },
{ key: 'Title', name: 'Title', fieldName: 'Title', minWidth: 200, maxWidth: 400, isResizable: true },
{ key: 'Body', name: 'Body', minWidth: 200, maxWidth: 400, isResizable: true,onRender: (item) => (
<div>
{item.Body.Body_p1}
</div>) },
];
this.state = {
controlName:"",
ControlValue:"",
windowsID:123.4,
showPanel:false,
items:this._allItems,
Options:[],
selectionDetails: this._getSelectionDetails() ,
showModal:false,
autoComplete: false,
allowFreeform: true
};
}
private _showModal = (): void => {
this.setState({ showModal: true });
};
private _closeModal = (): void => {
this.setState({ showModal: false });
};
private _getSelectionDetails(): string {
const selectionCount = this._selection.getSelectedCount();
switch (selectionCount) {
case 0:
return 'No items selected';
case 1:
return '1 item selected: ' + (this._selection.getSelection()[0] as IListItem).Title;
default:
return `${selectionCount} items selected`;
}
}
private _onItemInvoked = (item: IListItem): void => {
alert(`Item invoked: ${item.Title}`);
};
private _showPanel = () => {
this.setState({ showPanel: true });
};
private _hidePanel = () => {
this.setState({ showPanel: false });
};
private _Save = () => {
//to do save logic
this.setState({ showPanel: false });
alert('save clicked');
};
private _onRenderFooterContent = () => {
return (
<div>
<PrimaryButton onClick={this._hidePanel} style={{ marginRight: '8px' }}>
Save
</PrimaryButton>
<DefaultButton onClick={this._showPanel}>Cancel</DefaultButton>
</div>
);
};
public componentDidMount(){
var reactHandler = this;
this.props.context.spHttpClient.get(`${this.props.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('TestList')/items?select=ID,Title`,
SPHttpClient.configurations.v1) .then((response: SPHttpClientResponse) => {
response.json().then((responseJSON: any) => {
let tempOptions:IComboBoxOption[]=[];
responseJSON.value.forEach(element => {
tempOptions.push({key:element.ID,text:element.Title})
});
reactHandler.setState({
Options: tempOptions
});
});
});
}
// private _onJobTitReportToChange = (ev: React.FormEvent<HTMLInputElement>, newValue?: string) => {
// this.props.onJobTitleReportToChange(newValue);
// }
public handleObjectWithMultipleFields = (ev, newText: string): void => {
const target = ev.target;
const value = newText;
var _ControlName=target.name;
this.setState({
controlName: _ControlName,
ControlValue:value
})
}
public render(): React.ReactElement<IOfficeFabricProps> {
return (
<div className={ styles.officeFabric }>
<div className={ styles.container }>
<div className={ styles.row }>
<div className={ styles.column }>
<span className={ styles.title }>Welcome to SharePoint!</span>
<p className={ styles.subTitle }>Customize SharePoint experiences using Web Parts.</p>
<p className={ styles.description }>{escape(this.props.description)}</p>
<ComboBox
label="ComboBox with toggleable freeform/auto-complete"
key={'' + this.state.autoComplete + this.state.allowFreeform /*key causes re-
render when toggles change*/}
allowFreeform={this.state.allowFreeform}
autoComplete={this.state.autoComplete ? 'on' : 'off'}
options={this.state.Options}
/>
<TextField name="txtA" value={this.state.windowsID.toString()}
onChange={this.handleObjectWithMultipleFields}/>
<TextField name="txtB"
onChange={this.handleObjectWithMultipleFields}/>
<div>
<DefaultButton secondaryText="Opens the Sample Panel" onClick={this._showPanel} text="Open Panel" />
<Panel
isOpen={this.state.showPanel}
type={PanelType.smallFixedFar}
onDismiss={this._hidePanel}
headerText="Panel - Small, right-aligned, fixed, with footer"
closeButtonAriaLabel="Close"
onRenderFooterContent={this._onRenderFooterContent}
>
<DefaultButton className={styles.tablink} text="Button" />
<ChoiceGroup
options={[
{
key: 'A',
text: 'Option A'
},
{
key: 'B',
text: 'Option B',
checked: true
},
{
key: 'C',
text: 'Option C',
disabled: true
},
{
key: 'D',
text: 'Option D',
checked: true,
disabled: true
}
]}
label="Pick one"
required={true}
/>
</Panel>
<DefaultButton secondaryText="Opens the Sample Modal" onClick={this._showModal} text="Open Modal" />
</div>
<MarqueeSelection selection={this._selection}>
<DetailsList
items={this.state.items}
columns={this._columns}
setKey="set"
layoutMode={DetailsListLayoutMode.justified}
selection={this._selection}
selectionPreservedOnEmptyClick={true}
ariaLabelForSelectionColumn="Toggle selection"
ariaLabelForSelectAllCheckbox="Toggle selection for all items"
checkButtonAriaLabel="Row checkbox"
onItemInvoked={this._onItemInvoked}
/>
</MarqueeSelection>
<a href="https://aka.ms/spfx" className={ styles.button }>
<span className={ styles.label }>Learn more</span>
</a>
<Icon iconName='Mail' />
<br/>
<Icon iconName='CirclePlus' />
<br/>
<Icon iconName='LocationDot' />
</div>
</div>
</div>
</div>
);
}
}

I ended up using this.
sp.web.lists.getByTitle("ChartOfAccounts").items.getAll().then((result: any)=>{
for(let i: number = 0; i< result.length; i++) {
const b: SPData = result[i];
const c: IComboBoxOption={key:b.Column1, text: b.CostCentreInfo};
INITIAL_OPTIONS.push(c);
}

Related

How to count selected checkboxes in React functional component?

I am needing to add bit of text in the sidebar of the following code. In each section, I need to have a count of the number of checkboxes selected. Is it at all possible to do this in a functional component as I have? Any examples that I have found so far are only for class components. I would like to keep it as a functional component if possible.
I have the code below, but here is a working version too:
https://codesandbox.io/s/react-playground-forked-jgmof?file=/index.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import styled from "styled-components";
import { categoryData } from "./data";
const Menu = (props) => {
const [showPanel, togglePanel] = useState(false);
return (
<MenuWrapper>
<p>Showing 20 results</p>
<div className="button-wrapper">
<p>Filter By</p>
{categoryData.map((categorybutton, i) => (
<div key={i}>
<button key={i} onClick={() => togglePanel(!showPanel)}>
{categorybutton.category}
</button>
</div>
))}
<div>
{showPanel && (
<Sidebar>
{categoryData.map((categorysection, i) => (
<details className="dropdown-header">
<summary>{categorysection.category}</summary>
{categorysection.data.map((categorylabel, i) => (
<div key={i}>
<input
type="checkbox"
id="vehicle1"
name="vehicle1"
value="Bike"
/>
<label for="vehicle1">{categorylabel.label}</label>
</div>
))}
</details>
))}
</Sidebar>
)}
</div>
<p>Toggle</p>
<p>Clear all filters</p>
</div>
</MenuWrapper>
);
};
const MenuWrapper = styled.div`
width: 90vw;
display: flex;
justify-content: space-between;
.button-wrapper {
display: flex;
}
`;
const Sidebar = styled.div`
position: fixed;
width: 200px;
height: 100vh;
background: white;
top: 0;
left: 0;
z-index: 100000;
text-align: left;
.dropdown-header {
background: #f7f7f7;
margin: 20px;
}
`;
ReactDOM.render(<Menu />, document.getElementById("container"));
export const categoryData = [
{
category: "user",
data: [
{ checked: false, value: "Me", label: "Me" },
{ checked: false, value: "Kids", label: "Kids" },
{ checked: false, value: "Guestroom", label: "Guestroom" }
]
},
{
category: "comfort",
data: [
{ checked: false, value: "Ultra-Soft", label: "Ultra-Soft" },
{ checked: false, value: "Soft", label: "Soft" },
{ checked: false, value: "Medium", label: "Medium" },
{ checked: false, value: "Medium-Firm", label: "Medium-Firm" },
{ checked: false, value: "Firm", label: "Firm" },
{ checked: false, value: "Ultra-Firm", label: "Ultra-Firm" },
{ checked: false, value: "Unsure", label: "Unsure" }
]
},
{
category: "type",
data: [
{ checked: false, value: "Pillow Top", label: "Pillow Top" },
{ checked: false, value: "Open Coil", label: "Open Coil" },
{ checked: false, value: "Pocketed Coil", label: "Pocketed Coil" },
{ checked: false, value: "Quantum Coil", label: "Quantum Coil" },
{ checked: false, value: "Memory Foam", label: "Memory Foam" },
{ checked: false, value: "Latex", label: "Latex" },
{ checked: false, value: "Unsure", label: "Unsure" }
]
},
{
category: "budget",
data: [
{ checked: false, value: 500, label: "Under $500" },
{ checked: false, value: 1000, label: "$501 - $1000" },
{ checked: false, value: 1600, label: "1001 - $1600" },
{ checked: false, value: 2500, label: "$1601 - $2500" },
{ checked: false, value: 2501, label: "$2500 and up" },
{ checked: false, value: "Unsure", label: "Unsure" }
]
}
];
If I understand correctly, you wan't to show the total number of selected items on each category.
It would be easy if we'll create a new component for the section that will have it's own state tracking its selected items.
Let's call it CategorySection. It would then have a selected state that will be an array (empty by default) of its selected items. To update our selected state, we have to fireup a function everytime any of the checkbox is changed — if the checkbox is checked, we add the current item to our selected state otherwise it will be removed.
Then to display the total selected items, we can simply count the length of our selected state.
function CategorySection({ categorysection }) {
const [selected, setSelected] = useState([]);
function onChange(event, item) {
if (event.target.checked) {
setSelected([...selected, item]);
} else {
setSelected((prev) =>
prev.filter((currItem) => currItem.value !== item.value)
);
}
}
return (
<details className="dropdown-header">
<summary>
{categorysection.category}{" "}
{selected.length > 0 ? selected.length : null}
</summary>
{categorysection.data.map((categorylabel, i) => (
<div key={i}>
<input
type="checkbox"
id={categorylabel.value}
name="vehicle1"
value="Bike"
onChange={(event) => onChange(event, categorylabel)}
/>
<label for={categorylabel.value}>{categorylabel.label}</label>
</div>
))}
</details>
);
}
PS: You should have a unique id for every checkbox on your page.

How to convert function component to class component in fluent UI?

I am creating multi select drop down in Office fabrics.I saw the code.It contain the functional component.I want to change in class component.and How can we store the selected options in state variable?
please guide me.I am new in spfx share-point.
Code given below:-
import * as React from 'react';
import { Dropdown, DropdownMenuItemType, IDropdownOption, IDropdownStyles } from 'office-ui-fabric-react/lib/Dropdown';
const dropdownStyles: Partial<IDropdownStyles> = { dropdown: { width: 300 } };
const DropdownControlledMultiExampleOptions = [
{ key: 'fruitsHeader', text: 'Fruits', itemType: DropdownMenuItemType.Header },
{ key: 'apple', text: 'Apple' },
{ key: 'banana', text: 'Banana' },
{ key: 'orange', text: 'Orange', disabled: true },
{ key: 'grape', text: 'Grape' },
{ key: 'divider_1', text: '-', itemType: DropdownMenuItemType.Divider },
{ key: 'vegetablesHeader', text: 'Vegetables', itemType: DropdownMenuItemType.Header },
{ key: 'broccoli', text: 'Broccoli' },
{ key: 'carrot', text: 'Carrot' },
{ key: 'lettuce', text: 'Lettuce' },
];
export const DropdownControlledMultiExample: React.FunctionComponent = () => {
const [selectedKeys, setSelectedKeys] = React.useState<string[]>([]);
const onChange = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
if (item) {
setSelectedKeys(
item.selected ? [...selectedKeys, item.key as string] : selectedKeys.filter(key => key !== item.key),
);
}
};
return (
<Dropdown
placeholder="Select options"
label="Multi-select controlled example"
selectedKeys={selectedKeys}
onChange={onChange}
multiSelect
options={DropdownControlledMultiExampleOptions}
styles={dropdownStyles}
/>
);
};
You can do the following:
export class DropdownControlledMultiExample extends React.Component {
state = {
selectedKeys: []
}
onChange = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
if (item) {
this.setState({
selectedKeys:
item.selected
? [...this.state.selectedKeys, item.key as string]
: this.state.selectedKeys.filter(key => key !== item.key),
});
}
};
render() {
return (
<Dropdown
placeholder="Select options"
label="Multi-select controlled example"
selectedKeys={this.state.selectedKeys}
onChange={this.onChange}
multiSelect
options={DropdownControlledMultiExampleOptions}
styles={dropdownStyles}
/>
);
}
};

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);
};
}

Antd UI Table: Dynamically add/delete columns

Based on ANTD's Table example: https://ant.design/components/table/#components-table-demo-edit-cell, I would like to replicate this, with the addition of having the ability to add/delete new columns. The sample from the link above only illustrates how to add new rows.
Here's the code from the sample:
import { Table, Input, Button, Popconfirm, Form } from 'antd';
const FormItem = Form.Item;
const EditableContext = React.createContext();
const EditableRow = ({ form, index, ...props }) => (
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
);
const EditableFormRow = Form.create()(EditableRow);
class EditableCell extends React.Component {
state = {
editing: false,
}
componentDidMount() {
if (this.props.editable) {
document.addEventListener('click', this.handleClickOutside, true);
}
}
componentWillUnmount() {
if (this.props.editable) {
document.removeEventListener('click', this.handleClickOutside, true);
}
}
toggleEdit = () => {
const editing = !this.state.editing;
this.setState({ editing }, () => {
if (editing) {
this.input.focus();
}
});
}
handleClickOutside = (e) => {
const { editing } = this.state;
if (editing && this.cell !== e.target && !this.cell.contains(e.target)) {
this.save();
}
}
save = () => {
const { record, handleSave } = this.props;
this.form.validateFields((error, values) => {
if (error) {
return;
}
this.toggleEdit();
handleSave({ ...record, ...values });
});
}
render() {
const { editing } = this.state;
const {
editable,
dataIndex,
title,
record,
index,
handleSave,
...restProps
} = this.props;
return (
<td ref={node => (this.cell = node)} {...restProps}>
{editable ? (
<EditableContext.Consumer>
{(form) => {
this.form = form;
return (
editing ? (
<FormItem style={{ margin: 0 }}>
{form.getFieldDecorator(dataIndex, {
rules: [{
required: true,
message: `${title} is required.`,
}],
initialValue: record[dataIndex],
})(
<Input
ref={node => (this.input = node)}
onPressEnter={this.save}
/>
)}
</FormItem>
) : (
<div
className="editable-cell-value-wrap"
style={{ paddingRight: 24 }}
onClick={this.toggleEdit}
>
{restProps.children}
</div>
)
);
}}
</EditableContext.Consumer>
) : restProps.children}
</td>
);
}
}
class EditableTable extends React.Component {
constructor(props) {
super(props);
this.columns = [{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
}, {
title: 'age',
dataIndex: 'age',
}, {
title: 'address',
dataIndex: 'address',
}, {
title: 'operation',
dataIndex: 'operation',
render: (text, record) => (
this.state.dataSource.length >= 1
? (
<Popconfirm title="Sure to delete?" onConfirm={() => this.handleDelete(record.key)}>
Delete
</Popconfirm>
) : null
),
}];
this.state = {
dataSource: [{
key: '0',
name: 'Edward King 0',
age: '32',
address: 'London, Park Lane no. 0',
}, {
key: '1',
name: 'Edward King 1',
age: '32',
address: 'London, Park Lane no. 1',
}],
count: 2,
};
}
handleDelete = (key) => {
const dataSource = [...this.state.dataSource];
this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
}
handleAdd = () => {
const { count, dataSource } = this.state;
const newData = {
key: count,
name: `Edward King ${count}`,
age: 32,
address: `London, Park Lane no. ${count}`,
};
this.setState({
dataSource: [...dataSource, newData],
count: count + 1,
});
}
handleSave = (row) => {
const newData = [...this.state.dataSource];
const index = newData.findIndex(item => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
this.setState({ dataSource: newData });
}
render() {
const { dataSource } = this.state;
const components = {
body: {
row: EditableFormRow,
cell: EditableCell,
},
};
const columns = this.columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave: this.handleSave,
}),
};
});
return (
<div>
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
Add a row
</Button>
<Table
components={components}
rowClassName={() => 'editable-row'}
bordered
dataSource={dataSource}
columns={columns}
/>
</div>
);
}
}
ReactDOM.render(<EditableTable />, mountNode);
You can make your columns array a part of the state and update it through setState.
Here is a working codepen: https://codepen.io/gges5110/pen/GLPjYr?editors=0010
// State
this.state = {
dataSource: [{
key: '0',
name: 'Edward King 0',
age: '32',
address: 'London, Park Lane no. 0',
}, {
key: '1',
name: 'Edward King 1',
age: '32',
address: 'London, Park Lane no. 1',
}],
columns: [{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
}, {
title: 'age',
dataIndex: 'age',
}]
};
// Event to add new column
handleAddColumn = () => {
const { columns } = this.state;
const newColumn = {
title: 'age',
dataIndex: 'age',
};
this.setState({
columns: [...columns, newColumn]
});
}
// Render method
render() {
const { dataSource, columns } = this.state;
return (
<Table
dataSource={dataSource}
columns={columns}
/>
);
}

Missing sort and filter icons in Ant design

I have the following table in Ant Design. The sort and filter icons in Ant Design. The icons aren't showing up, although if I hover over the column header, I can see the "sort" and "filter" text pop up, and the actual sort and filter functionality works.
In my parent component I am importing the CSS
App.css
#import "~antd/dist/antd.css";
App.js
import './App.css';
Assets.js
/* eslint-disable no-undef */
/*
From https://ant.design/components/table/ (Dynamic Settings example)
*/
import React, { Component } from 'react';
import Title from './Title';
import { Table, Icon, Switch, Radio, Form, Divider } from 'antd';
const FormItem = Form.Item;
/* const data = [];
for (let i = 1; i <= 10; i++) {
data.push({
key: i,
name: 'John Brown',
age: `${i}2`,
address: `New York No. ${i} Lake Park`,
description: `My name is John Brown, I am ${i}2 years old, living in New York No. ${i} Lake Park.`
});
} */
const expandedRowRender = record => <p>{record.description}</p>;
const title = () => 'Assets';
const showHeader = true;
const footer = () => 'Here is footer';
const scroll = { y: 240 };
const pagination = { position: 'bottom' };
// TODO: Delete this after swapping in the table
/* const renderAssets = (data, handleAssetClick) => {
console.log('renderAssets data', data);
const assetsToRender = data.map(asset => {
const assetDiv = (
<div style={{ display: 'block' }} onClick={handleAssetClick} value={asset.id} key={asset.id}>
{asset.name}
</div>
);
return assetDiv;
});
console.log('Assets assetsToRender', assetsToRender);
return assetsToRender;
}; */
const getAssets = data => {
console.log('getAssets data', data);
let assets = [];
assets = data.map(asset => {
//console.log(asset);
return {
key: asset.id,
id: asset.id,
name: asset.name,
type: asset.properties.class,
substation: asset.properties.substation,
feeder: asset.properties.feeder,
status: asset.properties.service_status,
vulnerability: asset.properties.peak_vulnerability
};
});
console.log('Assets', assets);
return assets;
};
class Assets extends Component {
constructor(props) {
super(props);
console.log('Assets data', this.props.data, 'handleAssetClick', this.props.handleAssetClick);
this.state = {
bordered: true,
loading: false,
pagination,
size: 'default',
//expandedRowRender,
title,
showHeader,
//footer,
// rowSelection: {},
scroll: undefined,
hasData: true
};
}
componentDidMount() {
if (!this.props.data || !this.props.data.length || this.props.data.length === 0) {
return null;
}
}
handleToggle = prop => {
return enable => {
this.setState({ [prop]: enable });
};
};
handleSizeChange = e => {
this.setState({ size: e.target.value });
};
handleExpandChange = enable => {
this.setState({ expandedRowRender: enable ? expandedRowRender : undefined });
};
handleTitleChange = enable => {
this.setState({ title: enable ? title : undefined });
};
handleHeaderChange = enable => {
this.setState({ showHeader: enable ? showHeader : false });
};
handleFooterChange = enable => {
this.setState({ footer: enable ? footer : undefined });
};
handleRowSelectionChange = enable => {
this.setState({ rowSelection: enable ? {} : undefined });
};
handleScollChange = enable => {
this.setState({ scroll: enable ? scroll : undefined });
};
handleDataChange = hasData => {
this.setState({ hasData });
};
handlePaginationChange = e => {
const { value } = e.target;
this.setState({
pagination: value === 'none' ? false : { position: value }
});
};
stringSorter(a, b) {
let a2 = '',
b2 = '';
if (a) {
a2 = a;
}
if (b) {
b2 = b;
}
return a2.localeCompare(b2);
}
stringFilterer(value, record, property) {
let recStatus = '';
if (record[property]) {
recStatus = record[property];
}
return recStatus.indexOf(value) === 0;
}
columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
//width: 50
width: '10%',
sorter: (a, b) => a.id - b.id
},
{
title: 'Name',
dataIndex: 'name',
key: 'name',
//width: 200
width: '15%',
sorter: (a, b) => this.stringSorter(a.name, b.name)
},
{
title: 'Type',
dataIndex: 'type',
key: 'type',
//width: 80
width: '12%',
sorter: (a, b) => this.stringSorter(a.type, b.type),
filters: [
{
text: 'meter',
value: 'meter'
},
{
text: 'climate',
value: 'climate'
},
{
text: 'pole',
value: 'pole'
},
{
text: 'overhead_line',
value: 'overhead_line'
}
],
onFilter: (value, record) => this.stringFilterer(value, record, 'type')
},
{
title: 'Substation',
dataIndex: 'substation',
key: 'substation',
//width: 150
width: '16%',
sorter: (a, b) => this.stringSorter(a.substation, b.substation)
// TODO: Add Filtering by Substation
},
{
title: 'Feeder',
dataIndex: 'feeder',
key: 'feeder',
//width: 100
width: '16%',
sorter: (a, b) => this.stringSorter(a.feeder, b.feeder)
// TODO: Add Filtering by Feeder
},
{
title: 'Status',
dataIndex: 'status',
key: 'status',
//width: 120
width: '18%',
sorter: (a, b) => this.stringSorter(a.status, b.status),
filters: [
{
text: 'IN_SERVICE',
value: 'IN_SERVICE'
},
{
text: 'OUT_OF_SERVICE',
value: 'OUT_OF_SERVICE'
}
],
onFilter: (value, record) => this.stringFilterer(value, record, 'status')
},
{
title: 'Peak\
Vulnerability\
(Pole Stress)',
key: 'vulnerability',
dataIndex: 'vulnerability',
sorter: (a, b) => this.stringSorter(a.vulnerability, b.vulnerability)
//width: 120
}
];
render() {
const { data, handleAssetClick, readyToLoad } = this.props;
{
/* <div style={{ display: 'inline-block', textAlign: 'left' }}>
<Title text="Assets" />
<div>{renderAssets(data, handleAssetClick)}</div>
</div> */
}
return (
<div>
{/* <div className="components-table-demo-control-bar">
<Form layout="inline">
<FormItem label="Bordered">
<Switch checked={this.state.bordered} onChange={this.handleToggle('bordered')} />
</FormItem>
<FormItem label="loading">
<Switch checked={this.state.loading} onChange={this.handleToggle('loading')} />
</FormItem>
<FormItem label="Title">
<Switch checked={!!this.state.title} onChange={this.handleTitleChange} />
</FormItem>
<FormItem label="Column Header">
<Switch checked={!!this.state.showHeader} onChange={this.handleHeaderChange} />
</FormItem>
<FormItem label="Footer">
<Switch checked={!!this.state.footer} onChange={this.handleFooterChange} />
</FormItem>
<FormItem label="Expandable">
<Switch checked={!!this.state.expandedRowRender} onChange={this.handleExpandChange} />
</FormItem>
<FormItem label="Checkbox">
<Switch
checked={!!this.state.rowSelection}
onChange={this.handleRowSelectionChange}
/>
</FormItem>
<FormItem label="Fixed Header">
<Switch checked={!!this.state.scroll} onChange={this.handleScollChange} />
</FormItem>
<FormItem label="Has Data">
<Switch checked={!!this.state.hasData} onChange={this.handleDataChange} />
</FormItem>
<FormItem label="Size">
<Radio.Group size="default" value={this.state.size} onChange={this.handleSizeChange}>
<Radio.Button value="default">Default</Radio.Button>
<Radio.Button value="middle">Middle</Radio.Button>
<Radio.Button value="small">Small</Radio.Button>
</Radio.Group>
</FormItem>
<FormItem label="Pagination">
<Radio.Group
value={this.state.pagination ? this.state.pagination.position : 'none'}
onChange={this.handlePaginationChange}
>
<Radio.Button value="top">Top</Radio.Button>
<Radio.Button value="bottom">Bottom</Radio.Button>
<Radio.Button value="both">Both</Radio.Button>
<Radio.Button value="none">None</Radio.Button>
</Radio.Group>
</FormItem>
</Form>
</div> */}
<Table
size="small"
onRow={record => {
return {
onClick: e => {
this.props.handleAssetClick(e);
} // click row
//onMouseEnter: () => {}, // mouse enter row
};
}}
{...this.state}
columns={this.columns}
dataSource={this.state.hasData ? getAssets(data) : null}
/>
</div>
);
}
}
export default Assets;
This turned out to be a CSS problem. I had padding around all of the SVGs, which made hid these small icons.

Resources