Skip to content

OBPIH-6110 Implement behavior for the X rows require your attention#4520

Merged
awalkowiak merged 15 commits intofeature/product-supplier-list-redesignfrom
OBPIH-6110
Feb 28, 2024
Merged

OBPIH-6110 Implement behavior for the X rows require your attention#4520
awalkowiak merged 15 commits intofeature/product-supplier-list-redesignfrom
OBPIH-6110

Conversation

@alannadolny
Copy link
Collaborator

No description provided.

};
};

export default useProductSupplierValidation;
Copy link
Collaborator

Choose a reason for hiding this comment

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

was there a reason it became a .jsx file?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

to be honest, I don't know, but I am changing it back to .js

id: z.string()
.optional(),
code: z.string()
.optional(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

let's chain id and code to follow the convention below.

<InputWrapper
title={title}
errorMessage={errorMessage}
errorMessage={displayErrorMessage ? errorMessage : null}
Copy link
Collaborator

Choose a reason for hiding this comment

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

something doesn't feel right to me about this one. If you need such a flag, it should be a separate flag imho.
I didn't see the context yet, so I would eventually extend the topic in the file where this displayErrorMessage prop is passed.

// If row index is equal to index of invalid row and if the row is dirty
// it means it's invalid
const isRowValid = (rowIndex) => {
const isRowDirty = _.some(Object.values(updatedRows?.[rowIndex] || {}));
Copy link
Collaborator

Choose a reason for hiding this comment

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

what's the purpose of that method, what does it do behind the scene? An empty object is truthy, so I assume that this method will always return true, hence the && isRowDirty might not make sense?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This empty object is a fallback in Object.values, so it returns an empty array, and then we are checking if something is true within this array. True means that we entered a value in any field in the row.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I didn't realize the fallback is inside the ( ). I think this shortcut is not intuitive (not passing predicate to the _.some function). This is why I hate lodash for methods that are available in the pure JS 🥲

Copy link
Collaborator

Choose a reason for hiding this comment

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

The naming of this function probably does not make sense.
if has errors on index and is dirty which means that row is invalid, soooo maybe the method should be isRowInvalid ?

);
import Button from 'components/form-elements/Button';

const invalidLines = {
Copy link
Collaborator

@kchelstowski kchelstowski Feb 27, 2024

Choose a reason for hiding this comment

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

what's the purpose of those two consts? Could the name be more intuitive on what it means, not what it does?

Copy link
Collaborator

Choose a reason for hiding this comment

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

After getting to know the context, let's at least name it as invalidLinesButton/validLinesButton

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was bothered about the naming here, so thanks for the suggestion.

wrapperClassName: 'is-valid',
};

const appliedFiltering = ({
Copy link
Collaborator

Choose a reason for hiding this comment

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

the same comment on that one - by reading appliedFiltering it's difficult to understand what's the purpose of that method.
Moreover I think the name of a method should not be an adjective.

export const DateFormat = {
MM_DD_YYYY: 'MM/DD/YYYY',
MMM_DD_YYYY: 'MMM DD, yyyy',
MMM_DD_YYYY: 'MMM DD, YYYY',
Copy link
Collaborator

Choose a reason for hiding this comment

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

what does it change out of curiosity to change from yyyy to YYYY?

Copy link
Collaborator Author

@alannadolny alannadolny Feb 27, 2024

Choose a reason for hiding this comment

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

This was my mistake during rebase, when I noticed that I didn't replace LL with MMMM DD, yyyy I just changed it by hand. It doesn't matter if this one is yyyy or YYYY

|| errors?.[index]?.[property]?.id?.message;
const isRowDirty = _.some(Object.values(updatedRows?.[index] || {}));

if (!isRowDirty) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

the same comment as somewhere above/below - won't it always be truthy, since an empty object is truthy and so is an empty array?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the same answer as above/below 😆

return (
<SelectField
options={buyers}
errorMessage={hasErrors}
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is why I said something didn't feel right to me - imho it feels like a workaround. errorMessage is supposed to receive a string message and in this case we pass a boolean.
I think if we need such a case, we should think about making the errorMessage as optional prop and add a new one hasErrors that would handle the logic you need.
I think it would be better than mixing the expected type of the errorMessage and adding displayErrorMessage prop.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Basically, I added a displayErrorMessage prop to avoid having this hasErrors 😆
The idea of this is to avoid passing hasErrors and errorMessage each time. For now, we can only pass errorMessage and this prop is an indicator of whether we have to display an error. The displayErrorsMessage is an additional prop turning on/off the captions under the input field. For me, this is a little bit easier to use than the solution you proposed. I am interested in what other developers think about that, so If they have similar feelings as you, I can change it.

cc @awalkowiak @drodzewicz

Copy link
Collaborator

Choose a reason for hiding this comment

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

on one hand you say:

errorMessage and this prop is an indicator of whether we have to display an error.

so if errorMessage is the indicator, then what the displayErrorMessage is? Imho the errorMessage should NOT be the indicator, because the name says error message, so we expect it to be a string, not a boolean/an indicator.

If we wanted somewhat a compromise, I'd suggest either (I think not recommended since you would have to change it in every place that has been done) changing from errorMessage to error object that would receive message and showMessage properties or......

I'll let the others speak, but I strongly feel like we should not mix the destination of the errorMessage to use it once as a string and once as somewhat an indicator and pass a boolean.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Eh, the errorMessage is a string, if it has content then the error is displayed, if it doesn't the the content is not visible (this is what I meant by indicator here).
The displayErrorMessage is just a prop for showing the caption under the input. If we don't pass it, then the error is visible by default.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, it should be a string, but you actually pass there hasErrors which is a boolean.
I still don't feel convinced about the approach on that. @drodzewicz @awalkowiak what do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@alannadolny you can do a workaround to Kacpers requirements 😁

const errorMessage = isFieldValid(row.index, 'destinationParty') ? null : 'validation error';
return (
   <SelectField
     options={buyers}
     errorMessage={errorMessage}
     ...

But if we want to be strict with ourselves I think we would need to add an additional isValid prop which would trigger some error classes and such.
Basically, errorMessage prop should only be responsible for the error message and isValid for everything else like red border classes etc..

});

const handleOnFilterButtonClick = () => {
trigger('productSupplierPreferences');
Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps it would be better to move it to const? Would it be possible to give this function a better name, so it's clear what it triggers?

isRowValid,
} = usePreferenceTypeVariationsFiltering({ errors, updatedRows });

const { columns, translate } = usePreferenceTypeVariationsColumns({
Copy link
Collaborator

Choose a reason for hiding this comment

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

I know this was implemented in a previous ticket but this caught my eye.
You are extracting translate from usePreferenceTypeVariationsColumns hook which I don't think is a good idea. I feel like translate should have it's own independent hook which just returns the translate function from redux.
It can be as simple as

const useTranslate = () => {
  const {
    translate,
  } = useSelector((state) => ({
    translate: translateWithDefaultMessage(getTranslate(state.localize)),
  }));

  return translate;
}

It's just translate doesn't have anything to do with PreferenceTypeVariationsColumns but this hook is returning. I understand that translate is also used inside this hook but still I strongly believe we should separate this.

// If row index is equal to index of invalid row and if the row is dirty
// it means it's invalid
const isRowValid = (rowIndex) => {
const isRowDirty = _.some(Object.values(updatedRows?.[rowIndex] || {}));
Copy link
Collaborator

Choose a reason for hiding this comment

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

The naming of this function probably does not make sense.
if has errors on index and is dirty which means that row is invalid, soooo maybe the method should be isRowInvalid ?

<InvalidItemsIndicator className="mr-3" errorsCounter={_.filter(errors)?.length} />
<InvalidItemsIndicator
className="mr-3"
errorsCounter={Object.keys(errors).filter(isRowValid).length}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I feel like this whole line

Object.keys(errors).filter(isRowValid).length

should be returned from usePreferenceTypeVariationsFiltering hook as invalidRowCount or something similar.

Comment on lines 73 to 76
onClick={() => {
prepend(defaultTableRow);
triggerValidation('productSupplierPreferences');
}}
Copy link
Collaborator

Choose a reason for hiding this comment

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

could maybe move this arrow function to a declared function to have context on what it's doing?
like

const addNewLine = () => {
   prepend(defaultTableRow);
   triggerValidation('productSupplierPreferences');
}

Comment on lines 39 to 40
const hasFieldErrors = errors?.[index]?.[property]?.message
|| errors?.[index]?.[property]?.id?.message;
Copy link
Collaborator

Choose a reason for hiding this comment

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

a lot of nested characters, can we split this up a bit 😅

const fieldError = errors?.[index]?.[property];
const fieldErrorMessage = fieldError?.message || fieldError?.id.message;

@awalkowiak awalkowiak merged commit 74b54e2 into feature/product-supplier-list-redesign Feb 28, 2024
@awalkowiak awalkowiak deleted the OBPIH-6110 branch February 28, 2024 09:58
awalkowiak pushed a commit that referenced this pull request Mar 12, 2024
…4520)

* OBPIH-6109 Fix datepicker format

* OBPIH-6109 Add translations

* OBPIH-6109 Fix styling of v2 input components

* OBPIH-6109 Fix placement of datefield component in subsections

* OBPIH-6109 Sort imports

* OBPIH-6110 Create filtering hook

* OBPIH-6110 Adjust showing errors in table

* OBPIH-6110 Change eslint rule to not show error on leading _

* OBPIH-6110 Adjust styling

* OBPIH-6110 Fix time format

* OBPIH-6110 Fix triggering validation and underlining rows

* OBPIH-6110 Add validation for duplicated organzations

* OBPIH-6110 Fix issue with detached dropdown and some styling changes

* OBPIH-6110 Fixes after review

* OBPIH-6110 Changes after review
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.

4 participants