Skip to content
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

Refactor switches, simplify code, fix related bugs. #5780

Merged
merged 8 commits into from
Dec 15, 2016

Conversation

nathanmarks
Copy link
Member

@nathanmarks nathanmarks commented Dec 13, 2016

[wip] Still a couple of things to solidify 100% (and get the keyboard focus styles working for the switches)

  • Refactors SwitchBase into a factory
  • Refactors SwitchLabel into HoC which acts as a wrapper
  • Refactors Checkbox, Radio, Switch to use new Factory
  • Drastically simplify RadioGroup, now it just provides the onChange/selectedValue helpers.
  • Fixes keyboard functionality for groups of radios and you no longer need the RadioGroup for proper keyboard functionality as the native elements drive it using the name="".
  • Removes the need to add a lot of manual markup for AT (aria attrs etc)
  • REMOVE CODE :)

The changes here allow us to simplify the code significantly with regards to everything from accessibility, complexity related to implementing native-like keyboard functionality, duplicated code, and more.

@oliviertassinari @mbrookes a few questions:

  1. Do you think the label wrapped switch should be the default export like I have it in this PR?
  2. In order to accomodate it being the default export, I had to make some styles conditional on an actual label being provided. If the label version stays as the default export, do you think this should stay or should none-labelled instances be using the named export instead?

cc @rosskevin:

I've removed labelReverse here. I can't find a single common use case in an RTL language to justify it being part of core. It's just single flex property so think it should be done in userland. WDYT?

  • PR has tests / docs demo, and is linted.
  • Commit and PR titles begin with [ComponentName], and are in imperative form: "[Component] Fix leaky abstraction".
  • Description explains the issue / use-case resolved, and auto-closes the related issue(s) (http://tr.im/vFqem).

@muibot muibot added the PR: needs revision The pull request can't be merged. More details is available on the code review or fails in the CI label Dec 13, 2016
@rosskevin
Copy link
Member

There is a labelReverse demo in Selection controls, specifically Switch. In a case where you wanted a switch with a label in a list, you would simply need to style the separation. I do think it is removable since this use case needs styling externally anyway.

@nathanmarks
Copy link
Member Author

@rosskevin Sounds good to me--also, I don't see the common use case for it outside of the list menus anyways, and for the material list menus, we do have a component that handles the list and placement already.

@nathanmarks nathanmarks force-pushed the fix-radiogroup-keybd-selection branch from 9f9f56a to a31f23d Compare December 13, 2016 23:48
@rosskevin
Copy link
Member

I like the code reduction.

From regressions, it looks like components were column layout, now they are row layout. I've been meaning to PR, but this is a good time to discuss and get it in. I'd like consistency of FormGroup and RadioGroup with behavior of the row property. I suggest default column layout and boolean row will inline controls.

Does this sound like a consistent/good addition to RadioGroup?

@nathanmarks
Copy link
Member Author

nathanmarks commented Dec 14, 2016

@rosskevin I think RadioGroup has been misunderstood, the reason for RadioGroup initially is because with radios it's handy to have an interface similar to a <select> (they are fairly interchangeable in practice). It was never intended as a layout tool and it doesn't have opinions on layout.

I didn't change anything that was explicitly setting a column layout. What I did do was change the display from flex to inline-flex so that the layout was consistent with the switches that didn't have a label. This was only happening because of the inconsistent display property between the two:

image

I suppose my question is, why should column be the default for selection controls? And if we are doing that, we should enforce it via flex-direction of the parent like FormGroup does.

Also, at the moment, FormGroup seems like a generic layout helper rather than something form specific. Do you think that there's anything specific to forms that it will need in the near future? Otherwise it's probably a sign that we should start thinking about a more all-purpose flex layout helper. I know there was a PR for one recently, I haven't had a chance to thoroughly check it out though.

@mbrookes
Copy link
Member

@nathanmarks I'm leaning towards labelled as the default export - my gut feeling is that unlabelled switches, such as the checkboxes in Table are less common. Using the named export for non-labeled instances seems logical, rather than conditional styles, but I'll admit I don't fully understand the implications of that change.

@nathanmarks
Copy link
Member Author

nathanmarks commented Dec 14, 2016

@mbrookes the labelled version actually works in tables, lists etc 😉 Just a tiny overhead by using it.

@rosskevin
Copy link
Member

FormGroup is intended to be similar to RadioGroup. It is currently stubbed as and provides column vs row layout. I was planning to enhance it similar to RadioGroup specifically for integration with a FormLabel. Checkboxes often need a label (with required styling behavior, which requires knowledge of focus).

FormGroup and RadioGroup can be agnostic of layout; I would like them to behave the same out of the box. The only argument of being layout agnostic is sacrificing convenience. Row vs column layout of selection controls is incredibly common, and I am one who would be wrapping them so I could get this behavior in a simple fashion.

@nathanmarks
Copy link
Member Author

nathanmarks commented Dec 14, 2016

@rosskevin I wasn't saying getting rid of the helper, I was just wondering about whether it had to be specific to forms or not. It looks super generic and could easily be made a HoC to give flex children styling to any component/element.

RadioGroup shouldn't have something that is "similar" because the primary goal of RadioGroup is to provide API helpers that aren't required for checkboxes or toggle switches. Layout is a separate concern (so FormGroup, etc)

@rosskevin
Copy link
Member

@nathanmarks - it is generic now, not generic later. Make sense?

@nathanmarks
Copy link
Member Author

@rosskevin 100%, what did you have in mind for it? As in--what other layout problems for the forms is it going to help fix? Just curious/interested

@rosskevin
Copy link
Member

rosskevin commented Dec 14, 2016

Did you miss my comment?

FormGroup is intended to be similar to RadioGroup...I was planning to enhance it similar to RadioGroup specifically for integration with a FormLabel. Checkboxes often need a label (with required styling behavior, which requires knowledge of focus).

I have a wrapper around RadioGroup locally that integrates FormLabel. Would like to get this into core and do the same for FormGroup. Sorry if I confused you.

@nathanmarks
Copy link
Member Author

@rosskevin Ahhh sorry. Missed the bit about the integration RE FormLabel.

I think we need more flexibility here to keep concerns separated. What if FormGroup had a named export too, createFormGroup that you could use to wrap other components if needed? (Such as RadioGroup).

@rosskevin
Copy link
Member

Agree with that. FormGroup should be able to wrap FormLabel + RadioGroup and achieve the required spec behaviors for labels.

@nathanmarks
Copy link
Member Author

Nice, that way RadioGroup can stay focused in purpose and the code behind FormGroup can handle that concern easily for all the form groups in one place.

@rosskevin
Copy link
Member

I had FormLabel focus behavior was in RadioGroup and I had it in the demo, but it was removed in the last change to RadioGroup by @oliviertassinari (prior to this pr). I see in the demo that the FormLabel is also now outside the RadioGroup.

Now label behavior is broken as I had coded it. The following label should be accented in this scenario (I clicked on the button):

material_ui

@nathanmarks your idea would solve this in a more elegant way (and leave @oliviertassinari's change as-is)

@nathanmarks
Copy link
Member Author

nathanmarks commented Dec 14, 2016

@rosskevin The problem we had is that with the current RadioGroup and the JS based focus management, having the label inside completely broke the ability to tab into the widget and navigate through the radios. That's why it got removed from the demo.

It should be super super easy to solve once this PR is ready. I'll try the idea I outlined above in a follow up PR.

@nathanmarks nathanmarks force-pushed the fix-radiogroup-keybd-selection branch 2 times, most recently from e6f45d9 to 6c0c097 Compare December 14, 2016 01:07
- Refactors SwitchBase into a factory
- Refactors Checkbox, Radio, Switch to use new Factory
- Drastically simplify RadioGroup
@nathanmarks nathanmarks force-pushed the fix-radiogroup-keybd-selection branch from 6c0c097 to 58b47fd Compare December 14, 2016 17:17
@nathanmarks nathanmarks changed the title [wip] Refactor switches, simplify code, fix related bugs. Refactor switches, simplify code, fix related bugs. Dec 14, 2016
@nathanmarks nathanmarks removed PR: needs revision The pull request can't be merged. More details is available on the code review or fails in the CI in progress labels Dec 14, 2016
@nathanmarks nathanmarks force-pushed the fix-radiogroup-keybd-selection branch from 58b47fd to bab90d4 Compare December 14, 2016 17:19
@nathanmarks
Copy link
Member Author

@oliviertassinari let me know what you think

@oliviertassinari
Copy link
Member

oliviertassinari commented Dec 14, 2016

  1. Do you think the label wrapped switch should be the default export like I have it in this PR?
  2. In order to accommodate it being the default export, I had to make some styles conditional on an actual label being provided. If the label version stays as the default export, do you think this should stay or should none-labelled instances be using the named export instead?

The export could be confusing right now. Doing a default import is gonna have the label property while doing a name import isn't. I like the idea of exporting by default the lower level component Checkbox and proposing a named export like CheckboxLabel with the withSwitchLabel() HoC.

Otherwise it's probably a sign that we should start thinking about a more all-purpose flex layout helper. I know there was a PR for one recently, I haven't had a chance to thoroughly check it out
though.

I had a close look at them. For what it worth, at @doctolib, we have been developing and using a simple homemade solution based on the #3614 discussion: LayoutBlock (still wip).

Copy link
Member

@oliviertassinari oliviertassinari left a comment

Choose a reason for hiding this comment

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

Cool to see how well the browser is taking care of the accessibility with the native input 👍

@@ -28,6 +28,7 @@ module.exports = {
'no-console': 'error', // airbnb is using warn
'no-param-reassign': 'off',
'no-prototype-builtins': 'off',
'no-use-before-define': ['error', { 'functions': false }], // airbnb have functions: true, annoying
Copy link
Member

Choose a reason for hiding this comment

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

I'm curious about the motivation.

Copy link
Member Author

Choose a reason for hiding this comment

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

@oliviertassinari I wanted to put all the test helpers below the main part of the suite in one case. Functions are hoisted so there isn't any harm and in some scenarios it helps you leave more important stuff at the top. It isn't something that affects our react components because for the most part we have non-hoistables (class, assignment).

Copy link
Member

Choose a reason for hiding this comment

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

Sounds like a fight between important stuff at the top vs load the context before digging into the code. I prefer the second option but some flexibility won't harm 👍 .

Copy link
Member Author

@nathanmarks nathanmarks Dec 15, 2016

Choose a reason for hiding this comment

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

Yeah, I see that side too -- but on the other hand it can be confusing to open a file and it's hard to find the root describe() in a test because there's a bunch of helper functions first. In that case, you don't even know what they are for yet.

@@ -62,6 +62,7 @@ export const styleSheet = createStyleSheet('IconButton', (theme) => {
export default function IconButton(props, context) {
const {
accent,
buttonRef,
Copy link
Member

Choose a reason for hiding this comment

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

👍

@@ -1,34 +1,8 @@
// @flow weak
// @flow weak
Copy link
Member

Choose a reason for hiding this comment

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

Space

@@ -121,6 +103,8 @@ export default class ButtonBase extends Component {
button = null;
keyboardFocusTimeout = undefined;

focus = () => this.button.focus();
Copy link
Member

Choose a reason for hiding this comment

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

I think that we could have issue with this.button being null when unmounting the tree. Not sure through. Will see in the long run.

Copy link
Member Author

Choose a reason for hiding this comment

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

@oliviertassinari we could even remove this... although it's worth considering if it should be part of the public API so there is a react-based way to programatically focus our buttons. Unless you think that we should just recommend findDOMNOde for now. WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

I don't know if findDOMNOde is going to be removed or not.
I'm not sure how we are going to document that focus imperative method.
I don't have much opinion. I would keep it for now as people might hit the eslint warning.

Copy link
Member Author

Choose a reason for hiding this comment

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

@oliviertassinari I don't think it's being removed -- the question is more about what the right API to expose to our users is.

Copy link
Member

Choose a reason for hiding this comment

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

I think that the imperative API is the best one. It's simpler to use to deprecated, to warn when not used correctly, it abstract the underlying dom implementation.

const switchComponentName = SwitchComponent.displayName || SwitchComponent.name;

return class SwitchLabel extends Component {
static displayName = `withSwitchLabel(${switchComponentName})`;
Copy link
Member

Choose a reason for hiding this comment

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

Could be using the recompose helper to set the display name:
https://github.com/acdlite/recompose/blob/master/src/packages/recompose/createHelper.js

@nathanmarks
Copy link
Member Author

@oliviertassinari I'm going to use LabelRadio, etc, as RadioLabel sounds too much like "label to use with the radio" rather than "radio wrapped with a label"

@nathanmarks nathanmarks force-pushed the fix-radiogroup-keybd-selection branch from 6da2cf8 to cd1978a Compare December 15, 2016 03:15
@nathanmarks
Copy link
Member Author

Just noticed a small focus related issue somewhere else that switches are used (lists). Fixing that.

@nathanmarks nathanmarks added PR: needs revision The pull request can't be merged. More details is available on the code review or fails in the CI and removed PR: Needs Review labels Dec 15, 2016
@nathanmarks nathanmarks removed the PR: needs revision The pull request can't be merged. More details is available on the code review or fails in the CI label Dec 15, 2016
@nathanmarks nathanmarks merged commit 3a155b1 into mui:next Dec 15, 2016
@nathanmarks nathanmarks deleted the fix-radiogroup-keybd-selection branch December 15, 2016 16:37
@nathanmarks
Copy link
Member Author

@rosskevin I ended up leaving FormGroup as is for now and i'm actually using it inside RadioGroup as the root node.

@rosskevin
Copy link
Member

@nathanmarks are you still going to pr a change to FormGroup and docs/site/src/demos/selection-controls/RadioButtonsGroup.js so that the FormLabel gets focus again or do you want me to do that?

@zannager zannager added the component: radio This is the name of the generic UI component, not the React module! label Mar 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: radio This is the name of the generic UI component, not the React module!
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants