-
Notifications
You must be signed in to change notification settings - Fork 2.9k
TextField: Convert to JS Styling #5639
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
Conversation
| export interface ITextFieldSubComponentStyles { | ||
| // TODO: this should be the interface once we're on TS 2.9.2 but otherwise causes errors in 2.8.4 | ||
| // label: IStyleFunctionOrObject<ILabelStyleProps, ILabelStyles>; | ||
| label: IStyleFunctionOrObject<any, any>; |
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've confirmed this isn't an issue with TS 2.9.2. I tried to find another way to avoid this, particularly since it's public, but could not without forcing a bunch of cascading anys and casts all throughout TextField. If anybody sees an easier solution until we can use a newer version of TS, let me 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.
| onPaste={this._onPaste} | ||
| value={this.state.displayValue} | ||
| ref={this._resolveRef('_textField')} | ||
| componentRef={this._resolveRef('_textField')} |
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.
can we use React.createRef here?
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 _resolveRef is deprecated, we could replace its usage throughout Fabric, but I'd rather do that in a separate PR.
oengusmacinog-zz
left a comment
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! Approving but there is one comment.
| <p | ||
| className={css('ms-TextField-errorMessage', AnimationClassNames.slideDownIn20, styles.errorMessage)} | ||
| > | ||
| <span className={styles.errorText} data-automation-id="error-message"> |
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.
Does this span no longer need a style set of its own?
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 if you're saying it can be removed or if you think it was removed. It's still in ITextFieldStyles with default global class name in TextField.styles.ts and applied on line 229 below.
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 asking if it should be missing or not. I am talking about the span tag style styles.errorText, not the p tag style styles.errorMessage which is reapplied on 229
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.
Actually, I'm not seeing it in the original scss do I must be missing something
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.
Ahh I see. Yeah, I didn't see errorText being defined anywhere. The fact that there wasn't an error here is a problem of using the old any 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.
Oh is that why there is no error?! Good to know I was racking my brain trying to figure out how that slipped by. In that case, must just be left over from some point when it did have styles.
| expect(field.state.errorMessage).toBeTruthy(); | ||
| // TODO: this test is not working as intended as even nonsense state names pass. | ||
| // commented out for now since test isn't executing when named 'xit' | ||
| // const field = ReactTestUtils.findRenderedComponentWithType(renderedForm, TextField); |
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.
ive seen these tests in form tests randomly fail. We will need to evaluate what the right pattern is here once we gain traction.
| /** Is true when the control has focus. */ | ||
| isFocused?: boolean; | ||
| isFocused: boolean; | ||
|
|
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 { isFocused } = this.state; | ||
| const errorMessage = this._errorMessage; | ||
|
|
||
| this._classNames = getClassNames(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.
! [](start = 43, length = 1)
You don't need the ! here for styles
| const errorMessage = this._errorMessage; | ||
|
|
||
| this._classNames = getClassNames(styles!, { | ||
| theme: theme!, |
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.
! [](start = 18, length = 1)
Totally understand your usage here but FWIW, I took a different approach to this in Dropdown to minimize use of !
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.
@cliffkoh Maybe you could just describe the approach right here :)
I will look in the other PR; i'm very curious how you could avoid this.
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 think @cliffkoh placed the theme prop as an "optional" in the styleprops definition so it pushes that issue down to styles.ts, but there he added a throw if not set. It's a bit harsh to have an optional that's gets thrown if not set :(
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 in the middle of (hopefully) resolving this issue in createComponent...
|
|
||
| if (label) { | ||
| return ( | ||
| <Label required={required} htmlFor={this._id} styles={labelStyles}> |
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.
required={required} [](start = 15, length = 19)
👍
| )} | ||
| {multiline ? this._renderTextArea() : this._renderInput()} | ||
| {(iconClass || iconProps) && <Icon className={css(iconClass, styles.icon)} {...iconProps} />} | ||
| {(iconClass || iconProps) && <Icon className={css(iconClass, this._classNames.icon)} {...iconProps} />} |
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.
iconClass [](start = 62, length = 9)
Should iconClass here be part of the baseStyles? (this was the approach I took in Dropdown)
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 a total miss on my part. I never intended for css function to still be used here. Thanks
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 the Icon styles function is already exposed through iconProps I don't think there needs to be another entry for it in subComponentStyles.
| className={css('ms-TextField-errorMessage', AnimationClassNames.slideDownIn20, styles.errorMessage)} | ||
| > | ||
| <span className={styles.errorText} data-automation-id="error-message"> | ||
| {errorMessage} |
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.
👍
| // TODO: this should be the interface once we're on TS 2.9.2 but otherwise causes errors in 2.8.4 | ||
| // label: IStyleFunctionOrObject<ILabelStyleProps, ILabelStyles>; | ||
| label: IStyleFunctionOrObject<any, any>; | ||
| } |
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 it's actually interesting you made this a dedicated interface. This way the autodoc picks it up - but somewhat ironically the label itself is missing JSDoc :)
| public render(): JSX.Element { | ||
| return ( | ||
| <div className="docs-TextFieldExample"> | ||
| <TextField label="Theme Primary Label, Red Top Border (JS):" required={true} styles={getStyles} /> |
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.
Love this new example but it would be great to have some exposition providing context for what the example is illustrating (I'm not certain this is necessarily obvious)
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.
While I agree that we should have more on styling, I don't know if this is the right place. We should really have a styling page. I've long envisioned a page where every component is JS styled with multiple themes and customizers on one page that would also serve as a great snapshot test.
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 actually just suggesting a 1-liner exposition - not a detailed documentation. Completely agree with you this is not the right place.
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.
Yes just an explanation paragraph would be useful.
Many of the examples we provide read like undocumented test cases. It's one of the common complaints we hear. Walk the user through what you're illustrating. We can leverage your example here to repeat it elsewhere.
| beforeAll(() => { | ||
| // Prevent warn deprecations from failing test | ||
| jest.spyOn(WarnUtil, 'warnDeprecations').mockImplementation(() => { | ||
| /** no impl **/ |
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 there's a test using deprecated props, shouldn't it be in a .deprecated.test file?
| it('should associate the label and input box', () => { | ||
| const renderedDOM: HTMLElement = renderIntoDocument(<TextField label="text-field-label" value="whatever value" />); | ||
| const renderedDOM: HTMLElement = renderIntoDocument( | ||
| <TextFieldBase label="text-field-label" value="whatever value" /> |
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.
TextFieldBase [](start = 7, length = 13)
Out of curiosity, what determines why some tests use TextFieldBase and others use TextField?
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.
Mostly because HOC styled wrapper breaks a lot of these fragile tests. I'll try and get back to making this more consistent.
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 it is with enzyme, you can use dive() to get pass the HOC..
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, they're mostly using ReactTestUtils.
| hasErrorMessage | ||
| } = props; | ||
|
|
||
| const { semanticColors, palette } = theme; |
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.
👍
| color: semanticColors.inputPlaceholderText, | ||
| opacity: 1 | ||
| }, | ||
| ':-ms-input-placeholder': { |
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.
':-ms-input-placeholder [](start = 10, length = 23)
I would actually have expected ::placeholder to include all browser variants (this and others like ::-webkit-input-placeholder) automatically hmm, and if so, this might be redundant..
cliffkoh
left a comment
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.
👍 Typical high quality work.
| } | ||
|
|
||
| export type ITextFieldStyleProps = Required<Pick<ITextFieldProps, 'theme'>> & | ||
| Pick< |
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.
wonder how these props show up in documentation.
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.
My guess is that it's ignored.
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.
Yeah, they don't show up, good catch. Do you think the parser can be modified to pick these up?
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 fully expected it to not show up but I would rather optimize for doing the right thing here and improve the parser as a systemic fix. But at the same time, I don't want to unnecessarily invest in the parser atm given API extractor and TSDoc especially given all the other burning problems we have :)
| import * as React from 'react'; | ||
| import { ITextFieldStyleProps, ITextFieldStyles, TextField } from 'office-ui-fabric-react/lib/TextField'; | ||
| import { ILabelStyles, ILabelStyleProps } from 'office-ui-fabric-react/lib/Label'; | ||
| import './TextField.Examples.scss'; |
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.
do we need these sass files? If you add a new example, ideally we wouldn't depend on sass. (If you do need them, then it's ok to not change this if it means less changes.)
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 new example explicitly has JS and CSS styling, so this is needed for the CSS styling example.
| className={classNames.input} | ||
| value={color.hex} | ||
| ref={ref => (this.hexText = ref!)} | ||
| componentRef={this._hexText} |
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.
yay thank you!
|
|
||
| private _onHexChanged = (): void => { | ||
| this._updateColor(getColorFromString('#' + this.hexText.value)); | ||
| this._updateColor(getColorFromString('#' + this._hexText.value)); |
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 value is deprecated. Use current.
(We REALLY need to enable the deprecations tslint rule.)
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 all pre-existing code. The only reason this changed at all is because TextField is now in an HOC wrapper. I'd rather not make ref-related or deprecation changes to this PR as it's already pretty wide in scope.
dzearing
left a comment
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 minor comments.
Pull request checklist
$ npm run changeDescription of changes
Convert TextField to JS styling. Also takes advantage of @cliffkoh's awesome PR #5513 to allow for passing in Label styling as part of TextField's
stylesprop. A new exampleTextField.Styled.Example.tsxwas added to demonstrate this feature (and ensure it continues to work.)Focus areas to test
Make sure all class names and styling are represented by output, as well as user styling.
Microsoft Reviewers: Open in CodeFlow