Skip to content

Make auto and limited label widths of horizontal FormLayout work in all browsers (#135) #165

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

Merged
merged 1 commit into from
Oct 8, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
32 changes: 3 additions & 29 deletions src/lib/components/layout/FormLayout/FormLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,13 @@ export const FormLayout = (props) => {
fieldLayout,
id,
labelWidth,
labelWidthFallback,
} = props;

if (!children) {
return null;
}

const HAS_CUSTOM_LABEL_WIDTH = !PREDEFINED_LABEL_WIDTH_VALUES.includes(labelWidth);
let customLabelWidth;

if (HAS_CUSTOM_LABEL_WIDTH) {
customLabelWidth = labelWidth;
}
const hasCustomLabelWidth = !PREDEFINED_LABEL_WIDTH_VALUES.includes(labelWidth);

const fieldLayoutClass = (layout) => {
if (layout === 'horizontal') {
Expand All @@ -34,7 +28,7 @@ export const FormLayout = (props) => {
};

const labelWidthClass = (width) => {
if (HAS_CUSTOM_LABEL_WIDTH) {
if (hasCustomLabelWidth) {
return styles.hasRootLabelWidthCustom;
}

Expand All @@ -49,30 +43,15 @@ export const FormLayout = (props) => {
return styles.hasRootLabelWidthDefault;
};

const inlineStyle = (customWidth, customWidthFallback) => {
const style = {};

if (customWidth) {
style['--rui-custom-label-width'] = customWidth;
}

if (customWidthFallback) {
style['--rui-custom-label-width-fallback'] = customWidthFallback;
}

return style;
};

return (
<div
id={id}
className={[
styles.root,
fieldLayoutClass(fieldLayout),
fieldLayout === 'horizontal' ? labelWidthClass(labelWidth) : '',
labelWidthFallback ? styles.hasRootCustomLabelWidthFallback : '',
].join(' ')}
style={inlineStyle(customLabelWidth, labelWidthFallback)}
{...hasCustomLabelWidth ? { style: { '--rui-custom-label-width': labelWidth } } : {}}
>
{flattenChildren(children).map((child) => {
if (!React.isValidElement(child)) {
Expand All @@ -93,7 +72,6 @@ FormLayout.defaultProps = {
fieldLayout: 'vertical',
id: undefined,
labelWidth: 'default',
labelWidthFallback: undefined,
};

FormLayout.propTypes = {
Expand All @@ -118,10 +96,6 @@ FormLayout.propTypes = {
PropTypes.oneOf(PREDEFINED_LABEL_WIDTH_VALUES),
PropTypes.string,
]),
/**
* Fallback value applied when `labelWidth` is either `auto` or `limited`.
*/
labelWidthFallback: PropTypes.string,
};

export default FormLayout;
22 changes: 7 additions & 15 deletions src/lib/components/layout/FormLayout/FormLayout.scss
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// 1. Form field label width is defined using custom property. Form Layout variants are created just
// 1. Form field label width is defined using custom property. FormLayout variants are created just
// by adjusting this value, either through a particular modification class, or manually through
// a prop. Form fields then inherit the layout using the `subgrid` feature which makes it possible
// to turn on automatic size of grid column with labels (in supporting browsers). There is always
// a fallback value for browsers that don't support `subgrid` (see `tools/forms/_layouts.scss`).
// This value is passed down to form fields utilising custom property cascade.
// a prop. Form fields then inherit the layout using the `subgrid` feature which makes it
// possible to turn on automatic size of grid column with labels (in supporting browsers, see 4).
//
// 2. By default, form field label width is set to a configurable global value.
//
// 3. Custom label width can be entered individually through a prop for each use of Form Layout.
// 4. Form Layout can also automatically adjust the width of column with labels according to the
//
// 4. FormLayout can also automatically adjust the width of column with labels according to the
// longest label (which is possible only with `subgrid`). However, this feature is currently
// supported only in Firefox so a fallback value is necessary for non-supporting browsers
// (a global default which can be customised per use).
Expand Down Expand Up @@ -35,25 +36,16 @@

.hasRootLabelWidthDefault {
--rui-local-label-width: #{$form-layout-horizontal-label-default-width}; // 1., 2.
--rui-local-label-width-fallback: #{$form-layout-horizontal-label-default-width}; // 1., 2.
}

.hasRootLabelWidthAuto {
--rui-local-label-width: #{$form-layout-horizontal-label-auto-width}; // 4.
--rui-local-label-width-fallback: #{$form-layout-horizontal-label-auto-width-fallback}; // 4.
}

.hasRootLabelWidthLimited {
--rui-local-label-width: #{$form-layout-horizontal-label-limited-width}; // 4.
--rui-local-label-width-fallback: #{$form-layout-horizontal-label-limited-width-fallback}; // 4.
}

.hasRootLabelWidthCustom {
--rui-local-label-width: var(--rui-custom-label-width); // 3.
--rui-local-label-width-fallback: var(--rui-custom-label-width); // 3.
}

.hasRootLabelWidthAuto.hasRootCustomLabelWidthFallback,
.hasRootLabelWidthLimited.hasRootCustomLabelWidthFallback {
--rui-local-label-width-fallback: var(--rui-custom-label-width-fallback); // 4.
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ export const FormLayoutCustomField = ({
`.trim()}
>
{label && (
<div
id={id && `${id}__label`}
className={styles.label}
>
<div id={id && `${id}__label`}>
{label}
</div>
)}
Expand Down
11 changes: 0 additions & 11 deletions src/lib/components/layout/FormLayout/FormLayoutCustomField.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@
@include form-field-in-form-layout();
}

.label {
overflow-wrap: break-word;
}

.rootLayoutHorizontal {
@include form-field-layout-horizontal();
}
Expand All @@ -24,10 +20,3 @@
.isRootFullWidth .field {
justify-self: stretch;
}

.root.rootLayoutHorizontal {
@include breakpoint-up($form-field-horizontal-breakpoint) {
grid-template-columns: var(--rui-local-label-width-fallback) 1fr;
grid-template-columns: subgrid;
}
}
27 changes: 7 additions & 20 deletions src/lib/components/layout/FormLayout/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -107,44 +107,31 @@ auto width, and limited width. For cases where an individual manual width works
better, there is the **local custom width mode** which enables setting a width
that is applied just for the current FormLayout.

⚠️ **Limited browser support:** The horizontal FormLayout uses the relatively
new
[CSS subgrid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Subgrid)
layout which is currently
[supported only by Firefox](https://caniuse.com/css-subgrid). However, we use
fallback solutions to achieve the closest possible result. The fallbacks will be
[removed](https://github.com/react-ui-org/react-ui/issues/127) once CSS subgrid
is fully supported in browsers that we support.

👉 All global label width options (including their fallback values, where
available) can be easily [customized](/customize/theming) with CSS custom
properties.
👉 All global label width options can be easily [customized](/customize/theming)
with CSS custom properties.

#### Label Width Options

- The `default` mode (global) sets the width of all labels to a **global default
value** which is 10 em. There is no fallback value for this option.
value** which is 10 em.

- The `auto` mode (global) aligns the form **according to the longest label.**
Its default fallback width is 10 em.

- The `limited` mode (global) works as `auto` except it's intended for values
that **set a limit for the label width.** Its default value is
`fitcontent(50%)` which also aligns the form according to the longest label
like `auto`, but with the difference that the labels cannot be wider than
50 % of the FormLayout. Its default fallback width is 10 em.
50 % of the FormLayout.

- The `custom` mode (local) allows you to enter any **custom label width for
individual FormLayouts.** There is no default or fallback value for this
option.
individual FormLayouts.**

👉 Please note the `auto` and `limited` label width options may not function
correctly in combination with other auto layout mechanisms, eg. the auto-width
[Modal](/components/ui/modal). It's just too much magic that doesn't work
together yet 🎩.

Try to resize the playground **in Firefox** to see how individual options work.
(The custom option works in all browsers.)
Try to resize the playground to see how individual options work.

<Playground>
{() => {
Expand Down Expand Up @@ -241,7 +228,7 @@ the FormLayout component.
This is a demo of all supported form components within the FormLayout.

<Playground>
<FormLayout fieldLayout="horizontal">
<FormLayout fieldLayout="horizontal" labelWidth="auto">
<>
<TextField
id="form-layout-horizontal-first-name"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ describe('rendering', () => {
expect(tree).toMatchSnapshot();
});

it('renders correctly with custom label width', () => {
it('renders correctly with auto label width', () => {
const tree = shallow((
<FormLayout
fieldLayout="horizontal"
id="test-id"
labelWidth="300px"
labelWidth="auto"
>
<TextField id="test-id" label="Text field" />
</FormLayout>
Expand All @@ -40,13 +40,12 @@ describe('rendering', () => {
expect(tree).toMatchSnapshot();
});

it('renders correctly with all props', () => {
it('renders correctly with custom label width', () => {
const tree = shallow((
<FormLayout
fieldLayout="horizontal"
id="test-id"
labelWidth="auto"
labelWidthFallback="200px"
labelWidth="300px"
>
<TextField id="test-id" label="Text field" />
</FormLayout>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

exports[`rendering renders correctly with a single child 1`] = `
<div
className="root rootFieldLayoutVertical "
style={Object {}}
className="root rootFieldLayoutVertical "
>
<ForwardRef(withForwardedRef(TextField))
id="test-id"
Expand All @@ -15,15 +14,10 @@ exports[`rendering renders correctly with a single child 1`] = `
</div>
`;

exports[`rendering renders correctly with all props 1`] = `
exports[`rendering renders correctly with auto label width 1`] = `
<div
className="root rootFieldLayoutHorizontal hasRootLabelWidthAuto hasRootCustomLabelWidthFallback"
className="root rootFieldLayoutHorizontal hasRootLabelWidthAuto"
id="test-id"
style={
Object {
"--rui-custom-label-width-fallback": "200px",
}
}
>
<ForwardRef(withForwardedRef(TextField))
id="test-id"
Expand All @@ -37,7 +31,7 @@ exports[`rendering renders correctly with all props 1`] = `

exports[`rendering renders correctly with custom label width 1`] = `
<div
className="root rootFieldLayoutHorizontal hasRootLabelWidthCustom "
className="root rootFieldLayoutHorizontal hasRootLabelWidthCustom"
id="test-id"
style={
Object {
Expand All @@ -57,8 +51,7 @@ exports[`rendering renders correctly with custom label width 1`] = `

exports[`rendering renders correctly with multiple children 1`] = `
<div
className="root rootFieldLayoutVertical "
style={Object {}}
className="root rootFieldLayoutVertical "
>
<ForwardRef(withForwardedRef(TextField))
id="test-id-1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ exports[`rendering renders correctly with all props 1`] = `
id="my-custom-field"
>
<div
className="label"
id="my-custom-field__label"
>
Label
Expand Down
2 changes: 0 additions & 2 deletions src/lib/styles/settings/_forms-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,5 @@ $form-field-sizes: (
);

$form-layout-horizontal-label-auto-width: var(--rui-form-layout-horizontal-label-auto-width);
$form-layout-horizontal-label-auto-width-fallback: var(--rui-form-layout-horizontal-label-auto-width-fallback);
$form-layout-horizontal-label-limited-width: var(--rui-form-layout-horizontal-label-limited-width);
$form-layout-horizontal-label-limited-width-fallback: var(--rui-form-layout-horizontal-label-limited-width-fallback);
$form-layout-horizontal-label-default-width: var(--rui-form-layout-horizontal-label-default-width);
5 changes: 4 additions & 1 deletion src/lib/styles/tools/_forms.deprecated.scss
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,12 @@
&.isRootLayoutHorizontal {
display: grid;
grid: inherit;
grid-template-columns: var(--rui-local-label-width-fallback) 1fr;
grid-template-columns: subgrid;
grid-column: span 2;

@supports not (grid-template-columns: subgrid) {
display: contents;
}
}

&.isRootLayoutHorizontal .inputWrap,
Expand Down
1 change: 0 additions & 1 deletion src/lib/styles/tools/forms/_foundation.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

@mixin form-field-label() {
padding-top: calc(#{$form-field-border-width} + var(--rui-local-padding-vertical)); // 6.
overflow-wrap: break-word;
}

@mixin form-field-label-required() {
Expand Down
22 changes: 15 additions & 7 deletions src/lib/styles/tools/forms/_layouts.scss
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
// 1. Form fields in vertical layout take up as much space as they need. Labels do not wrap until
// forced by container of the field. Min and max limits make extra long words break if necessary.
//
// 2. Form fields in horizontal layout also take up only as much space as they need. Labels do not
// wrap until label width limit is reached (50 % of available horizontal space by default).
//
// 3. Helper texts are aligned below input and wrapped. Their width is always limited to the width
// of the input field so they don't shift adjacent elements when they show up.
//
// 4. Define grid spacing as padding of child elements because grid areas make row and column gaps
// show up even when not necessary.
//
// 5. When input size is small and form field layout is set to horizontal, expand helper text over
// full form field width to improve its readability. Otherwise the text would be packed into a
// very narrow column and hard to read. To prevent the helper text from growing the layout more
// than necessary (ie. the size without helper text), `min-content` grid sizing must be used
// along with disabled wrapping of label.
//
// 6. Full-width horizontal form fields prefer not wrapping the label until a limit is reached
// (50 % of available horizontal space by default, same as in 2.).
//
// 7. Justify inputs to `start` in horizontal layouts to fix positioning issues with Select Fields.
// Reverted for full-width fields.
// 8. When form field exists inside Form Layout, automatic margins are removed as the Form Layout
//
// 8. When form field exists inside FormLayout, automatic margins are removed as the FormLayout
// takes care of proper spacing.
//
// 9. Grid settings are inherited from horizontal Form Layout and applied using `subgrid`.
// A fallback is supplied to browsers that don't support `subgrid` yet. Note the `min-content`
// value of the input column to prevent the fallback from growing the grid when helper text shows
// up. `subgrid` is fine with `1fr`.
// See Form Layout styles for more.
// A fallback is supplied to browsers that don't support `subgrid` yet. See FormLayout styles
// for more.

@import '../../settings/forms';
@import '../../settings/forms-theme';
Expand Down Expand Up @@ -132,13 +138,15 @@
&.rootLayoutHorizontal,
&.rootLayoutHorizontal.hasRootSmallInput {
grid: inherit; // 9.
grid-template-columns: var(--rui-local-label-width-fallback) min-content; // 9.
grid-template-columns: subgrid; // 9.
grid-column: span 2; // 9.

@supports not (grid-template-columns: subgrid) {
display: contents; // 9.
}
}

&.rootLayoutHorizontal.isRootFullWidth {
grid-template-columns: var(--rui-local-label-width-fallback) 1fr; // 9.
grid-template-columns: subgrid; // 9.
}

Expand Down
Loading