React-Table display contents of array as individual elements inside a cell - react-table

I am trying to use react-table to visualise some data structured as below:
{
companyName: "Barclays Bank Plc",
tenancies: ["G.01 # Newbuilding"],
status: "Active",
accountNumber: "EX209302",
logo: "companylogo1",
},
{
companyName: "Boeing AerospaceLtd",
tenancies: ["G.01 # Newbuilding", "G.02 # Newbuilding"],
status: "Active",
accountNumber: "EX209302",
logo: "companylogo1",
},
My column definition (which doesn't work) looks something like this:
{
Header: "Tenancies",
accessor: "tenancies",
Cell: (tableProps) => (
<div>{tableProps.cell.value.forEach((item) => item)}</div>
),
},
What I am trying to do is display each array item within it's own HTML tags (such as a div) so I can visually style and seperate them within the table cell.
Can anyone point me in the right direction as to the best way to achieve this?

According to the API document, the Cell receives the table Instance and returns a valid JSX.
{
...
Cell: (tableInstance) => JSX
}
Since the tenancies array are available from tableInstance.row.original.tenancies, so you can change your tenancies column definition as follow:
{
Header: "Tenancies",
Cell: ({ row }) => {
return (
<div>
{row.original.tenancies.map((tenancy, i) => (
<div key={i}>{tenancy}</div>
))}
</div>
);
}
}
Here is the example:

Related

How to pass attributes to child Gutenberg blocks in InnerBlocks template?

I'm trying to build a custom set of column blocks using InnerBlocks and am having trouble passing attributes to block templates. To start, I scaffolded a new plugin using Create-Guten-Block. I then created two blocks, one to serve as a row container, and one for individual columns.
Here is a simplified version of how I'm creating the main container (with two columns hard-coded for testing):
registerBlockType( 'wabe/multi-column-block', {
title: __( 'Multi-Column' ),
icon: 'columns',
category: 'common',
keywords: [
__( 'columns' ),
__( 'grid' ),
],
attributes: {
layout: {
type: 'string',
},
},
edit: () => {
return (
<div>
{ /* inspector controls for choosing a layout will go here */ }
<InnerBlocks
template={ [
[ 'wabe/multi-column-column', { columnwidth: '6' }, [
[ 'core/paragraph', { content: 'Insert column content here.' } ],
],
],
[ 'wabe/multi-column-column', { columnwidth: '6' }, [
[ 'core/paragraph', { content: 'Insert column content here.' } ],
],
],
] }
/>
</div>
);
},
save: () => {
// This is a dynamic block
return (
<InnerBlocks.Content />
);
},
} );
... and how I'm creating the individual column block:
registerBlockType( 'wabe/multi-column-column', {
title: __( 'Multi-Column Column' ),
icon: 'columns',
category: 'common',
keywords: [
__( 'columns' ),
__( 'grid' ),
],
attributes: {
columnwidth: {
type: 'string',
default: '',
},
},
edit: ( props ) => {
return (
<div>
<p>Width: { props.attributes.columnwidth }</p>
<InnerBlocks />
</div>
);
},
save: () => {
return (
<InnerBlocks.Content />
);
},
} );
I'll have a custom select for users to choose a column layout, and using that, will determine how many columns to include, and pass the "columnwidth" attribute to each column block. This will tell each column what CSS class to use.
The problem is no matter what I try, that "columnwidth" attribute just will not be passed to the column block. In fact, I can't get it to pass anything, even "className".
I've looked at the code for core columns/column blocks, and can't see what I'm doing wrong. Everything has gone great up until this giant roadblock. Any tips are appreciated.
The block code example you have provided shows you are setting up the InnerBlocks fine. The issue may be a simple oversight that you have missed importing the required dependancy of <InnerBlocks>.
I was able to build your block code successfully once I added the required imports:
import { registerBlockType } from '#wordpress/blocks';
import { __ } from '#wordpress/i18n';
import { InnerBlocks} from '#wordpress/block-editor';
Here is the result:
The code editor shows that your attributes are being passed and being saved.
If you are still encountering an issue, double check that you are clearing your browser cache before retesting and also check the browser console to see if any errors are present.
As you mentioned you have already reviewed the Gutenberg code for Columns/Column, a tip would be to look at how they do variations for creating the options in your Inspector Control. Hope this gets you back on track with creating your block.

How to specify column width for first column using react-table

When using useRowSelect and useTable how can I specify column width for the first column so that it never exceeds the width of the checkboxes? I don't want to use useFlex as I want to preserve column widths based on the header width.
You'll notice when the table is set to 100% width the first column starts to grow if you expand your browser window.
Here's an example:
https://codesandbox.io/s/youthful-hamilton-j0rev?file=/src/App.js
I'm also open to just grabbing the Header and Cell from where I'm pushing into the columns array and adding a className, but wasn't able to find a way to do that. How can I add a className so that the table cell that surrounds this column data has a className to attach to:
(hooks) => {
isRowSelectable &&
hooks.allColumns.push((columns) => [
{
id: 'selection',
minWidth: 35,
width: '35px',
maxWidth: 35,
Header: ({ getToggleAllRowsSelectedProps }) => (
<Checkbox alignSelf="center" {...getToggleAllRowsSelectedProps()} />
),
Cell: ({ row }: any) => {
row.className = '123';
return <Checkbox alignSelf="center" {...row.getToggleRowSelectedProps()} />;
}
},
...columns
]);
}
Since Array.map can pass the index of the element, it's fairly simple to conditionally add a className to the cell if the row index is 0:
{
rows.map((row, i) => {
prepareRow(row);
return (
<td className={i === 0 ? "class-name" : ""} {...cell.getCellProps()>
{cell.render("Cell")}
</td>
) ​
}
If you want the same class to apply to the headers, you can do the same thing on the <th> or <tr> elements, since those are also rendered with Array.map. You don't actually need to touch the columns object at all for this change.

How to draw array items as chips in one element instead of drawing each array item on a separate raw with its own widget?

By default rjsf renders array items as separate fields on the form.
So, for example, this schema/layout:
"jsonSchema" : {
"type" : "object",
"required" : [ "test field" ],
"properties" : {
"test field" : {
"title" : "test field",
"type" : "array",
"items" : {
"type" : "string"
}
}
}
},
"jsonLayout" : {
"ui:order" : [ "test field" ],
"test field" : {
"ui:widget" : "text_multi",
"items" : {
"ui:widget" : "text_multi"
},
"ui:disabled" : false,
"ui:options" : { }
}
}
Will render each string item on its own line together with add,remove,reorder buttons.
But what I want to achieve is using material-ui's Autocomplete element to handle the entire array where one Autocomplete will represent the whole array and items will be chips in it.
So, I used the schema above, and defined this widget:
<Autocomplete
fullWidth
multiple
options={[]}
freeSolo
onChange={this.handleChange}
renderTags={(value: string[], getTagProps) =>
value.map((option: string, index: number) => (
<Chip variant="outlined" label={option} {...getTagProps({ index })} />
))
}
renderInput={(params) => (
<MaterialTextField
{...params}
variant="outlined"
error={this.isError()}
required={this.isRequired()}
label={this.props.label}
disabled={this.isDisabled()}/>
)}
/>
with handleChange method passing array of values like this:
handleChange = (event, value) => {
console.log('%c multi text value', 'background: orange; color: black;', value);
this.props.onChange(value);
}
Autocomplete works as I expect and saves all entered values into array of strings. But later when this array of values is passed somewhere further I get an error from rjsf:
So, what I think happens is that I specified array items as strings and that's why it fails validation.
But is there a way to "cheat" rjsf and achieve what I want? Multiselect in rjsf is also an array of strings, but it works fine as a one component and doesn't render each item on new string...
I tried to play around with ArrayTemplate but it only allows to add a new widget for item with onAddClickProp.

react-table : Add a select (checkboxes) column with a headergroup

For aesthetic reasons I'm trying to add a HeaderGroup to the "select" (checkboxes) column solution provided in react-teable examples.
hooks => {
hooks.visibleColumns.push(columns => {
return [
{
id: 'selection',
// Make this column a groupByBoundary. This ensures that groupBy columns
// are placed after it
groupByBoundary: true,
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
},
...columns,
]
})
Source:
https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/kitchen-sink
I've tried many ways using hooks object but none of them worked (for example nested calls to hooks.headerGroups.push and then hooks.visibleColumns.push).
Here is my last attempt which makes sense but doesn't work either (TypeError: Cannot read property 'forEach' of undefined) :
hooks => {
hooks.headerGroups.push(headerGroups => {
return [
{
Header: 'X',
columns: [
{
id: 'selection',
// Make this column a groupByBoundary. This ensures that groupBy columns
// are placed after it
groupByBoundary: true,
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
}
]
},
...headerGroups
]
})
})
Thank you for your help ^^

Has column show property stopped working in latest react-table v7?

I have used the show property to show/hide columns in my table earlier and it has worked fine using react-table v7. However, recently I cannot get it to work any longer, since I've made a bunch of changes and my table is quite complex I'm not sure what caused it, possibly also an update of react-table itself (7.0.0-beta.12 to 7.0.0-rc.5).
Anyway, now I can't even get the most basic show example to work:
const columns = React.useMemo(
() => [
{
Header: "Info",
columns: [
{
Header: "Age",
accessor: "age",
show: false
},
{
Header: "Visits",
accessor: "visits"
}
]
}
],
[]);
https://codesandbox.io/s/react-table-hide-column-2g3js
Why is the 'age' column showing?
Edit
Digging into the changelog I now understand that column show/hide has indeed changed:
7.0.0-beta.28 Added the useColumnVisibility plugin as a core plugin along with several new instance and column-level methods to control
column visibility Added the "column-hiding" example
However, I still have not figured out how to apply the useColumnVisibility hook to a column in a similar way to how show used to work. The "column-hiding" example shows how to do it with checkboxes but does not help in my case (afaik).
I had a similar issue as I was using show. Instead of setting show in the column, set the initialState.hiddenColumns, here you can convert your existing show into the initial hidden columns:
useTable<T>(
{
columns,
data,
hiddenColumns: columns.filter(column => !column.show).map(column => column.id)
},
I couldn't use that as my columns are loaded dynamically (so the initial state was set to some columns that didn't exist, and was just set to an empty array) so I used:
React.useEffect(
() => {
setHiddenColumns(
columns.filter(column => !column.show).map(column => column.id)
);
},
[columns]
);
where setHiddenColumns is provided by useTable:
const {
headerGroups,
rows,
prepareRow,
state,
toggleRowSelected,
toggleAllRowsSelected,
setHiddenColumns
} = useTable<T>(
{
columns,
data,
getRowId,
...
This means if I changed the column props it would be reflected in the table.
I also use setHiddenColumns to show/hide columns. However, instead returning Header, I use id. My code:
...
setHiddenColumns,
flatColumns,
headerGroups,
state: { pageIndex, pageSize },
} = useTable({
columns,
data,
initialState: { pageIndex: 0 },
manualPagination: true,
pageCount: controlledPageCount,
},
useSortBy,
usePagination
);
React.useEffect(() => {
const hiddenColumns = flatColumns.filter((column: any) => !column.show).map((column: any)=> column.id);
setHiddenColumns(hiddenColumns); }, []);

Resources