-
Notifications
You must be signed in to change notification settings - Fork 14
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
[FX-4599] Migrate OutlineInput #4363
[FX-4599] Migrate OutlineInput #4363
Conversation
🦋 Changeset detectedLatest commit: 5f0ced2 The changes in this PR will be included in the next version bump. This PR includes changesets to release 16 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
85ffac2
to
9a770cc
Compare
0d7c2f6
to
d49ccd6
Compare
@toptal-anvil ping reviewers |
1 similar comment
@toptal-anvil ping reviewers |
@@ -28,25 +28,22 @@ | |||
"@toptal/picasso-input-adornment": "1.0.7", | |||
"@toptal/picasso-shared": "15.0.0", | |||
"@toptal/picasso-utils": "1.0.3", | |||
"@toptal/picasso-tailwind-merge": "1.0.1", |
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.
let's move @toptal/picasso-tailwind-merge
to peer dependencies and dev dependencies
disabled && 'text-gray-600 [&::-webkit-text-fill-color]:text-gray-600', | ||
// On Safari the text gets a bit lighter as if it had some transparency applied to it | ||
// We need this webkit-specific property to achieve the exact font color | ||
disabled && '[&::-webkit-text-fill-color]:text-gray-600', | ||
// It's a special case for TimePicker | ||
type === 'time' && inputProps?.disabled && 'text-black', | ||
isDark && | ||
'text-white [&::placeholder]:text-white [&::placeholder]:opacity-[0.64]', |
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.
So if we have disabled dark input, it should still have white text 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.
No, because styles for disabled
goes after this line, so twMerge
will remove text-white
.
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.
Sorry. My previous answer was incorrect. Correct answer is - yes. And It is so in master branch.
) | ||
|
||
// eslint-disable-next-line complexity | ||
export const getInputClassName = ({ |
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'm not sure I like these big functions that deal with everything 🤔 Imagine you would have to test this function, it would be very painful. I prefer smaller functions with a single responsibility and with few inputs
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.
Since we already have visual tests I was thinking about this function as something we will need to test. Its purpose here is just to decrease the complexity of the component. Sure, we can split it into smaller functions, but will it give us any benefit, considering that the effect of this function is already covered with more high-level tests?
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.
The main reason why I think it is better to have one function is that all classes are in the same place. It is easier to work with them, and find issues, like conflicting classes that override each other and mess up styling.
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.
Visual tests are second-class tests, IMO. Unit tests and integration tests are much more important from a development perspective; visual tests are only there to test the aesthetic parts, not behavior.
Complexity is not a problem only about testability, but it is also a problem with readability. We need to think of how other developers will read this code in future, walking through all of these conditions, sometimes you (the original writer) will be the one to re-unveil its secrets.
I agree with Tomas, just moving the complexity from a function to another doesn't solve the problem, it might make the main component function aesthetically "pleasing" but we still need to walk through the complexity if we want to understand how the styling works, now with some extra 20 lines for the function definition. I believe this function is clear, but perhaps we should remove the extra lines for restructuring and leave it as props: Props
, and just change the references to props.prop
. Can you also add a unit test for it? Tests are also good documentation complements, as they make clear what are our intents in an English text, having it as a separate function is a great step on making the code testable
I believe that adding tests here to be a good opportunity to improve on this component, which will become one of the most complex we have
Tests should be easy and fun, you can just import it and check for all conditionals, example:
import { getInputClassName } from './utils'
describe('getInputClassName', () => {
describe('when disabled', () => {
it('styles webkit fill color', () => {
const result = getInputClassName({ disabled })
expect(result).toContain('[&::-webkit-text-fill-color]:text-gray-600')
})
// ...
})
})
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 see now the benefit of testing these functions. But I'm not sure about props.prop
. I see, that we don't use this approach in our components and util functions. It happens from time to time, mostly in tests. So I prefer to leave destructuring. Are you ok with that @augustobmoura, @TomasSlama?
width === 'shrink' && 'w-auto', | ||
width === 'auto' && 'w-[18.75rem]', |
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.
even though this looks weird, it is correct, I double checked with master ;-)
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.
Checked the code, but we still have a few differences on Happo that need to be addressed. I believe adding unit tests here will also be a huge improvement for the future, this is a major component on our stack and having it well tested is very important
return multiline ? ( | ||
<Input | ||
{...sharedProps} | ||
multiline={true} | ||
rows={getRows(rows)} | ||
maxRows={getRows(rowsMax)} | ||
/> | ||
) : ( | ||
<Input {...sharedProps} /> |
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.
If the component is the same and we only change multiline
, rows
, maxRows
, perhaps we should only have them conditionally added, while also keeping the props inlined with the element
return multiline ? ( | |
<Input | |
{...sharedProps} | |
multiline={true} | |
rows={getRows(rows)} | |
maxRows={getRows(rowsMax)} | |
/> | |
) : ( | |
<Input {...sharedProps} /> | |
return ( | |
<Input | |
className='...' | |
style={{}} | |
// ... | |
multiline={multiline ? true : undefined} | |
rows={multiline ? getRows(rows) : undefined} | |
maxRows={multiline ? getRows(rowsMax) : undefined} | |
/> | |
) |
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.
It will not work like this due to Input
props type. It is a union of two different types. And for the one where multiline
is true
, rows
and maxRows
should not be undefined
. And due to that it should be splited to two different returns. Otherwise, TS will throw an error.
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.
Got it. We still can have only the multiline props on an object while the remaining is inlined without the ternary expression, wdyt?
Having most of props on a object instead of inlined on the JSX element is a bit unstylistic and we lose
return multiline ? ( | |
<Input | |
{...sharedProps} | |
multiline={true} | |
rows={getRows(rows)} | |
maxRows={getRows(rowsMax)} | |
/> | |
) : ( | |
<Input {...sharedProps} /> | |
const multilineProps = multiline | |
? ({ | |
multiline: true, | |
rows: getRows(rows), | |
maxRows: getRows(rowsMax), | |
} as const) | |
: {} | |
return ( | |
<Input | |
className='...' | |
style={{}} | |
// ... | |
{...multilineProps} | |
/> |
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 nice 🚀
) | ||
|
||
// eslint-disable-next-line complexity | ||
export const getInputClassName = ({ |
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.
Visual tests are second-class tests, IMO. Unit tests and integration tests are much more important from a development perspective; visual tests are only there to test the aesthetic parts, not behavior.
Complexity is not a problem only about testability, but it is also a problem with readability. We need to think of how other developers will read this code in future, walking through all of these conditions, sometimes you (the original writer) will be the one to re-unveil its secrets.
I agree with Tomas, just moving the complexity from a function to another doesn't solve the problem, it might make the main component function aesthetically "pleasing" but we still need to walk through the complexity if we want to understand how the styling works, now with some extra 20 lines for the function definition. I believe this function is clear, but perhaps we should remove the extra lines for restructuring and leave it as props: Props
, and just change the references to props.prop
. Can you also add a unit test for it? Tests are also good documentation complements, as they make clear what are our intents in an English text, having it as a separate function is a great step on making the code testable
I believe that adding tests here to be a good opportunity to improve on this component, which will become one of the most complex we have
Tests should be easy and fun, you can just import it and check for all conditionals, example:
import { getInputClassName } from './utils'
describe('getInputClassName', () => {
describe('when disabled', () => {
it('styles webkit fill color', () => {
const result = getInputClassName({ disabled })
expect(result).toContain('[&::-webkit-text-fill-color]:text-gray-600')
})
// ...
})
})
@augustobmoura there is a note about those failing tests in the PR description |
0998017
to
d8020fb
Compare
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.
Left a small comment at #4363 (comment), please resolve it before merging.
GJ on the PR! 👍
|
||
import styles from './styles' | ||
import { getInputClassName, getRootClassName, getRows } from './styles/styles' |
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.
just for consistency, we don't have styles folder in any component, we can just have OutlinedInput/styles.ts
className: getRootClassName({ | ||
size, | ||
width, | ||
type, | ||
layout, | ||
isDark, | ||
multiline, | ||
highlight, | ||
disabled, | ||
className, | ||
classes, | ||
isError, | ||
}), |
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.
className: getRootClassName({ | |
size, | |
width, | |
type, | |
layout, | |
isDark, | |
multiline, | |
highlight, | |
disabled, | |
className, | |
classes, | |
isError, | |
}), | |
className: twMerge( | |
getRootClassName({ | |
size, | |
width, | |
type, | |
layout, | |
isDark, | |
multiline, | |
highlight, | |
disabled, | |
isError, | |
}), | |
className, | |
classes.root | |
), |
'text-gray-600', | ||
// On Safari the text gets a bit lighter as if it had some transparency applied to it | ||
// We need this webkit-specific property to achieve the exact font color | ||
disabled ? '[&::-webkit-text-fill-color]:text-gray-600' : '', |
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 condition will be always true as it is already in if (disabled)
// On Safari the text gets a bit lighter as if it had some transparency applied to it | ||
// We need this webkit-specific property to achieve the exact font color | ||
disabled ? '[&::-webkit-text-fill-color]:text-gray-600' : '', | ||
].join(' ') |
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.
].join(' ') | |
] |
twJoin and twMerge accepts arrays, so we don't need to join these. It can even work with nested arrays
d68ae19
to
9d272d9
Compare
174b904
to
86bf502
Compare
86bf502
to
08b0403
Compare
After refactoring visual tests provides the expected result. What is left is to add tests for style utils. I have a concern about And in this case, it also can make sense to have them in a separate folder, where we also can have tests for them. |
FX-4599
Description
Migrate OutlinedInput to TailwindCSS.
How to test
Note
Visual tests for Select with a focused state after click are failing. There is no box shadow and correct border. But in browser, they are. The issue occurs only in Cypress. I and @TomasSlama could not figure out for now the reason. Since it works in browser, we agreed, that we can merge it now and fix it in a follow-up ticket.
Development checks
props
in component with documentationexamples
for componentBreaking change
codemod is created and showcased in the changesettest alpha package of Picasso in StaffPortalPR commands
List of available commands:
@toptal-bot run package:alpha-release
- Release alpha version@toptal-anvil ping reviewers
- Ping FX team for reviewPR Review Guidelines
When to approve? ✅
You are OK with merging this PR and
nit:
to your comment. (ex.nit: I'd rename this variable from makeCircle to getCircle
)When to request changes? ❌
You are not OK with merging this PR because
When to comment (neither ✅ nor ❌)
You want your comments to be addressed before merging this PR in cases like:
How to handle the comments?