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

feat(Table): support inline editing in PF4 #1227

Merged
merged 2 commits into from
Mar 25, 2019

Conversation

atiratree
Copy link

@atiratree atiratree commented Jan 22, 2019

Hi, I tried to tackle inline editing with reactabular. But it is still not perfect and a bit hacky.

My main goal was to prevent this behavior: kubevirt/web-ui-components#9, but other issues are still present.

  • inability to set a cursor to other edited cell (the cursor will firstly appear at the end and then it can be moved)
  • no possibility for real time validation (char by char)

This is a due to the reactabular architecture, where we cannot expect our cells to behave in react-like way and have to implement workarounds introduced by this PR. It is inspired by https://github.com/reactabular/react-edit and uses onBlur and autoFocus on input elements

There is one issue with gatsby? where the whole table page is refreshed and the table component is called by costructor. This happens on a dropdown selection in Editable table With Inline Edit Row

live site: https://1227-pr-patternfly-react-patternfly.surge.sh/patternfly-4/components/inlineedit

TODO:

  • finish styles
  • move hardcoded styles to pf-next
  • move to separate package
  • add component extensibility
  • add typescript support
  • tests

Current Issues:

@priley86 @karelhala

@@ -52,7 +52,7 @@ const defaultProps = {
isDisabled: false,
isReadOnly: false,
type: 'text',
value: null,
value: undefined,
Copy link
Author

Choose a reason for hiding this comment

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

to allow uncontrolled components like TableTextInput

@@ -0,0 +1,8 @@
// TODO: better sharing util components between modules
Copy link
Author

Choose a reason for hiding this comment

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

this is copied from @react-core internal. Does someone have any recommendations how to share utility classes between modules?

Copy link
Member

Choose a reason for hiding this comment

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

added #1232 for this. If it merges you can:

import { debounce } from '@patternfly/react-core';

Copy link
Author

Choose a reason for hiding this comment

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

thanks

onClick: (event) => onRowClick(event, row, props)
row,
rowProps: props,
onMouseDown: (event) => {
Copy link
Author

Choose a reason for hiding this comment

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

onMouseDown is needed to register clicks before onChange/onBlur changes the row (which discards the onClick)

Copy link
Member

@priley86 priley86 Jan 22, 2019

Choose a reason for hiding this comment

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

this seems like kind of a hack, but works. I wonder if proposing an upstream PR to reactabular in parallel to allow hooks into onChange onBlur would be a good idea?

Copy link
Author

Choose a reason for hiding this comment

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

Where would the hooks be? I am not sure something like this would help.
The idea of this is to get ahead of cell onBlur callbacks. Because of the cell's onBlur callback, the cell will be scrapped and then constructed again. And then we get a chance to fire callbacks (captured here) on our new instance.

Copy link
Author

Choose a reason for hiding this comment

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

it is a hack nevertheless :)

Copy link
Contributor

Choose a reason for hiding this comment

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

One note, can this function be moved outside of this component? So it would be easier to read the component when debugging?

};

export const TableEditConfirmation = {
NONE: 'NONE', // TODO: do edit buttons make sense on the left?
Copy link
Author

Choose a reason for hiding this comment

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

Should we include more combinations of edit buttons? E.g. on the left?

Copy link
Contributor

Choose a reason for hiding this comment

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

This question is better to be asked any UX person @mcarrano any thoughts on this?

};

// TODO: shouldComponentUpdate?
// TableConfirmButtonsRow.shouldComponentUpdate = true;
Copy link
Author

Choose a reason for hiding this comment

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

not really sure yet if shouldComponentUpdate is needed? It was present in the pf3 component

Copy link
Contributor

Choose a reason for hiding this comment

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

As long as you are not trying to calculate re-renders for you we don't really need to use shouldComponentUpdate

privateRepo: false
}
// isEditing: true,
// activeEditCell: 3
Copy link
Author

Choose a reason for hiding this comment

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

possibility to select editing programatically

@coveralls
Copy link

coveralls commented Jan 22, 2019

Pull Request Test Coverage Report for Build 4563

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • 31 unchanged lines in 3 files lost coverage.
  • Overall coverage increased (+2.08%) to 81.212%

Files with Coverage Reduction New Missed Lines %
packages/patternfly-4/react-core/src/components/Dropdown/Separator.js 1 80.0%
packages/patternfly-4/react-core/src/components/Dropdown/DropdownItem.js 4 34.52%
packages/patternfly-4/react-core/src/helpers/util.ts 26 14.29%
Totals Coverage Status
Change from base Build 4547: 2.08%
Covered Lines: 3670
Relevant Lines: 4239

💛 - Coveralls

@karelhala karelhala self-requested a review January 22, 2019 13:38
@priley86
Copy link
Member

Added issue 1228 for this.

case TableEditConfirmation.ROW:
if (isEditing) {
editStyle = {
background: '#def3ff',
Copy link
Member

Choose a reason for hiding this comment

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

can you replace all color references with actual color tokens from @patternfly/react-tokens (ex consumption here )?

you can also find these and their default mappings here:
http://patternfly-react.surge.sh/patternfly-4/styles/tokens/

If these colors don't exist, maybe we can subtitute an existing pf-next color?

Copy link
Author

Choose a reason for hiding this comment

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

yes, I am planning to do the css cleanup

@dgutride
Copy link
Member

dgutride commented Jan 22, 2019

This seems like a great idea; however, we are avoiding adding anything to react-core or the core packages (which include react-table) that do not have backing PatternFly patterns and documentation. Would it be possible to move this to a new @ patternfly / {name of packaqge} - extension package so we can consider including it into core PatternFly at a later date?

@@ -2,13 +2,14 @@ import React from 'react';
import { Body } from 'reactabular-table';
import PropTypes from 'prop-types';
import { TableContext } from './Table';
import '../../../internal/closestPolyfill';
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a sideEffect and will break webpack 4 (well not be imported in production)., It is also generally a bad idea to pollute prototypes for the user. This should be an opt-in pollyfill by the consumer. We can still export it and if they want/need it they can use it. Also since by default PF4 is excluding IE support (a workaround is coming for consumers needing it) the closest polyfill is not needed.

If we do for sure want to keep this in the lib it would be better to do something like:

function closest(element, selector) {
  if (typeof element.closest === 'function') {
    return element.closest(selector);
  }

  if (document.documentElement.contains(el)) {
    while (el && el.nodeType === 1) {
      if (el.matches(selector)) return el;
      el = el.parentElement || el.parentNode;
    }
  }
  return null;
}

Copy link
Author

Choose a reason for hiding this comment

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

Good to know. I just assumed it would be the same as PF3 so I copied it from there. Removed

export const matches =
Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;

if (!Element.prototype.matches) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not needed at all. This is supported by all major browsers including IE9+

return null;
}

if (!Element.prototype.closest) {
Copy link
Contributor

Choose a reason for hiding this comment

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

See comment in packages/patternfly-4/react-table/src/components/Table/Body.js

onRowClick(event, row, props);
if (onEditCellChanged) {
// edit cell after rerender
setTimeout(onEditCellChanged, 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems kind of "hacky" with upcoming react releases it is possible that the rerender may not have actually taken place here. will that be ok? I can't tell from the code if that will cause issues or not.

Copy link
Author

Choose a reason for hiding this comment

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

In that case we would really have to contribute to reactabular and fix these issues. Yup it is hacky, but there are not many other solutions.

@atiratree
Copy link
Author

This seems like a great idea; however, we are avoiding adding anything to react-core or the core packages (which include react-table) that do not have backing PatternFly patterns and documentation. Would it be possible to move this to a new @ patternfly / {name of packaqge} - extension package so we can consider including it into core PatternFly at a later date?

Sorry for late response because of the PTO.

I get the idea of putting bigger features to separate packages.
Nevertheless, I think inline editing is quite integral part of the table and it would be less complicated for the user to not use multiple packages. It is possible, but it would be quite cumbersome and not quite worth it in my opinion.

Btw, do you mean patternfly next patterns or https://www.patternfly.org/pattern-library/forms-and-controls/inline-edit/ ?

@atiratree atiratree force-pushed the pf4-table-inline-edit branch 2 times, most recently from ac5c96e to f8864c5 Compare February 1, 2019 17:16
Copy link
Contributor

@karelhala karelhala left a comment

Choose a reason for hiding this comment

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

Really nice extension! I like how you are passing different editors for different edits. That is really great approach. Few nitpicks, but overall good PR!

onClick: (event) => onRowClick(event, row, props)
row,
rowProps: props,
onMouseDown: (event) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

One note, can this function be moved outside of this component? So it would be easier to read the component when debugging?

hidden={isExpanded !== undefined && !isExpanded}
/>
);
const hasConfirmationButtons = (editConfirmationType, { isEditing, isTableEditing, isFirst, isLast }) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

These function with switch can be converted to object, this approach is much more extensible.

const tableMapper = {
  [TableEditConfirmation.TABLE_TOP]: ({ isTableEditing, isFirst }) => isTableEditing && isFirst,
  [TableEditConfirmation.TABLE_BOTTOM]: ({ isTableEditing, isFirst }) => isTableEditing && isLast,
  [TableEditConfirmation.ROW]: ({ isEditing }) => isEditing,
  [TableEditConfirmation.NONE]: () => false
}

You would then use it like

const hasConfirmationButtons = (editConfirmationType, props) => {
  tableMapper[editConfirmationType] && tableMapper[editConfirmationType](props);
}

Copy link
Author

Choose a reason for hiding this comment

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

you are right, it looks much cleaner now

}
};

export const areButtonsOnTop = (editConfirmationType, { isLast }) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Similiar approach as with the previous function.

let editStyle;

if (isTableEditing) {
switch (editConfirmationType) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Honestly I don't really like switches, they tend to be really complex, hard to read and even harder to extend. Please consider using object literal with functions.

}
const { editConfirmationType, onEditConfirmed, onEditCanceled } = editConfig;

let confirmButtons;
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to assign to variable if we are not gonna do anything with it and just return it. It's much cleaner to just return from if statement. When the if is not satisfied function will return undefined nevertheless.

Copy link
Author

Choose a reason for hiding this comment

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

I would rather remove it too, but I kept that because of eslint/prettier. I am not sure how do we stand on that?

};

export const TableEditConfirmation = {
NONE: 'NONE', // TODO: do edit buttons make sense on the left?
Copy link
Contributor

Choose a reason for hiding this comment

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

This question is better to be asked any UX person @mcarrano any thoughts on this?

};

// TODO: shouldComponentUpdate?
// TableConfirmButtonsRow.shouldComponentUpdate = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

As long as you are not trying to calculate re-renders for you we don't really need to use shouldComponentUpdate

) => {
const { rowData, columnIndex } = additionalData;

let result;
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to assign to result if we are only returning it. You can add return instead of assign to each if statements and it wil be the same.

Copy link
Author

Choose a reason for hiding this comment

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

you are right, thanks

@karelhala
Copy link
Contributor

@suomiy what @dgutride is proposing is kinda understandable. We want to ship table component as pure as possible with only few enhancers and if consumers want to use inline edit they'd import @patternfly/react-table-inline-edit for instance. Inside of this package there would be few functions and components reponsible for edits. The code would still be part of this repository, it would just be moved into special folder and a bit of config is needed. If you want I can help you by moving it there or you can get inspired by @priley86 #1250 about virtualized scroll.

@atiratree atiratree force-pushed the pf4-table-inline-edit branch from f8864c5 to 18067b4 Compare February 4, 2019 11:02
@atiratree
Copy link
Author

@suomiy what @dgutride is proposing is kinda understandable. We want to ship table component as pure as possible with only few enhancers and if consumers want to use inline edit they'd import @patternfly/react-table-inline-edit for instance. Inside of this package there would be few functions and components reponsible for edits. The code would still be part of this repository, it would just be moved into special folder and a bit of config is needed. If you want I can help you by moving it there or you can get inspired by @priley86 #1250 about virtualized scroll.

Ok, I guess it makes sense to separate it. I will look into that.
Nevertheless I think there will still be some overlap of the code. I will discuss this with @priley86.

@patternfly-build
Copy link
Contributor

PatternFly-React preview: https://1227-pr-patternfly-react-patternfly.surge.sh

@karelhala
Copy link
Contributor

@suomiy if you need some special variables to be passed to core implementation that is fine, that code won't be used. But if we split this into another package we have really great way of importing custom packages and promote other teams to contribute and use some other packages (even written outside of PF world)

@tlabaj tlabaj added the PF4 label Feb 4, 2019
@atiratree atiratree force-pushed the pf4-table-inline-edit branch 4 times, most recently from 645f600 to e2ce121 Compare February 15, 2019 13:15
@atiratree atiratree force-pushed the pf4-table-inline-edit branch 3 times, most recently from 18a037b to 47b70e6 Compare February 15, 2019 14:57
@jschuler
Copy link
Collaborator

I've removed the Do not merge label since it's in its own package now @dgutride
@priley86 @karelhala please review again :)

@atiratree atiratree force-pushed the pf4-table-inline-edit branch 4 times, most recently from dd96fd9 to 1d8b7f2 Compare March 21, 2019 19:38
@atiratree
Copy link
Author

Fixed TS and added support for rendering NO_CONFIRM_ROW type.

This PR is ready for final reviews (sibling #1520 was merged).

@priley86
Copy link
Member

great job on this @suomiy! I know you are eager to iterate on this extension downstream. I filed my follow-up issue just now in #1628. I am happy to see this merge when others are.

jschuler
jschuler previously approved these changes Mar 22, 2019
@priley86
Copy link
Member

getting these here locally now...can we fix them first?

@patternfly/react-docs: error There was a problem parsing component metadata for file: "components/InlineEdit/editableRowWrapper.js"
@patternfly/react-docs: 
@patternfly/react-docs:   TypeError: Property name must be an Identifier or a Literal
@patternfly/react-docs:   
@patternfly/react-docs:   - getPropertyName.js:36 getPropertyName

@atiratree
Copy link
Author

getting these here locally now...can we fix them first?

fixed, seems like we can't have dynamic object keys (reactjs/react-docgen#317 (comment))

+ added a comment about this issue to the code as the constants are now hardcoded

priley86
priley86 previously approved these changes Mar 22, 2019
Copy link
Member

@priley86 priley86 left a comment

Choose a reason for hiding this comment

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

react-docs look good and tests pass locally. Please just update the patternfly dependency, but will go ahead and approve. nice work!

@karelhala karelhala self-requested a review March 25, 2019 08:11
karelhala
karelhala previously approved these changes Mar 25, 2019
Copy link
Contributor

@karelhala karelhala left a comment

Choose a reason for hiding this comment

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

Looks really good! Thank you for this PR!

@atiratree atiratree dismissed stale reviews from karelhala and priley86 via 21ca9d8 March 25, 2019 10:22
@atiratree atiratree force-pushed the pf4-table-inline-edit branch from 931e249 to 21ca9d8 Compare March 25, 2019 10:22
@atiratree atiratree force-pushed the pf4-table-inline-edit branch from 21ca9d8 to dfb18fd Compare March 25, 2019 12:09
@atiratree
Copy link
Author

updated @patternfly/patternfly dependency

Copy link
Collaborator

@jschuler jschuler left a comment

Choose a reason for hiding this comment

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

Thanks @suomiy , great work on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.