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

fix(makeStyles): handle RTL properly #17549

Merged
merged 21 commits into from
Mar 30, 2021
Merged

fix(makeStyles): handle RTL properly #17549

merged 21 commits into from
Mar 30, 2021

Conversation

layershifter
Copy link
Member

@layershifter layershifter commented Mar 24, 2021

Pull request checklist

  • Addresses an existing issue: Fixes
  • Include a change request file using $ yarn change

TL;DR

This PR fixes RTL handling in makeStyles() and makes ax() context aware, so it will also handle it properly.

RTL classnames

We can only use different classes for LTR & RTL if we want to avoid collisions, see CodeSandbox for example. This PR follows this approach, we will emit following CSS:

makeStyles({
  root: { color: "red", paddingLeft: "10px" }
});

(results in 👇)

.fe3e8s90 {
  color: red;
}
.frdkuqy0 {
  padding-left: 10px;
}
.rfrdkuqy0 {
  padding-right: 10px;
}

Previously, makeStyles() always returned LTR classes, this PR fixes it as classes for LTR and RTL are resolved separately (resolvedClasses & resolvedClassesRtl).

ax() changes

The signature of ax() function @fluentui/make-styles have been changed as spreads are complied into additional loops:

// Before
ax(className1, className2);
// After
ax("ltr", [className1, className2]);
Sample TS compiler output
var __spreadArray =
  (this && this.__spreadArray) ||
  function(to, from) {
    for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
      to[j] = from[i];
    return to;
  };
export function useAx() {
  var classes = [];
  for (var _i = 0; _i < arguments.length; _i++) {
    classes[_i] = arguments[_i];
  }
  var dir = useFluent().dir;
  return ax.apply(void 0, __spreadArray([dir], classes));
}

In @fluentui/react-make-styles, but now ax() consumes a React context to be aware of text direction. I will rename it into useAx() in a separate PR, in this PR both ax() and useAx() are exported.

// Before
ax(className1, className2);
// After
useAx(className1, className2);

:global changes

Behavior for :global() pseudo selector have been changed.

Before

makeStyles({
  root: {
    ":global(body)": { color: "red" }, // outputs "body { color: red }"
    // 👇 because of "&" output is different
    ":global(body) &": { color: "red" } // outputs "body .HASH { color: red }"
  }
});

After

Now output will be the same.

makeStyles({
  root: {
    ":global(body)": { color: "red" }, // outputs "body .HASH { color: red }"
    ":global(body) &": { color: "red" } // outputs "body .HASH { color: red }"
  }
});

We should use makeStaticStyles() to emit global styles: makeStyles() emits theme specific styles that can be different for different themes. An example of possible issue before the change:

makeStyles({
  root: theme => ({
    ":global(body)": { color: theme.alias.color.brand } // outputs "body { color: var(--theme-alias-color-brand) }"
  })
});

Testing

PR contains a lot of new unit tests, new stories for @fluentui/react-make-styles and VR tests.

@codesandbox-ci
Copy link

codesandbox-ci bot commented Mar 24, 2021

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 88d58b9:

Sandbox Source
Fluent UI Button Configuration
Scoped RTL demo PR

@size-auditor
Copy link

size-auditor bot commented Mar 24, 2021

Asset size changes

Project Bundle Baseline Size New Size Difference
office-ui-fabric-react fluentui-react-components-Avatar 44.361 kB 45.407 kB ExceedsTolerance     1.046 kB
office-ui-fabric-react fluentui-react-components-Link 32.217 kB 32.693 kB ExceedsBaseline     476 bytes
office-ui-fabric-react fluentui-react-components-Button 37.316 kB 37.79 kB ExceedsBaseline     474 bytes
office-ui-fabric-react fluentui-react-components-Image 32.033 kB 32.502 kB ExceedsBaseline     469 bytes
office-ui-fabric-react fluentui-react-components-Divider 32.279 kB 32.746 kB ExceedsBaseline     467 bytes
office-ui-fabric-react fluentui-react-components-ax 1.245 kB 1.525 kB ExceedsBaseline     280 bytes
office-ui-fabric-react fluentui-react-components-makeStyles 21.446 kB 21.645 kB ExceedsBaseline     199 bytes

ExceedsTolerance Over Tolerance (1024 B) ExceedsBaseline Over Baseline BelowBaseline Below Baseline New New Deleted  Removed 1 kB = 1000 B

Baseline commit: c2331d66c74be1b3e0ed413b89d4f795ee5581a1 (build)

@fabricteam
Copy link
Collaborator

fabricteam commented Mar 24, 2021

Perf Analysis

Scenario Render type Master Ticks PR Ticks Iterations Status
Avatar mount 906 940 5000 Possible regression
ButtonNext mount 545 552 5000 Possible regression
All results

Scenario Render type Master Ticks PR Ticks Iterations Status
Avatar mount 906 940 5000 Possible regression
BaseButton mount 892 880 5000
Breadcrumb mount 42522 42692 5000
ButtonNext mount 545 552 5000 Possible regression
Checkbox mount 1485 1478 5000
CheckboxBase mount 1276 1254 5000
ChoiceGroup mount 4600 4705 5000
ComboBox mount 965 932 1000
CommandBar mount 9914 9900 1000
ContextualMenu mount 5997 6048 1000
DefaultButton mount 1066 1084 5000
DetailsRow mount 3567 3531 5000
DetailsRowFast mount 3583 3586 5000
DetailsRowNoStyles mount 3371 3395 5000
Dialog mount 1449 1438 1000
DocumentCardTitle mount 1798 1796 1000
Dropdown mount 3198 3222 5000
FocusTrapZone mount 1735 1763 5000
FocusZone mount 1788 1780 5000
IconButton mount 1678 1687 5000
Label mount 325 327 5000
Layer mount 1738 1736 5000
Link mount 458 443 5000
MakeStyles mount 1721 1811 50000
MenuButton mount 1406 1420 5000
MessageBar mount 1949 1996 5000
Nav mount 3163 3181 1000
OverflowSet mount 1033 1023 5000
Panel mount 1397 1376 1000
Persona mount 805 805 1000
Pivot mount 1404 1391 1000
PrimaryButton mount 1276 1262 5000
Rating mount 7484 7552 5000
SearchBox mount 1280 1269 5000
Shimmer mount 2511 2414 5000
Slider mount 1920 1920 5000
SpinButton mount 4781 4803 5000
Spinner mount 411 422 5000
SplitButton mount 3035 3080 5000
Stack mount 484 481 5000
StackWithIntrinsicChildren mount 1502 1489 5000
StackWithTextChildren mount 4384 4362 5000
SwatchColorPicker mount 10023 10123 5000
Tabs mount 1377 1372 1000
TagPicker mount 2781 2768 5000
TeachingBubble mount 11461 11314 5000
Text mount 405 407 5000
TextField mount 1336 1292 5000
ThemeProvider mount 1140 1142 5000
ThemeProvider virtual-rerender 588 570 5000
ThemeProviderNext mount 15553 15704 5000
Toggle mount 783 783 5000
buttonNative mount 111 109 5000

Perf Analysis (Fluent)

Perf comparison
Status Scenario Fluent TPI Fabric TPI Ratio Iterations Ticks
🦄 Avatar.Fluent 0.17 0.47 0.36:1 2000 342
🦄 Button.Fluent 0.11 0.19 0.58:1 5000 573
🔧 Checkbox.Fluent 0.62 0.34 1.82:1 1000 623
🎯 Dialog.Fluent 0.16 0.21 0.76:1 5000 780
🔧 Dropdown.Fluent 3.1 0.39 7.95:1 1000 3104
🔧 Icon.Fluent 0.14 0.05 2.8:1 5000 687
🦄 Image.Fluent 0.08 0.13 0.62:1 5000 394
🔧 Slider.Fluent 1.59 0.44 3.61:1 1000 1593
🔧 Text.Fluent 0.07 0.03 2.33:1 5000 369
🦄 Tooltip.Fluent 0.14 0.88 0.16:1 5000 687

🔧 Needs work     🎯 On target     🦄 Amazing

Perf tests with no regressions
Scenario Current PR Ticks Baseline Ticks Ratio
AccordionMinimalPerf.default 164 153 1.07:1
AlertMinimalPerf.default 308 288 1.07:1
AttachmentSlotsPerf.default 1211 1142 1.06:1
GridMinimalPerf.default 365 345 1.06:1
AvatarMinimalPerf.default 209 200 1.05:1
PortalMinimalPerf.default 174 166 1.05:1
RadioGroupMinimalPerf.default 454 434 1.05:1
Image.Fluent 394 377 1.05:1
AttachmentMinimalPerf.default 170 164 1.04:1
SegmentMinimalPerf.default 378 363 1.04:1
DividerMinimalPerf.default 379 369 1.03:1
ImageMinimalPerf.default 405 395 1.03:1
LabelMinimalPerf.default 444 429 1.03:1
ListNestedPerf.default 591 574 1.03:1
TextMinimalPerf.default 372 362 1.03:1
Avatar.Fluent 342 333 1.03:1
Icon.Fluent 687 665 1.03:1
CarouselMinimalPerf.default 495 485 1.02:1
TextAreaMinimalPerf.default 494 484 1.02:1
ToolbarMinimalPerf.default 981 960 1.02:1
TreeMinimalPerf.default 790 771 1.02:1
BoxMinimalPerf.default 383 378 1.01:1
ButtonMinimalPerf.default 185 183 1.01:1
ButtonSlotsPerf.default 581 576 1.01:1
ButtonUseCssNestingPerf.default 1085 1075 1.01:1
CardMinimalPerf.default 571 565 1.01:1
FormMinimalPerf.default 427 422 1.01:1
HeaderMinimalPerf.default 381 377 1.01:1
InputMinimalPerf.default 1292 1279 1.01:1
LoaderMinimalPerf.default 739 732 1.01:1
MenuMinimalPerf.default 895 885 1.01:1
RosterPerf.default 1150 1143 1.01:1
StatusMinimalPerf.default 742 736 1.01:1
TableMinimalPerf.default 415 411 1.01:1
CustomToolbarPrototype.default 3794 3771 1.01:1
Button.Fluent 573 567 1.01:1
AnimationMinimalPerf.default 416 416 1:1
ChatDuplicateMessagesPerf.default 292 293 1:1
CheckboxMinimalPerf.default 2801 2799 1:1
DatepickerMinimalPerf.default 44377 44298 1:1
DialogMinimalPerf.default 769 768 1:1
DropdownManyItemsPerf.default 713 713 1:1
DropdownMinimalPerf.default 3079 3086 1:1
EmbedMinimalPerf.default 4221 4231 1:1
HeaderSlotsPerf.default 794 796 1:1
LayoutMinimalPerf.default 428 427 1:1
MenuButtonMinimalPerf.default 1567 1573 1:1
PopupMinimalPerf.default 705 703 1:1
SkeletonMinimalPerf.default 374 375 1:1
Dropdown.Fluent 3104 3101 1:1
ButtonOverridesMissPerf.default 1700 1716 0.99:1
ChatMinimalPerf.default 612 616 0.99:1
FlexMinimalPerf.default 312 316 0.99:1
ItemLayoutMinimalPerf.default 1221 1228 0.99:1
ListMinimalPerf.default 524 527 0.99:1
ProviderMinimalPerf.default 986 993 0.99:1
SliderMinimalPerf.default 1560 1568 0.99:1
SplitButtonMinimalPerf.default 3715 3740 0.99:1
TableManyItemsPerf.default 1987 2004 0.99:1
TreeWith60ListItems.default 185 186 0.99:1
Dialog.Fluent 780 789 0.99:1
Slider.Fluent 1593 1609 0.99:1
Tooltip.Fluent 687 695 0.99:1
ReactionMinimalPerf.default 404 411 0.98:1
RefMinimalPerf.default 241 247 0.98:1
Text.Fluent 369 378 0.98:1
ListWith60ListItems.default 630 649 0.97:1
ProviderMergeThemesPerf.default 1633 1677 0.97:1
IconMinimalPerf.default 663 687 0.97:1
TooltipMinimalPerf.default 943 973 0.97:1
VideoMinimalPerf.default 617 636 0.97:1
Checkbox.Fluent 623 643 0.97:1
ButtonUseCssPerf.default 812 844 0.96:1
ChatWithPopoverPerf.default 380 396 0.96:1
ListCommonPerf.default 638 662 0.96:1

Comment on lines -28 to -29
// arguments are parsed manually to avoid double loops as TS & Babel transforms rest via an additional loop
// @see https://babeljs.io/docs/en/babel-plugin-transform-parameters
Copy link
Member

Choose a reason for hiding this comment

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

Is this perf opimization no longer true ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, as signature of ax() in make-styles changed (now it accepts an array as a second param).

@@ -1,3 +1,2 @@
export { createCSSVariablesProxy } from './createCSSVariablesProxy';
export { resolveDefinitions } from './resolveDefinitions';
Copy link
Member Author

Choose a reason for hiding this comment

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

Cruft, from previous implementation of makeStyles()

@@ -394,7 +390,7 @@ describe('resolveStyleRules', () => {
animationDuration: '5s',
}),
).toMatchInlineSnapshot(`
@-webkit-keyframes f13owpa8 {
@-webkit-keyframes f1q8eu9e {
Copy link
Member Author

Choose a reason for hiding this comment

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

Hashes of keyframes have been changed because of a change in cssifyObject() that always adds trailing commas now.

@layershifter layershifter marked this pull request as ready for review March 25, 2021 16:56
import { cssifyObject } from './utils/cssifyObject';

export function compileStaticCSS(property: string, value: MakeStyles): string {
const cssRule = `${property} {${cssifyObject(value)}}`;
return compileCSSRule(cssRule);
return compileCSSRules(cssRule)[0];
Copy link
Member

Choose a reason for hiding this comment

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

What is the story for RTL in static CSS?

Copy link
Member Author

Choose a reason for hiding this comment

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

Automatic transformers are not supported as we don't know how global classes will be applied later. For example:

body { left: 10px }


// eslint-disable-next-line guard-for-in
for (const slotName in resolvedStyles) {
const slotClasses = renderer.insertDefinitions(dir, resolvedStyles[slotName]);
Copy link
Member

Choose a reason for hiding this comment

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

It is renderer's responsibility to compute the classnames, but the rest of the code expect the classnames are stable and atomic. Where do we have this contract described and tested?

Copy link
Member Author

Choose a reason for hiding this comment

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

It is not described. For me it's unclear path yet...

  • Will we implement CSS extraction to .css files?
  • How will look build time transform?
  • Where is the balance between memory and perf for runtime/build time?

I don't have answers and thus don't want to make a strict contract there yet...

@layershifter layershifter merged commit a20aadf into master Mar 30, 2021
@layershifter layershifter deleted the fix/ax-rtl branch March 30, 2021 14:00
@miroslavstastny miroslavstastny mentioned this pull request Mar 30, 2021
22 tasks
@msft-fluent-ui-bot
Copy link
Collaborator

🎉@fluentui/react-text@v0.2.7 has been released which incorporates this pull request.:tada:

Handy links:

@msft-fluent-ui-bot
Copy link
Collaborator

🎉@fluentui/react-menu@v0.13.0 has been released which incorporates this pull request.:tada:

Handy links:

@msft-fluent-ui-bot
Copy link
Collaborator

🎉@fluentui/react-make-styles@v9.0.0-alpha.17 has been released which incorporates this pull request.:tada:

Handy links:

@msft-fluent-ui-bot
Copy link
Collaborator

🎉@fluentui/make-styles@v9.0.0-alpha.8 has been released which incorporates this pull request.:tada:

Handy links:

@msft-fluent-ui-bot
Copy link
Collaborator

🎉@fluentui/react-avatar@v9.0.0-alpha.18 has been released which incorporates this pull request.:tada:

Handy links:

@msft-fluent-ui-bot
Copy link
Collaborator

🎉@fluentui/react-cards@v1.0.0-beta.77 has been released which incorporates this pull request.:tada:

Handy links:

@msft-fluent-ui-bot
Copy link
Collaborator

🎉@fluentui/react-examples@v8.11.0 has been released which incorporates this pull request.:tada:

Handy links:

miroslavstastny added a commit to miroslavstastny/fluentui that referenced this pull request May 5, 2021
* fix(makeStyles): handle RTL properly

* Change files

* fix test, add export to avoid explosion in all components

* fix lint

* fix tests, fix build issues

* fix typings, ignore eslint

* fixes to compileCSS, unit tests

* fixes to cssifyObject(), update useBadgeStyles & useCardStyles

* rework impl

* Change files

* remove todo

* complete todos

* add a UT

* add VR tests

* use smaller screenshots

* Update packages/make-styles/src/ax.ts

Co-authored-by: Miroslav Stastny <mistastn@microsoft.com>

* Update packages/react-examples/src/react-make-styles/RTL/RTL.stories.tsx

Co-authored-by: Miroslav Stastny <mistastn@microsoft.com>

* add a warning and UT for unregistered sequence

* fix insertion for RTL

* fix lint error

Co-authored-by: Miroslav Stastny <mistastn@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants