From 4e2832d06131a979187757dce5756588c4a52890 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Wed, 22 Nov 2017 12:27:39 -0800 Subject: [PATCH] [UI Framework] Add support for wrap prop to FlexGroup (#15009) (#15128) * Migrate KuiFlexItem numeric grow prop from EUI. * Loop through FlexGroup gutter size modifiers. * Add wrap prop to FlexGroup. --- ui_framework/dist/ui_framework.css | 158 +++++++++++++----- .../doc_site/src/views/flex/flex_example.js | 57 ++++++- .../src/views/flex/flex_group_wrap.js | 22 +++ .../src/views/flex/flex_grow_numeric.js | 27 +++ .../flex/{flex_grow.js => flex_grow_zero.js} | 0 .../src/components/flex/_flex_group.scss | 77 +++++---- .../src/components/flex/_flex_item.scss | 6 + .../src/components/flex/flex_group.js | 17 +- ui_framework/src/components/flex/flex_item.js | 21 ++- .../src/components/flex/flex_item.test.js | 15 ++ 10 files changed, 314 insertions(+), 86 deletions(-) create mode 100644 ui_framework/doc_site/src/views/flex/flex_group_wrap.js create mode 100644 ui_framework/doc_site/src/views/flex/flex_grow_numeric.js rename ui_framework/doc_site/src/views/flex/{flex_grow.js => flex_grow_zero.js} (100%) diff --git a/ui_framework/dist/ui_framework.css b/ui_framework/dist/ui_framework.css index d33febaf4f77c0..66d256b0989341 100644 --- a/ui_framework/dist/ui_framework.css +++ b/ui_framework/dist/ui_framework.css @@ -1171,48 +1171,72 @@ input[type="button"] { -webkit-flex-basis: 0; -ms-flex-preferred-size: 0; flex-basis: 0; } - .kuiFlexGroup.kuiFlexGroup--gutterSmall > .kuiFlexItem + .kuiFlexItem { - margin-left: 8px; } - .kuiFlexGroup.kuiFlexGroup--gutterMedium > .kuiFlexItem + .kuiFlexItem { - margin-left: 16px; } - .kuiFlexGroup.kuiFlexGroup--gutterLarge > .kuiFlexItem + .kuiFlexItem { - margin-left: 24px; } - .kuiFlexGroup.kuiFlexGroup--gutterExtraLarge > .kuiFlexItem + .kuiFlexItem { - margin-left: 40px; } - .kuiFlexGroup.kuiFlexGroup--justifyContentSpaceBetween { - -webkit-box-pack: justify; - -webkit-justify-content: space-between; - -ms-flex-pack: justify; - justify-content: space-between; } - .kuiFlexGroup.kuiFlexGroup--justifyContentSpaceAround { - -webkit-justify-content: space-around; - -ms-flex-pack: distribute; - justify-content: space-around; } - .kuiFlexGroup.kuiFlexGroup--justifyContentCenter { - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; } - .kuiFlexGroup.kuiFlexGroup--justifyContentFlexEnd { - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - -ms-flex-pack: end; - justify-content: flex-end; } - .kuiFlexGroup.kuiFlexGroup--alignItemsStart { - -webkit-box-align: start; - -webkit-align-items: flex-start; - -ms-flex-align: start; - align-items: flex-start; } - .kuiFlexGroup.kuiFlexGroup--alignItemsCenter { - -webkit-box-align: center; - -webkit-align-items: center; - -ms-flex-align: center; - align-items: center; } - .kuiFlexGroup.kuiFlexGroup--alignItemsEnd { - -webkit-box-align: end; - -webkit-align-items: flex-end; - -ms-flex-align: end; - align-items: flex-end; } + +.kuiFlexGroup--gutterSmall { + margin: -4px; } + .kuiFlexGroup--gutterSmall > .kuiFlexItem { + margin: 4px; } + +.kuiFlexGroup--gutterMedium { + margin: -8px; } + .kuiFlexGroup--gutterMedium > .kuiFlexItem { + margin: 8px; } + +.kuiFlexGroup--gutterLarge { + margin: -12px; } + .kuiFlexGroup--gutterLarge > .kuiFlexItem { + margin: 12px; } + +.kuiFlexGroup--gutterExtraLarge { + margin: -20px; } + .kuiFlexGroup--gutterExtraLarge > .kuiFlexItem { + margin: 20px; } + +.kuiFlexGroup--justifyContentSpaceBetween { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; } + +.kuiFlexGroup--justifyContentSpaceAround { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around; } + +.kuiFlexGroup--justifyContentCenter { + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; } + +.kuiFlexGroup--justifyContentFlexEnd { + -webkit-box-pack: end; + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end; } + +.kuiFlexGroup--alignItemsStart { + -webkit-box-align: start; + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; } + +.kuiFlexGroup--alignItemsCenter { + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; } + +.kuiFlexGroup--alignItemsEnd { + -webkit-box-align: end; + -webkit-align-items: flex-end; + -ms-flex-align: end; + align-items: flex-end; } + +.kuiFlexGroup--wrap { + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; } @media only screen and (max-width: 768px) { .kuiFlexGroup { @@ -1433,6 +1457,56 @@ input[type="button"] { -ms-flex-preferred-size: auto; flex-basis: auto; /* 2 */ } + .kuiFlexItem.kuiFlexItem--flexGrow1 { + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; } + .kuiFlexItem.kuiFlexItem--flexGrow2 { + -webkit-box-flex: 2; + -webkit-flex-grow: 2; + -ms-flex-positive: 2; + flex-grow: 2; } + .kuiFlexItem.kuiFlexItem--flexGrow3 { + -webkit-box-flex: 3; + -webkit-flex-grow: 3; + -ms-flex-positive: 3; + flex-grow: 3; } + .kuiFlexItem.kuiFlexItem--flexGrow4 { + -webkit-box-flex: 4; + -webkit-flex-grow: 4; + -ms-flex-positive: 4; + flex-grow: 4; } + .kuiFlexItem.kuiFlexItem--flexGrow5 { + -webkit-box-flex: 5; + -webkit-flex-grow: 5; + -ms-flex-positive: 5; + flex-grow: 5; } + .kuiFlexItem.kuiFlexItem--flexGrow6 { + -webkit-box-flex: 6; + -webkit-flex-grow: 6; + -ms-flex-positive: 6; + flex-grow: 6; } + .kuiFlexItem.kuiFlexItem--flexGrow7 { + -webkit-box-flex: 7; + -webkit-flex-grow: 7; + -ms-flex-positive: 7; + flex-grow: 7; } + .kuiFlexItem.kuiFlexItem--flexGrow8 { + -webkit-box-flex: 8; + -webkit-flex-grow: 8; + -ms-flex-positive: 8; + flex-grow: 8; } + .kuiFlexItem.kuiFlexItem--flexGrow9 { + -webkit-box-flex: 9; + -webkit-flex-grow: 9; + -ms-flex-positive: 9; + flex-grow: 9; } + .kuiFlexItem.kuiFlexItem--flexGrow10 { + -webkit-box-flex: 10; + -webkit-flex-grow: 10; + -ms-flex-positive: 10; + flex-grow: 10; } @media only screen and (max-width: 768px) { .kuiFlexItem { diff --git a/ui_framework/doc_site/src/views/flex/flex_example.js b/ui_framework/doc_site/src/views/flex/flex_example.js index 04eb729d42c0c4..078c68b3658d4e 100644 --- a/ui_framework/doc_site/src/views/flex/flex_example.js +++ b/ui_framework/doc_site/src/views/flex/flex_example.js @@ -15,6 +15,10 @@ import FlexGroup from './flex_group'; const flexGroupSource = require('!!raw-loader!./flex_group'); const flexGroupHtml = renderToHtml(FlexGroup); +import FlexGroupWrap from './flex_group_wrap'; +const flexGroupWrapSource = require('!!raw-loader!./flex_group_wrap'); +const flexGroupWrapHtml = renderToHtml(FlexGroupWrap); + import FlexItems from './flex_items'; const flexItemsSource = require('!!raw-loader!./flex_items'); const flexItemsHtml = renderToHtml(FlexItems); @@ -23,9 +27,13 @@ import FlexGutter from './flex_gutter'; const flexGutterSource = require('!!raw-loader!./flex_gutter'); const flexGutterHtml = renderToHtml(FlexGutter); -import FlexGrow from './flex_grow'; -const flexGrowSource = require('!!raw-loader!./flex_grow'); -const flexGrowHtml = renderToHtml(FlexGrow); +import FlexGrowZero from './flex_grow_zero'; +const flexGrowZeroSource = require('!!raw-loader!./flex_grow_zero'); +const flexGrowZeroHtml = renderToHtml(FlexGrowZero); + +import FlexGrowNumeric from './flex_grow_numeric'; +const flexGrowNumericSource = require('!!raw-loader!./flex_grow_numeric'); +const flexGrowNumericHtml = renderToHtml(FlexGrowNumeric); import FlexJustify from './flex_justify'; const flexJustifySource = require('!!raw-loader!./flex_justify'); @@ -64,6 +72,25 @@ export default props => ( + + + You can set wrap on FlexGroup if it + contains FlexItems with minimum widths, which you want to wrap as + the container becomes narrower. + + + + + ( title="FlexItem can individually turn off stretching" source={[{ type: GuideSectionTypes.JS, - code: flexGrowSource, + code: flexGrowZeroSource, }, { type: GuideSectionTypes.HTML, - code: flexGrowHtml, + code: flexGrowZeroHtml, }]} > @@ -97,7 +124,25 @@ export default props => ( can be turned off on each item individually. - + + + + + + You can specify a number between 1 and 10 for a FlexItem to + try to take up a proportional part of the flex box it is in. + + + ( + + + Min-width 300px + + + + Min-width 300px + + + + Min-width 300px + + +); diff --git a/ui_framework/doc_site/src/views/flex/flex_grow_numeric.js b/ui_framework/doc_site/src/views/flex/flex_grow_numeric.js new file mode 100644 index 00000000000000..9ed249d857a7d8 --- /dev/null +++ b/ui_framework/doc_site/src/views/flex/flex_grow_numeric.js @@ -0,0 +1,27 @@ +import React from 'react'; + +import { + KuiFlexGroup, + KuiFlexItem, +} from '../../../../components'; + +export default () => ( +
+ + 1 + 2
wraps content if necessary
+ 3
expands_to_fit_if_content_cannot_wrap
+ 4 +
+ +

+ + + 6 + 3 + 1 + 3 + 6 + +
+); diff --git a/ui_framework/doc_site/src/views/flex/flex_grow.js b/ui_framework/doc_site/src/views/flex/flex_grow_zero.js similarity index 100% rename from ui_framework/doc_site/src/views/flex/flex_grow.js rename to ui_framework/doc_site/src/views/flex/flex_grow_zero.js diff --git a/ui_framework/src/components/flex/_flex_group.scss b/ui_framework/src/components/flex/_flex_group.scss index 16931e2a781a78..3d76a1f87246a5 100644 --- a/ui_framework/src/components/flex/_flex_group.scss +++ b/ui_framework/src/components/flex/_flex_group.scss @@ -6,53 +6,60 @@ flex-grow: 1; flex-basis: 0; } +} - // Gutter Sizes - &.kuiFlexGroup--gutterSmall > .kuiFlexItem + .kuiFlexItem { - margin-left: $kuiSizeS; - } +$gutterTypes: ( + gutterSmall: $kuiSizeS, + gutterMedium: $kuiSize, + gutterLarge: $kuiSizeL, + gutterExtraLarge: $kuiSizeXXL, +); - &.kuiFlexGroup--gutterMedium > .kuiFlexItem + .kuiFlexItem { - margin-left: $kuiSize; - } +// Gutter Sizes +@each $gutterName, $gutterSize in $gutterTypes { + $halfGutterSize: $gutterSize * 0.5; - &.kuiFlexGroup--gutterLarge > .kuiFlexItem + .kuiFlexItem { - margin-left: $kuiSizeL; - } + &.kuiFlexGroup--#{$gutterName} { + margin: -$halfGutterSize; - &.kuiFlexGroup--gutterExtraLarge > .kuiFlexItem + .kuiFlexItem { - margin-left: $kuiSizeXXL; + & > .kuiFlexItem { + margin: $halfGutterSize; + } } +} - // Justify the grid - &.kuiFlexGroup--justifyContentSpaceBetween { - justify-content: space-between; - } +// Justify the grid +.kuiFlexGroup--justifyContentSpaceBetween { + justify-content: space-between; +} - &.kuiFlexGroup--justifyContentSpaceAround { - justify-content: space-around; - } +.kuiFlexGroup--justifyContentSpaceAround { + justify-content: space-around; +} - &.kuiFlexGroup--justifyContentCenter { - justify-content: center; - } +.kuiFlexGroup--justifyContentCenter { + justify-content: center; +} - &.kuiFlexGroup--justifyContentFlexEnd { - justify-content: flex-end; - } +.kuiFlexGroup--justifyContentFlexEnd { + justify-content: flex-end; +} - // Align Items - &.kuiFlexGroup--alignItemsStart { - align-items: flex-start; - } +// Align Items +.kuiFlexGroup--alignItemsStart { + align-items: flex-start; +} - &.kuiFlexGroup--alignItemsCenter { - align-items: center; - } +.kuiFlexGroup--alignItemsCenter { + align-items: center; +} - &.kuiFlexGroup--alignItemsEnd { - align-items: flex-end; - } +.kuiFlexGroup--alignItemsEnd { + align-items: flex-end; +} + +.kuiFlexGroup--wrap { + flex-wrap: wrap; } @include screenXSmall { diff --git a/ui_framework/src/components/flex/_flex_item.scss b/ui_framework/src/components/flex/_flex_item.scss index 82147f38df383e..167222dcf47810 100644 --- a/ui_framework/src/components/flex/_flex_item.scss +++ b/ui_framework/src/components/flex/_flex_item.scss @@ -13,6 +13,12 @@ flex-grow: 0; /* 2 */ flex-basis: auto; /* 2 */ } + + @for $i from 1 through 10 { + &.kuiFlexItem--flexGrow#{$i} { + flex-grow: $i + } + } } // On mobile we force them to stack and act the same. diff --git a/ui_framework/src/components/flex/flex_group.js b/ui_framework/src/components/flex/flex_group.js index ae0fbf4415411f..6c5567d4d758f3 100644 --- a/ui_framework/src/components/flex/flex_group.js +++ b/ui_framework/src/components/flex/flex_group.js @@ -31,13 +31,24 @@ const justifyContentToClassNameMap = { export const JUSTIFY_CONTENTS = Object.keys(justifyContentToClassNameMap); -export const KuiFlexGroup = ({ children, className, gutterSize, alignItems, justifyContent, ...rest }) => { +export const KuiFlexGroup = ({ + children, + className, + gutterSize, + alignItems, + justifyContent, + wrap, + ...rest +}) => { const classes = classNames( 'kuiFlexGroup', gutterSizeToClassNameMap[gutterSize], alignItemsToClassNameMap[alignItems], justifyContentToClassNameMap[justifyContent], - className + className, + { + 'kuiFlexGroup--wrap': wrap, + }, ); return ( @@ -56,10 +67,12 @@ KuiFlexGroup.propTypes = { gutterSize: PropTypes.oneOf(GUTTER_SIZES), alignItems: PropTypes.oneOf(ALIGN_ITEMS), justifyContent: PropTypes.oneOf(JUSTIFY_CONTENTS), + wrap: PropTypes.bool, }; KuiFlexGroup.defaultProps = { gutterSize: 'large', alignItems: 'stretch', justifyContent: 'flexStart', + wrap: false, }; diff --git a/ui_framework/src/components/flex/flex_item.js b/ui_framework/src/components/flex/flex_item.js index d348924d7e4528..eeabc214de0afd 100644 --- a/ui_framework/src/components/flex/flex_item.js +++ b/ui_framework/src/components/flex/flex_item.js @@ -2,11 +2,14 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; +const validGrowNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + export const KuiFlexItem = ({ children, className, grow, ...rest }) => { const classes = classNames( 'kuiFlexItem', { 'kuiFlexItem--flexGrowZero': !grow, + [`kuiFlexItem--flexGrow${grow}`]: validGrowNumbers.indexOf(grow) >= 0, }, className ); @@ -23,9 +26,25 @@ export const KuiFlexItem = ({ children, className, grow, ...rest }) => { KuiFlexItem.propTypes = { children: PropTypes.node, - grow: PropTypes.bool, + grow: growPropType, }; +function growPropType(props, propName, componentName) { + const value = props[propName]; + + const validValues = [ + null, undefined, + true, false, + ...validGrowNumbers + ]; + + if (validValues.indexOf(value) === -1) { + return new Error( + `Prop \`${propName}\` supplied to \`${componentName}\` must be a boolean or an integer between 1 and 10.` + ); + } +} + KuiFlexItem.defaultProps = { grow: true, }; diff --git a/ui_framework/src/components/flex/flex_item.test.js b/ui_framework/src/components/flex/flex_item.test.js index fad345933de451..d9fdd02f35ae0d 100644 --- a/ui_framework/src/components/flex/flex_item.test.js +++ b/ui_framework/src/components/flex/flex_item.test.js @@ -13,4 +13,19 @@ describe('KuiFlexItem', () => { expect(component) .toMatchSnapshot(); }); + + test('tests the grow prop correctly', () => { + const propType = KuiFlexItem.propTypes.grow; + + const validValues = [undefined, null, true, false, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + const invalidValues = ['true', 'false', '1', 0]; + + validValues.forEach(value => + expect(propType({ grow: value }, `grow`)).toBe(undefined) + ); + + invalidValues.forEach(value => + expect(propType({ grow: value }, `grow`) instanceof Error).toBe(true) + ); + }); });