Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: [DHIS2-17650] Replace Material-UI Table, TableBody, TableCell, TableHead and TableRow #3721

Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ Then('the working list should be updated', () => {
.click();

cy.contains('WHOMCH Hemoglobin value')
.parents('tr')
.find('input[type="checkbox"]')
.click();

cy.contains('Save')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ When('you open the column selector', () => {
When('you select Household location and save from the column selector', () => {
cy.get('aside[role="dialog"]')
.contains('Household location')
.find('input')
.parents('tr')
.find('input[type="checkbox"]')
.click();

cy.get('aside[role="dialog"]')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@ When('you open the column selector', () => {
When('you select the organisation unit and save from the column selector', () => {
cy.get('aside[role="dialog"]')
.contains('Organisation unit')
.find('input')
.parents('tr')
.find('input[type="checkbox"]')
.click();

cy.get('aside[role="dialog"]')
Expand Down Expand Up @@ -613,7 +614,8 @@ When('you select the Foci response program stage', () => {
When('you select a data element columns and save from the column selector', () => {
cy.get('aside[role="dialog"]')
.contains('People included')
.find('input')
.parents('tr')
.find('input[type="checkbox"]')
.click();

cy.get('aside[role="dialog"]')
Expand Down Expand Up @@ -674,7 +676,8 @@ Then('you see scheduledAt filter', () => {
When('you select a scheduledAt column and save from the column selector', () => {
cy.get('aside[role="dialog"]')
.contains('Appointment date')
.find('input')
.parents('tr')
.find('input[type="checkbox"]')
.click();

cy.get('aside[role="dialog"]')
Expand Down
7 changes: 5 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2024-06-28T11:23:02.970Z\n"
"PO-Revision-Date: 2024-06-28T11:23:02.970Z\n"
"POT-Creation-Date: 2024-07-29T16:38:48.947Z\n"
"PO-Revision-Date: 2024-07-29T16:38:48.947Z\n"

msgid "Choose one or more dates..."
msgstr "Choose one or more dates..."
Expand Down Expand Up @@ -568,6 +568,9 @@ msgstr "Columns to show in table"
msgid "Column"
msgstr "Column"

msgid "Visible"
msgstr "Visible"

msgid "Update"
msgstr "Update"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,27 @@
// @flow
import React, { Component } from 'react';

import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'react-addons-update';

import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';

import { DataTable, TableHead, TableBody, DataTableRow, DataTableColumnHeader } from '@dhis2/ui';
import i18n from '@dhis2/d2-i18n';

import { DragDropListItem } from './DragDropListItem.component';

type Props = {
listItems: Array<Object>,
handleUpdateListOrder: (sortedList: Array<Object>) => void,
handleToggle: (id: string) => () => void,
handleToggle: (id: string) => () => any,
};

export class DragDropList extends Component<Props> {
moveListItem: (dragIndex: any, hoverIndex: any) => void;
moveListItem: (dragIndex: number, hoverIndex: number) => void;

constructor(props: Props) {
super(props);
this.moveListItem = this.moveListItem.bind(this);
}

moveListItem(dragIndex: any, hoverIndex: any) {
moveListItem(dragIndex: number, hoverIndex: number) {
const { listItems } = this.props;
const dragListItem = listItems[dragIndex];
let sortedList = [];
Expand All @@ -47,11 +40,13 @@ export class DragDropList extends Component<Props> {

return (
<DndProvider backend={HTML5Backend}>
<Table>
<DataTable>
<TableHead>
<TableRow>
<TableCell colSpan={12}>{i18n.t('Column')}</TableCell>
</TableRow>
<DataTableRow>
<DataTableColumnHeader />
<DataTableColumnHeader>{i18n.t('Column')}</DataTableColumnHeader>
<DataTableColumnHeader>{i18n.t('Visible')}</DataTableColumnHeader>
</DataTableRow>
</TableHead>
<TableBody>
{listItems.map((item, i) => (
Expand All @@ -66,7 +61,7 @@ export class DragDropList extends Component<Props> {
/>
))}
</TableBody>
</Table>
</DataTable>
</DndProvider>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,107 +1,69 @@
// @flow
import React, { Component } from 'react';
import { DragSource, DropTarget } from 'react-dnd';
import { Checkbox, IconReorder24, spacersNum } from '@dhis2/ui';
import TableCell from '@material-ui/core/TableCell';
import { withStyles } from '@material-ui/core/styles';
import React, { useRef } from 'react';
import { useDrag, useDrop } from 'react-dnd';
import { DataTableRow, DataTableCell, Checkbox } from '@dhis2/ui';

const styles = () => ({
checkbox: {
marginTop: spacersNum.dp12,
marginBottom: spacersNum.dp12,
},
});
const ItemTypes = {
LISTITEM: 'listItem',
};

type Props = {
id: string,
visible: boolean,
text: string,
handleToggle: (id: string) => void,
isDragging: () => void,
connectDragSource: (any) => void,
connectDropTarget: (any) => void,
classes: {
checkbox: string,
}
};

const style = {
cursor: 'move',
outline: 'none',
};

const ItemTypes = {
LISTITEM: 'listItem',
index: number,
handleToggle: (id: string) => any,
moveListItem: (dragIndex: number, hoverIndex: number) => void,
};

const cardSource = {
beginDrag(props) {
return {
id: props.id,
index: props.index,
};
},
};
export const DragDropListItem = ({ id, index, text, visible, handleToggle, moveListItem }: Props) => {
const ref = useRef(null);

const cardTarget = {
hover(props, monitor) {
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;
const [, drop] = useDrop({
accept: ItemTypes.LISTITEM,
hover(item: { id: string, index: number }) {
const dragIndex = item.index;
const hoverIndex = index;

// Don't replace items with themselves.
if (dragIndex === hoverIndex) {
return;
}
// Don't replace items with themselves.
if (dragIndex === hoverIndex) {
return;
}

// Time to actually perform the action.
props.moveListItem(dragIndex, hoverIndex);
// Time to actually perform the action
moveListItem(dragIndex, hoverIndex);

// Note: we're mutating the monitor item here!
// Generally it's better to avoid mutations,
// but it's good here for the sake of performance
// to avoid expensive index searches.
monitor.getItem().index = hoverIndex;
},
};
// Note: we're mutating the item here!
// Generally it's better to avoid mutations,
// but it's good here for the sake of performance
// to avoid expensive index searches.
item.index = hoverIndex;
},
});

class Index extends Component<Props> {
render() {
const { text, isDragging, connectDragSource, connectDropTarget } = this.props;
const opacity = isDragging ? 0 : 1;
const [{ isDragging }, drag] = useDrag({
type: ItemTypes.LISTITEM,
item: { id, index },
collect: monitor => ({
isDragging: monitor.isDragging(),
}),
});

// $FlowFixMe[incompatible-extend] automated comment
return connectDropTarget(connectDragSource(
<tr key={this.props.id} tabIndex={-1} style={{ ...style, opacity }}>
<TableCell component="th" scope="row">
<Checkbox
checked={this.props.visible}
tabIndex={-1}
onChange={this.props.handleToggle(this.props.id)}
label={text}
className={this.props.classes.checkbox}
valid
dense
/>
</TableCell>
<TableCell>
<span style={{ float: 'right' }}>
<IconReorder24 />
</span>
</TableCell>
</tr>,
));
}
}
drag(drop(ref));

export const DragDropListItemPlain =
DragSource(
ItemTypes.LISTITEM,
cardSource,
(connect, monitor) => ({ connectDragSource: connect.dragSource(), isDragging: monitor.isDragging() }),
)(DropTarget(
ItemTypes.LISTITEM,
cardTarget,
connect => ({ connectDropTarget: connect.dropTarget() }),
)(Index));
const opacity = isDragging ? 0.5 : 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I can see that the hover effect can be confusing under drag, but I found it hard to fix it.

How about setting the opacity to 0 when dragging? This way the drawn row will look empty and can help with the confusion of the hover effect (this is also how it currently works in play with the material UI components).

Copy link
Contributor Author

@henrikmv henrikmv Jul 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skjermbilde 2024-07-31 kl  11 35 33

@simonadomnisoru
I agree that setting the opacity to 0 makes it better so I will change it to:
const opacity = isDragging ? 0 : 1;

However, the table row hover is still confusing. I added a picture where I am dragging the Gender, but the hover is marking Last name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use Firefox where the hover issue doesn't happen, but I was able to reproduce it in Chrome. I bealive you can remove the hover styles altogether with something like this in the DragDropListItem.component.js.

// the styles

const styles = {
    rowWithoutHover: {
        '&:hover > td': {
            backgroundColor: 'transparent !important',
        },
    },
};

// and in the render use the class

return (
        <DataTableRow ref={ref} style={{ opacity }} className={classes.rowWithoutHover} draggable>
            <DataTableCell>{text}</DataTableCell>
            ......

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @simonadomnisoru! That works. With the changes, the hover was completely removed, so in addition to your suggested changes, I added a state to tell if any element in the list is being dragged. Based on that state, I will disable the hover. What do you think? I appreciate your help!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks great now 👏


export const DragDropListItem = withStyles(styles)(DragDropListItemPlain);
return (
<DataTableRow ref={ref} style={{ opacity }} draggable>
<DataTableCell>{text}</DataTableCell>
<DataTableCell>
<Checkbox
checked={visible}
onChange={handleToggle(id)}
valid
dense
/>
</DataTableCell>
</DataTableRow>
);
};
Loading