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

BorderBoxControl: fix right border control alignment on small viewports and RTL styles #41254

Merged
merged 6 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

- `CheckboxControl`: Add unit tests ([#41165](https://github.com/WordPress/gutenberg/pull/41165)).

### Bug fix

- `BorderBoxControl`: fix some layout misalignments, especially for RTL users ([#41254](https://github.com/WordPress/gutenberg/pull/41254)).

## 19.11.0 (2022-05-18)

### Enhancements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const BorderBoxControlSplitControls = (
onChange,
popoverPlacement,
popoverOffset,
rightAlignedClassName,
value,
__experimentalHasMultipleOrigins,
__experimentalIsRenderedInSidebar,
Expand Down Expand Up @@ -80,6 +81,7 @@ const BorderBoxControlSplitControls = (
{ ...sharedBorderControlProps }
/>
<BorderControl
className={ rightAlignedClassName }
hideLabelFromVision={ true }
label={ __( 'Right border' ) }
onChange={ ( newBorder ) => onChange( newBorder, 'right' ) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,27 @@ export function useBorderBoxControlSplitControls(

// Generate class names.
const cx = useCx();
const rtlWatchResult = rtl.watch();
const classes = useMemo( () => {
return cx( styles.BorderBoxControlSplitControls, className );
}, [ className, rtl.watch() ] );
return cx( styles.borderBoxControlSplitControls(), className );
// rtlWatchResult is needed to refresh styles when the writing direction changes
// eslint-disable-next-line react-hooks/exhaustive-deps
Comment on lines +28 to +29
Copy link
Contributor Author

@ciampo ciampo May 23, 2022

Choose a reason for hiding this comment

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

Added a bunch of these eslint-disable-next-line rules in preparation for the exhaustive-deps rule (see #41166)

@sarayourfriend , WDYT about the current rtl.watch() approach? Asking because, with the new eslint rule, we may need to have to "ignore" the rule a lot of the times, which defeats the point a little bit

(cc @chad1008 )

Copy link
Contributor

Choose a reason for hiding this comment

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

It does defeat the purpose of adding the exhaustive-deps rule if all components should eventually support RTL. In the interim though probably better to catch a few oversights in the dependency arrays.

Copy link
Contributor Author

@ciampo ciampo May 24, 2022

Choose a reason for hiding this comment

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

I agree. Ignoring the rule for now is a lesser evil compared to not adding the eslint rule at all. Will keep it as is for now

Copy link
Contributor

Choose a reason for hiding this comment

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

Want to confirm I'm following the logic here :)

rtlWatchResult isn't actually called in the useMemo's create function, so it's not technically a dependency from React's POV.

We do, however, want to recalculate classes whenever rtlWatchResult changes, so we add it to the dependency array to trigger the useMemo, which exhaustive-deps will hate for the reason described above?

Copy link
Contributor Author

@ciampo ciampo May 24, 2022

Choose a reason for hiding this comment

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

Correct.

A couple of alternative approaches that come to mind:

  • we could make these "dynamic style" functions accept, as an argument, a isRTL boolean (for example: return cx( styles.borderBoxControlSplitControls( rtlWatchResult ), className );). This would make the computation of RTL styles less "auto-magical", which isn't necessarily a bad thing since we've been missing a few of those!
  • create a hook that forces a re-render of the component when the writing direction changes, and therefore remove it from the list of dependencies of useMemo

Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

The first option sounds, to me, like it might make things a little more intuitive, but I'd happily defer to others who've worked more with the current auto-magical approach 😄

Copy link
Member

Choose a reason for hiding this comment

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

I feel like we've discussed this before so sorry if I'm forgetting a crucial piece of the convo, but do we actually have a good reason to memoize these things at all? I searched around but couldn't really find anything that mentioned any performance issues that were observed. The Emotion folks also don't recommend blanket optimizations like this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is also a good question. From what I remember, the reason why we memoize Emotion classname computation is because of a test ran by Q before I joined the components squad, where he found out that computing these classnames can be quite expensive.

Since then, quite some time has passed and, with that, libraries (like Emotion and React) also got updated. Therefore, I'm not sure if this assumption still holds true. We should run some tests and take it from there

}, [ cx, className, rtlWatchResult ] );

const centeredClassName = useMemo( () => {
return cx( styles.CenteredBorderControl, className );
}, [] );
}, [ cx, className ] );

return { ...otherProps, centeredClassName, className: classes };
const rightAlignedClassName = useMemo( () => {
return cx( styles.rightBorderControl(), className );
// rtlWatchResult is needed to refresh styles when the writing direction changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ cx, className, rtlWatchResult ] );

return {
...otherProps,
centeredClassName,
className: classes,
rightAlignedClassName,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@ export function useBorderBoxControlVisualizer(

// Generate class names.
const cx = useCx();
const rtlWatchResult = rtl.watch();
const classes = useMemo( () => {
return cx(
styles.BorderBoxControlVisualizer( value, __next36pxDefaultSize ),
styles.borderBoxControlVisualizer( value, __next36pxDefaultSize ),
className
);
}, [ className, value, __next36pxDefaultSize, rtl.watch() ] );
// rtlWatchResult is needed to refresh styles when the writing direction changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ cx, className, value, __next36pxDefaultSize, rtlWatchResult ] );

return { ...otherProps, className: classes, value };
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@ export function useBorderBoxControl(
const cx = useCx();
const classes = useMemo( () => {
return cx( styles.BorderBoxControl, className );
}, [ className ] );
}, [ cx, className ] );

const linkedControlClassName = useMemo( () => {
return cx( styles.LinkedBorderControl );
}, [] );
}, [ cx ] );

return {
...otherProps,
Expand Down
8 changes: 6 additions & 2 deletions packages/components/src/border-box-control/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const BorderBoxStyleWithFallback = ( border?: Border ) => {
return `${ color } ${ borderStyle } ${ clampedWidth }`;
};

export const BorderBoxControlVisualizer = (
export const borderBoxControlVisualizer = (
borders?: Borders,
__next36pxDefaultSize?: boolean
) => {
Expand All @@ -64,7 +64,7 @@ export const BorderBoxControlVisualizer = (
`;
};

export const BorderBoxControlSplitControls = css`
export const borderBoxControlSplitControls = () => css`
position: relative;
flex: 1;
${ rtl( { marginRight: space( 3 ) }, { marginLeft: space( 3 ) } )() }
Expand All @@ -74,3 +74,7 @@ export const CenteredBorderControl = css`
grid-column: span 2;
margin: 0 auto;
`;

export const rightBorderControl = () => css`
${ rtl( { marginLeft: 'auto' }, { marginRight: 'auto' } )() }
`;