-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Add OfflineWithFeedback component #9931
Conversation
dca81f3
to
915c5a5
Compare
This is not ready yet, I need to do some more testing, but can you review this in the meantime? |
style={[styles.peopleRowCell]} | ||
isChecked={_.contains(this.state.selectedEmployees, item.login)} | ||
disabled={!canBeRemoved} | ||
<OfflineWithFeedback error="This is an errors This is an errors This is an errors This is an errors This is an errors This is an errors " style={styles.peopleRowOfflineFeedback} pendingAction="delete"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just for testing, I will either remove this from this PR or properly connect it to the Onyx data
src/styles/styles.js
Outdated
}, | ||
error: { | ||
flexDirection: 'row', | ||
justifyContent: 'center', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB: I think this is unnecessary: justifyContent: 'center'
, but also should not cause bugs
src/styles/styles.js
Outdated
borderColor: themeColors.textError, | ||
borderRadius: 6, | ||
borderWidth: 2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least in web, these border properties seem to be unnecessary (except for borderRadius )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copied them from somewhere else... will do some tests in native and see if they make a difference there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing with the error dot - I think we want to wrap it in a view that has 2px padding. This way the total bound of the red dot is 16x16, which will match other small icons we use for this kind of icon + message pattern. So essentially we'd get something like this:
Let me know what you think about that. I know this isn't an "icon" per se, but all of our icons have a little bit of this white space around it.
Actually, that being said, what we might want to do is just supply this as an .svg and then serve it up just like the rest of the icon system? Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am unsure. I would think is better to do this than using svgs if the effect is the same?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this would then become almost like an "exception" to the icon system, ya know?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok, consistency then. I am fine with making it SVG, but have no idea how to do that, any tips?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got you! dot-indicator.svg.zip
So then just make sure this shows at 16x16, and uses red (danger) for the fill color.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah thanks! So I drop this in /assets/images
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe so, you should see a bunch of other .svgs there
style: [], | ||
}; | ||
|
||
function applyStrikeThrough(children) { |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
I saw you we're struggling to test Android, so I tried to build it myself, but it looks like this PR is missing the indicator.svg. When it's out of WIP I'll give it another try |
Thanks @Julesssss! Yes, I forgot to push that out, it's pushed now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Would be good to have some instructions on how to test this now that you removed the hardcoded use case from WorkspaceMembersPage
error: PropTypes.string, | ||
|
||
/** A function to run when the X button next to the error is clicked */ | ||
onClose: PropTypes.func.isRequired, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens when I don't want anything to happen when I click the close button, do I need to strictly pass a method that does nothing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would think that you always want to do something with the X button. It would be weird to have an X button that does nothing :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
heh yes, I think you will always have to pass one, though now I realize that maybe we should have this component remove the error box by default and in that case maybe there are actions that do not require an onClose.... though not sure, I will leave this as required, we can remove the required later when we find a case that does not need it.
Updated to support multiple errors. Please re-review.
Enclose this with: Then modify that pendingAction manually to |
Oh good catch, we have them left aligned in the doc, so will switch it. |
Yup, I agree with that |
const needsStrikeThrough = props.network.isOffline && props.pendingAction === 'delete'; | ||
const hideChildren = !props.network.isOffline && props.pendingAction === 'delete' && !props.errors; | ||
let children = props.children; | ||
const sortedErrors = _.map(_.sortBy(_.keys(props.errors)), key => props.errors[key]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const sortedErrors = _.map(_.sortBy(_.keys(props.errors)), key => props.errors[key]); | |
const sortedErrors = _.chain(props.errors) | |
.keys() | |
.sortBy() | |
.map(key => props.errors[key]) | |
.value(); |
This may be more readable
Updated |
{props.errors && ( | ||
<View style={styles.offlineFeedback.error}> | ||
<View style={styles.offlineFeedback.errorDot}> | ||
<Icon src={Expensicons.DotIndicator} fill={colors.red} height={16} width={16} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB: replace 16 by variables.iconSizeSmall (import variables from '../styles/variables';)
<Icon src={Expensicons.DotIndicator} fill={colors.red} height={16} width={16} /> | ||
</View> | ||
<View style={styles.offlineFeedback.textContainer}> | ||
{_.map(sortedErrors, (error, i) => ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB: because it is hard for me to tell if this has a real impact or not, but it is not recommended by the react doc.
It is not recommended to use indexes (i
) as a key
, from react doc:
We don’t recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state. Check out Robin Pokorny’s article for an in-depth explanation on the negative impacts of using an index as a key. If you choose not to assign an explicit key to list items then React will default to using indexes as keys.
Not sure if it will have a real impact here... but an easy option to it would have been:
const sortedErrorKeys = _.chain(props.errors)
.keys()
.sortBy()
// .map(key => props.errors[key]) Don't map this to values!
.value();
then, in your map here you would use the error's key as the key:
{_.map(sortedErrorKeys, (errorKey) => (
<Text key={errorKey} style={styles.offlineFeedback.text}>{props.errors[errorKey]}</Text>
))}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don’t recommend using indexes for keys if the order of items may change
The order of items will never change, so I think we are fine here, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm the index for one of the errors may change if one of the errors is deleted
|
||
const OfflineWithFeedback = (props) => { | ||
const isOfflinePendingAction = props.network.isOffline && props.pendingAction; | ||
const isUpdateOrDeleteError = props.errors && (props.pendingAction === 'delete' || props.pendingAction === 'update'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB: I think you are considering props.errors
equal null
when there are no errors. Just wondering if this should also consider {}
as no errors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe this should be left as it is and we should not expect {}
... i don't like having two different things representing the same (no errors)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah good question... I think you are right and I should be using _.isEmpty. Changing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify, when we have fieldErrors (or errorFields don't recall what we settled in), then it's totally possible that this would end up being an empty object (since onyx can't really clear things)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested different cases in Android native and Web and didn't find any error. Left just some Nabs.
The text you can see like Test props: errors: 1 - delete - offline
are just me adding a <Text>
describing the props being passed to the case just below it.
In case it is of any use, I did the following to test random cases:
const errorsSeed = [
null,
{
two: 'two error',
three: 'three error and this one is medium length',
one: 'one error that is long one error that is long one error that is long one error that is long one error that is long ',
},
{
three: 'three error and this one is medium length',
},
];
const offlineSeed = [true, false];
const pendingActionSeed = ['add', 'update', 'delete'];
const errors = _.sample(errorsSeed);
const offline = _.sample(offlineSeed);
const pendingAction = _.sample(pendingActionSeed);
const caseDescription = `Test props: errors: ${errors !== null ? Object.keys(errors).length : null} - ${pendingAction} - ${offline ? 'offline' : 'online'}`;
return (
<View>
<Text>{caseDescription}</Text>
<OfflineWithFeedback
pendingAction={pendingAction}
isOffline={offline}
errors={errors}
>
<Hoverable onHoverIn={() =>....
... rest of the code from WorkspaceMembersPage
Damn you are smart! I did it manually LIKE A CAVEMAN! |
Updated it again BTW. |
Going to merge this to unblock other issues, if you have more comments, I will address them in a separate PR. |
@iwiznia looks like this was merged without passing tests. Please add a note explaining why this was done and remove the |
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
🚀 Deployed to production by @yuwenmemon in version: 1.1.86-5 🚀
|
Details
This PR is just introducing the new component. I tested it in the WorkspaceMembersPage by hardocding the error and pendingAction properties and then removed it before submitting this PR for final review.
Fixed Issues
$ https://github.com/Expensify/Expensify/issues/218716
Tests
PR Review Checklist
Contributor (PR Author) Checklist
### Fixed Issues
section aboveTests
sectionQA steps
sectiontoggleReport
and notonIconClick
)src/languages/*
filesSTYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)/** comment above it */
displayName
propertythis
properly so there are no scoping issues (i.e. foronClick={this.submit}
the methodthis.submit
should be bound tothis
in the constructor)this
are necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);
ifthis.submit
is never passed to a component event handler likeonClick
)StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)PR Reviewer Checklist
The Contributor+ will copy/paste it into a new comment and complete it after the author checklist is completed
### Fixed Issues
section aboveTests
sectionQA steps
sectiontoggleReport
and notonIconClick
).src/languages/*
filesSTYLE.md
) were followedAvatar
, I verified the components usingAvatar
have been tested & I retested again)/** comment above it */
displayName
propertythis
properly so there are no scoping issues (i.e. foronClick={this.submit}
the methodthis.submit
should be bound tothis
in the constructor)this
are necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);
ifthis.submit
is never passed to a component event handler likeonClick
)StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)QA Steps
Screenshots
Web
Mobile Web
Desktop
iOS
Android