[UI Framework] Add support for wrap prop to FlexGroup (#15009)

* Migrate KuiFlexItem numeric grow prop from EUI.
* Loop through FlexGroup gutter size modifiers.
* Add wrap prop to FlexGroup.
This commit is contained in:
CJ Cenizal 2017-11-22 11:19:45 -08:00 committed by GitHub
parent 3fb3511563
commit a2f08677e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 314 additions and 86 deletions

View file

@ -1171,48 +1171,72 @@ input[type="button"] {
-webkit-flex-basis: 0; -webkit-flex-basis: 0;
-ms-flex-preferred-size: 0; -ms-flex-preferred-size: 0;
flex-basis: 0; } flex-basis: 0; }
.kuiFlexGroup.kuiFlexGroup--gutterSmall > .kuiFlexItem + .kuiFlexItem {
margin-left: 8px; } .kuiFlexGroup--gutterSmall {
.kuiFlexGroup.kuiFlexGroup--gutterMedium > .kuiFlexItem + .kuiFlexItem { margin: -4px; }
margin-left: 16px; } .kuiFlexGroup--gutterSmall > .kuiFlexItem {
.kuiFlexGroup.kuiFlexGroup--gutterLarge > .kuiFlexItem + .kuiFlexItem { margin: 4px; }
margin-left: 24px; }
.kuiFlexGroup.kuiFlexGroup--gutterExtraLarge > .kuiFlexItem + .kuiFlexItem { .kuiFlexGroup--gutterMedium {
margin-left: 40px; } margin: -8px; }
.kuiFlexGroup.kuiFlexGroup--justifyContentSpaceBetween { .kuiFlexGroup--gutterMedium > .kuiFlexItem {
-webkit-box-pack: justify; margin: 8px; }
-webkit-justify-content: space-between;
-ms-flex-pack: justify; .kuiFlexGroup--gutterLarge {
justify-content: space-between; } margin: -12px; }
.kuiFlexGroup.kuiFlexGroup--justifyContentSpaceAround { .kuiFlexGroup--gutterLarge > .kuiFlexItem {
-webkit-justify-content: space-around; margin: 12px; }
-ms-flex-pack: distribute;
justify-content: space-around; } .kuiFlexGroup--gutterExtraLarge {
.kuiFlexGroup.kuiFlexGroup--justifyContentCenter { margin: -20px; }
-webkit-box-pack: center; .kuiFlexGroup--gutterExtraLarge > .kuiFlexItem {
-webkit-justify-content: center; margin: 20px; }
-ms-flex-pack: center;
justify-content: center; } .kuiFlexGroup--justifyContentSpaceBetween {
.kuiFlexGroup.kuiFlexGroup--justifyContentFlexEnd { -webkit-box-pack: justify;
-webkit-box-pack: end; -webkit-justify-content: space-between;
-webkit-justify-content: flex-end; -ms-flex-pack: justify;
-ms-flex-pack: end; justify-content: space-between; }
justify-content: flex-end; }
.kuiFlexGroup.kuiFlexGroup--alignItemsStart { .kuiFlexGroup--justifyContentSpaceAround {
-webkit-box-align: start; -webkit-justify-content: space-around;
-webkit-align-items: flex-start; -ms-flex-pack: distribute;
-ms-flex-align: start; justify-content: space-around; }
align-items: flex-start; }
.kuiFlexGroup.kuiFlexGroup--alignItemsCenter { .kuiFlexGroup--justifyContentCenter {
-webkit-box-align: center; -webkit-box-pack: center;
-webkit-align-items: center; -webkit-justify-content: center;
-ms-flex-align: center; -ms-flex-pack: center;
align-items: center; } justify-content: center; }
.kuiFlexGroup.kuiFlexGroup--alignItemsEnd {
-webkit-box-align: end; .kuiFlexGroup--justifyContentFlexEnd {
-webkit-align-items: flex-end; -webkit-box-pack: end;
-ms-flex-align: end; -webkit-justify-content: flex-end;
align-items: 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) { @media only screen and (max-width: 768px) {
.kuiFlexGroup { .kuiFlexGroup {
@ -1433,6 +1457,56 @@ input[type="button"] {
-ms-flex-preferred-size: auto; -ms-flex-preferred-size: auto;
flex-basis: auto; flex-basis: auto;
/* 2 */ } /* 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) { @media only screen and (max-width: 768px) {
.kuiFlexItem { .kuiFlexItem {

View file

@ -15,6 +15,10 @@ import FlexGroup from './flex_group';
const flexGroupSource = require('!!raw-loader!./flex_group'); const flexGroupSource = require('!!raw-loader!./flex_group');
const flexGroupHtml = renderToHtml(FlexGroup); 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'; import FlexItems from './flex_items';
const flexItemsSource = require('!!raw-loader!./flex_items'); const flexItemsSource = require('!!raw-loader!./flex_items');
const flexItemsHtml = renderToHtml(FlexItems); const flexItemsHtml = renderToHtml(FlexItems);
@ -23,9 +27,13 @@ import FlexGutter from './flex_gutter';
const flexGutterSource = require('!!raw-loader!./flex_gutter'); const flexGutterSource = require('!!raw-loader!./flex_gutter');
const flexGutterHtml = renderToHtml(FlexGutter); const flexGutterHtml = renderToHtml(FlexGutter);
import FlexGrow from './flex_grow'; import FlexGrowZero from './flex_grow_zero';
const flexGrowSource = require('!!raw-loader!./flex_grow'); const flexGrowZeroSource = require('!!raw-loader!./flex_grow_zero');
const flexGrowHtml = renderToHtml(FlexGrow); 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'; import FlexJustify from './flex_justify';
const flexJustifySource = require('!!raw-loader!./flex_justify'); const flexJustifySource = require('!!raw-loader!./flex_justify');
@ -64,6 +72,25 @@ export default props => (
<GuideDemo className="guideDemo__highlightGrid"><FlexGroup /></GuideDemo> <GuideDemo className="guideDemo__highlightGrid"><FlexGroup /></GuideDemo>
</GuideSection> </GuideSection>
<GuideSection
title="FlexGroup can wrap its items"
source={[{
type: GuideSectionTypes.JS,
code: flexGroupWrapSource,
}, {
type: GuideSectionTypes.HTML,
code: flexGroupWrapHtml,
}]}
>
<GuideText>
You can set <GuideCode>wrap</GuideCode> on <GuideCode>FlexGroup</GuideCode> if it
contains <GuideCode>FlexItem</GuideCode>s with minimum widths, which you want to wrap as
the container becomes narrower.
</GuideText>
<GuideDemo className="guideDemo__highlightGrid"><FlexGroupWrap /></GuideDemo>
</GuideSection>
<GuideSection <GuideSection
title="FlexGroup accepts infinite items" title="FlexGroup accepts infinite items"
source={[{ source={[{
@ -86,10 +113,10 @@ export default props => (
title="FlexItem can individually turn off stretching" title="FlexItem can individually turn off stretching"
source={[{ source={[{
type: GuideSectionTypes.JS, type: GuideSectionTypes.JS,
code: flexGrowSource, code: flexGrowZeroSource,
}, { }, {
type: GuideSectionTypes.HTML, type: GuideSectionTypes.HTML,
code: flexGrowHtml, code: flexGrowZeroHtml,
}]} }]}
> >
<GuideText> <GuideText>
@ -97,7 +124,25 @@ export default props => (
can be turned off on each item individually. can be turned off on each item individually.
</GuideText> </GuideText>
<GuideDemo className="guideDemo__highlightGrid"><FlexGrow /></GuideDemo> <GuideDemo className="guideDemo__highlightGrid"><FlexGrowZero /></GuideDemo>
</GuideSection>
<GuideSection
title="FlexItem can specify a proportional width"
source={[{
type: GuideSectionTypes.JS,
code: flexGrowNumericSource,
}, {
type: GuideSectionTypes.HTML,
code: flexGrowNumericHtml,
}]}
>
<GuideText>
You can specify a number between 1 and 10 for a <GuideCode>FlexItem</GuideCode> to
try to take up a proportional part of the flex box it is in.
</GuideText>
<GuideDemo className="guideDemo__highlightGrid"><FlexGrowNumeric /></GuideDemo>
</GuideSection> </GuideSection>
<GuideSection <GuideSection

View file

@ -0,0 +1,22 @@
import React from 'react';
import {
KuiFlexGroup,
KuiFlexItem,
} from '../../../../components';
export default () => (
<KuiFlexGroup wrap>
<KuiFlexItem style={{ minWidth: 300 }}>
Min-width 300px
</KuiFlexItem>
<KuiFlexItem style={{ minWidth: 300 }}>
Min-width 300px
</KuiFlexItem>
<KuiFlexItem style={{ minWidth: 300 }}>
Min-width 300px
</KuiFlexItem>
</KuiFlexGroup>
);

View file

@ -0,0 +1,27 @@
import React from 'react';
import {
KuiFlexGroup,
KuiFlexItem,
} from '../../../../components';
export default () => (
<div>
<KuiFlexGroup>
<KuiFlexItem grow={1}>1</KuiFlexItem>
<KuiFlexItem grow={2}>2<br />wraps content if necessary</KuiFlexItem>
<KuiFlexItem grow={3}>3<br />expands_to_fit_if_content_cannot_wrap</KuiFlexItem>
<KuiFlexItem grow={4}>4</KuiFlexItem>
</KuiFlexGroup>
<br /><br />
<KuiFlexGroup>
<KuiFlexItem grow={6}>6</KuiFlexItem>
<KuiFlexItem grow={3}>3</KuiFlexItem>
<KuiFlexItem grow={1}>1</KuiFlexItem>
<KuiFlexItem grow={3}>3</KuiFlexItem>
<KuiFlexItem grow={6}>6</KuiFlexItem>
</KuiFlexGroup>
</div>
);

View file

@ -6,53 +6,60 @@
flex-grow: 1; flex-grow: 1;
flex-basis: 0; flex-basis: 0;
} }
}
// Gutter Sizes $gutterTypes: (
&.kuiFlexGroup--gutterSmall > .kuiFlexItem + .kuiFlexItem { gutterSmall: $kuiSizeS,
margin-left: $kuiSizeS; gutterMedium: $kuiSize,
} gutterLarge: $kuiSizeL,
gutterExtraLarge: $kuiSizeXXL,
);
&.kuiFlexGroup--gutterMedium > .kuiFlexItem + .kuiFlexItem { // Gutter Sizes
margin-left: $kuiSize; @each $gutterName, $gutterSize in $gutterTypes {
} $halfGutterSize: $gutterSize * 0.5;
&.kuiFlexGroup--gutterLarge > .kuiFlexItem + .kuiFlexItem { &.kuiFlexGroup--#{$gutterName} {
margin-left: $kuiSizeL; margin: -$halfGutterSize;
}
&.kuiFlexGroup--gutterExtraLarge > .kuiFlexItem + .kuiFlexItem { & > .kuiFlexItem {
margin-left: $kuiSizeXXL; margin: $halfGutterSize;
}
} }
}
// Justify the grid // Justify the grid
&.kuiFlexGroup--justifyContentSpaceBetween { .kuiFlexGroup--justifyContentSpaceBetween {
justify-content: space-between; justify-content: space-between;
} }
&.kuiFlexGroup--justifyContentSpaceAround { .kuiFlexGroup--justifyContentSpaceAround {
justify-content: space-around; justify-content: space-around;
} }
&.kuiFlexGroup--justifyContentCenter { .kuiFlexGroup--justifyContentCenter {
justify-content: center; justify-content: center;
} }
&.kuiFlexGroup--justifyContentFlexEnd { .kuiFlexGroup--justifyContentFlexEnd {
justify-content: flex-end; justify-content: flex-end;
} }
// Align Items // Align Items
&.kuiFlexGroup--alignItemsStart { .kuiFlexGroup--alignItemsStart {
align-items: flex-start; align-items: flex-start;
} }
&.kuiFlexGroup--alignItemsCenter { .kuiFlexGroup--alignItemsCenter {
align-items: center; align-items: center;
} }
&.kuiFlexGroup--alignItemsEnd { .kuiFlexGroup--alignItemsEnd {
align-items: flex-end; align-items: flex-end;
} }
.kuiFlexGroup--wrap {
flex-wrap: wrap;
} }
@include screenXSmall { @include screenXSmall {

View file

@ -13,6 +13,12 @@
flex-grow: 0; /* 2 */ flex-grow: 0; /* 2 */
flex-basis: auto; /* 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. // On mobile we force them to stack and act the same.

View file

@ -31,13 +31,24 @@ const justifyContentToClassNameMap = {
export const JUSTIFY_CONTENTS = Object.keys(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( const classes = classNames(
'kuiFlexGroup', 'kuiFlexGroup',
gutterSizeToClassNameMap[gutterSize], gutterSizeToClassNameMap[gutterSize],
alignItemsToClassNameMap[alignItems], alignItemsToClassNameMap[alignItems],
justifyContentToClassNameMap[justifyContent], justifyContentToClassNameMap[justifyContent],
className className,
{
'kuiFlexGroup--wrap': wrap,
},
); );
return ( return (
@ -56,10 +67,12 @@ KuiFlexGroup.propTypes = {
gutterSize: PropTypes.oneOf(GUTTER_SIZES), gutterSize: PropTypes.oneOf(GUTTER_SIZES),
alignItems: PropTypes.oneOf(ALIGN_ITEMS), alignItems: PropTypes.oneOf(ALIGN_ITEMS),
justifyContent: PropTypes.oneOf(JUSTIFY_CONTENTS), justifyContent: PropTypes.oneOf(JUSTIFY_CONTENTS),
wrap: PropTypes.bool,
}; };
KuiFlexGroup.defaultProps = { KuiFlexGroup.defaultProps = {
gutterSize: 'large', gutterSize: 'large',
alignItems: 'stretch', alignItems: 'stretch',
justifyContent: 'flexStart', justifyContent: 'flexStart',
wrap: false,
}; };

View file

@ -2,11 +2,14 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
const validGrowNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
export const KuiFlexItem = ({ children, className, grow, ...rest }) => { export const KuiFlexItem = ({ children, className, grow, ...rest }) => {
const classes = classNames( const classes = classNames(
'kuiFlexItem', 'kuiFlexItem',
{ {
'kuiFlexItem--flexGrowZero': !grow, 'kuiFlexItem--flexGrowZero': !grow,
[`kuiFlexItem--flexGrow${grow}`]: validGrowNumbers.indexOf(grow) >= 0,
}, },
className className
); );
@ -23,9 +26,25 @@ export const KuiFlexItem = ({ children, className, grow, ...rest }) => {
KuiFlexItem.propTypes = { KuiFlexItem.propTypes = {
children: PropTypes.node, 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 = { KuiFlexItem.defaultProps = {
grow: true, grow: true,
}; };

View file

@ -13,4 +13,19 @@ describe('KuiFlexItem', () => {
expect(component) expect(component)
.toMatchSnapshot(); .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)
);
});
}); });