Skip to content

Conversation

@JasonGore
Copy link
Member

@JasonGore JasonGore commented Jul 19, 2018

Pull request checklist

Description 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 styles prop. A new example TextField.Styled.Example.tsx was 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

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>;
Copy link
Member Author

@JasonGore JasonGore Jul 19, 2018

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.

Copy link
Contributor

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')}
Copy link
Member

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?

Copy link
Member Author

@JasonGore JasonGore Jul 23, 2018

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.

Copy link
Collaborator

@oengusmacinog-zz oengusmacinog-zz left a 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">
Copy link
Collaborator

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?

Copy link
Member Author

@JasonGore JasonGore Jul 23, 2018

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.

Copy link
Collaborator

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

Copy link
Collaborator

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

Copy link
Member Author

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. :/

Copy link
Collaborator

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);
Copy link
Member

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;

Copy link
Contributor

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!, {
Copy link
Contributor

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!,
Copy link
Contributor

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 !

Copy link
Member

@dzearing dzearing Jul 25, 2018

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.

Copy link
Contributor

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 :(

Copy link
Member Author

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}>
Copy link
Contributor

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} />}
Copy link
Contributor

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)

Copy link
Member Author

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

Copy link
Member Author

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}
Copy link
Contributor

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>;
}
Copy link
Contributor

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} />
Copy link
Contributor

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)

Copy link
Member Author

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.

Copy link
Contributor

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.

Copy link
Member

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 **/
Copy link
Contributor

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" />
Copy link
Contributor

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?

Copy link
Member Author

@JasonGore JasonGore Jul 25, 2018

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.

Copy link
Contributor

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..

Copy link
Member Author

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;
Copy link
Contributor

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': {
Copy link
Contributor

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..

Copy link
Contributor

@cliffkoh cliffkoh left a 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<
Copy link
Member

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.

Copy link
Member

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.

Copy link
Member Author

@JasonGore JasonGore Jul 25, 2018

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?

Copy link
Contributor

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';
Copy link
Member

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.)

Copy link
Member Author

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}
Copy link
Member

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));
Copy link
Member

@dzearing dzearing Jul 25, 2018

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.)

Copy link
Member Author

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.

Copy link
Member

@dzearing dzearing left a comment

Choose a reason for hiding this comment

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

Just minor comments.

@JasonGore JasonGore merged commit d056d71 into microsoft:master Jul 25, 2018
@microsoft microsoft locked as resolved and limited conversation to collaborators Aug 30, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TextField: Convert to css-in-js

6 participants