Skip to content

Commit

Permalink
Add support for flexbox helpers; add spacing helpers typedef
Browse files Browse the repository at this point in the history
  • Loading branch information
kennethnym committed Oct 18, 2020
1 parent 9e74e9b commit fbc25f7
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 18 deletions.
2 changes: 2 additions & 0 deletions src/modifiers/__test__/__snapshots__/modifiers.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ exports[`Helper proptypes Should have alignment responsive modifiers 1`] = `"has

exports[`Helper proptypes Should have background color success 1`] = `"has-background-success"`;

exports[`Helper proptypes Should have flexbox helpers 1`] = `"is-flex-direction-row-reverse is-flex-wrap-nowrap is-align-content-flex-start"`;

exports[`Helper proptypes Should have hidden modifier tablet-only and widescreen 1`] = `"is-hidden-tablet-only is-hidden-widescreen"`;

exports[`Helper proptypes Should have paddingless and clearfix classes 1`] = `"is-clearfix is-paddingless"`;
Expand Down
10 changes: 10 additions & 0 deletions src/modifiers/__test__/modifiers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ describe('Helper proptypes', () => {
).toMatchSnapshot();
});

test('Should have flexbox helpers', () => {
expect(
modifiers.classnames({
flexWrap: 'nowrap',
alignContent: 'flex-start',
flexDirection: 'row-reverse',
}),
).toMatchSnapshot();
});

test('Should have responsive modifier flex-tablet-only and block-widescreen', () => {
expect(
modifiers.classnames({
Expand Down
73 changes: 73 additions & 0 deletions src/modifiers/flexbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import PropTypes from 'prop-types';
import classNames from 'classnames';

const camelToSnake = (camel) =>
camel.replace(/[A-Z]/g, (cap) => `-${cap.toLowerCase()}`);

const flexboxHelperPropTypes = {
flexDirection: PropTypes.oneOf([
'row',
'row-reverse',
'column',
'column-reverse',
]),
flexWrap: PropTypes.oneOf(['nowrap', 'wrap', 'wrap-reverse']),
justifyContent: PropTypes.oneOf([
'flex-start',
'flex-end',
'center',
'space-around',
'space-between',
'space-evenly',
'start',
'end',
'left',
'right',
]),
alignContent: PropTypes.oneOf([
'flex-start',
'flex-end',
'center',
'space-around',
'space-between',
'space-evenly',
'stretch',
'start',
'end',
'baseline',
]),
alignItems: PropTypes.oneOf([
'auto',
'flex-start',
'flex-end',
'center',
'baseline',
'stretch',
]),
flexGrow: PropTypes.oneOf([0, 1, 2, 3, 4, 5]),
};

const flexboxHelperNames = Object.keys(flexboxHelperPropTypes);

export default {
propTypes: flexboxHelperPropTypes,
defaultProps: flexboxHelperNames.reduce((defaults, flexboxHelperName) => {
defaults[flexboxHelperName] = undefined;
return defaults;
}, {}),
classnames: (props) =>
classNames(
flexboxHelperNames.reduce((classes, flexboxHelper) => {
const propValue = props[flexboxHelper];
classes[`is-${camelToSnake(flexboxHelper)}-${propValue}`] = propValue;
return classes;
}, {}),
),
clean: (props) =>
Object.keys(props).reduce((cleanedProps, propName) => {
if (!flexboxHelperNames.includes(propName)) {
cleanedProps[propName] = props[propName];
}
return cleanedProps;
}, {}),
};
55 changes: 54 additions & 1 deletion src/modifiers/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,57 @@ interface HelperProps {
hidden?: boolean;
}

type SpacingSize = 0 | 1 | 2 | 3 | 4 | 5 | 6;

interface SpacingProps {
mt?: SpacingSize;
mr?: SpacingSize;
mb?: SpacingSize;
ml?: SpacingSize;
mx?: SpacingSize;
my?: SpacingSize;
pt?: SpacingSize;
pr?: SpacingSize;
pb?: SpacingSize;
pl?: SpacingSize;
px?: SpacingSize;
py?: SpacingSize;
}

interface FlexboxProps {
flexDirection?: 'row' | 'row-reverse' | 'column' | 'column-reverse';
flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse';
justifyContent?:
| 'flex-start'
| 'flex-end'
| 'center'
| 'space-around'
| 'space-between'
| 'space-evenly'
| 'start'
| 'end'
| 'left'
| 'right';
alignContent?:
| 'flex-start'
| 'flex-end'
| 'center'
| 'space-around'
| 'space-between'
| 'space-evenly'
| 'stretch'
| 'start'
| 'end'
| 'baseline';
alignItems?:
| 'auto'
| 'flex-start'
| 'flex-end'
| 'center'
| 'baseline'
| 'stretch';
}

interface ResponsiveModifiers {
display?: 'block' | 'flex' | 'inline' | 'inline-block' | 'inline-flex';
hide?: boolean;
Expand Down Expand Up @@ -57,7 +108,9 @@ type HTMLAttributes<K extends keyof JSX.IntrinsicElements> = OmitKeys<

// Credit to https://stackoverflow.com/questions/54049871/how-do-i-type-this-as-jsx-attribute-in-typescript

type ModifierProps = HelperProps &
type ModifierProps = SpacingProps &
FlexboxProps &
HelperProps &
ColorProps &
ResponsiveProps &
TypographyProps;
Expand Down
5 changes: 5 additions & 0 deletions src/modifiers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import responsive from './responsives';
import colors from './colors';
import typography from './typography';
import spacing from './spacing';
import flexbox from './flexbox';

const compose = (...fns) => (args) => fns.reduce((arg, fn) => fn(arg), args);

Expand All @@ -13,12 +14,14 @@ export default {
...responsive.propTypes,
...colors.propTypes,
...typography.propTypes,
...flexbox.propTypes,
},
defaultProps: {
...helpers.defaultProps,
...responsive.defaultProps,
...colors.defaultProps,
...typography.defaultProps,
...flexbox.defaultProps,
},
classnames: (props) =>
classNames(
Expand All @@ -27,6 +30,7 @@ export default {
colors.classnames(props),
typography.classnames(props),
spacing.classnames(props),
flexbox.classnames(props),
),
clean: (props) =>
compose(
Expand All @@ -35,5 +39,6 @@ export default {
colors.clean,
typography.clean,
spacing.clean,
flexbox.clean,
)(props),
};
24 changes: 14 additions & 10 deletions src/modifiers/responsives.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,14 @@ const classNamesFromProps = (props) =>
// each viewport has two props:
// mobile, mobileOnly; desktop, desktopOnly, etc.
// this checks if propName is a responsive modifier prop
if (VIEWPORTS.includes(maybeViewportName)) {
if (VIEWPORTS.includes(maybeViewportName) && props[propName]) {
const currentViewport = maybeViewportName;
const { display = '', hide = false, textSize = 0, textAlignment = '' } =
props[propName] || {};
const {
display = '',
hide = false,
textSize = 0,
textAlignment = '',
} = props[propName];

if (propName.includes('Only')) {
// current modifiers are viewport specific
Expand All @@ -91,11 +95,11 @@ const classNamesFromProps = (props) =>
export default {
...responsiveModifierPropTypes,
classnames: (props) => classNames(classNamesFromProps(props)),
clean: (props) => {
VIEWPORTS.forEach((viewport) => {
delete props[viewport];
delete props[`${viewport}Only`];
});
return props;
},
clean: (props) =>
Object.keys(props).reduce((cleanedProps, propName) => {
if (!VIEWPORTS.includes(propName.replace('Only', ''))) {
cleanedProps[propName] = props[propName];
}
return cleanedProps;
}, {}),
};
13 changes: 6 additions & 7 deletions src/modifiers/spacing.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import classnames from 'classnames';

const MODIFIER_NAMES = [
const SPACING_PROPS = [
'mt',
'mr',
'mb',
Expand All @@ -18,7 +18,7 @@ const MODIFIER_NAMES = [
const SIZES = [0, 1, 2, 3, 4, 5, 6];

export default {
...MODIFIER_NAMES.reduce(
...SPACING_PROPS.reduce(
(props, name) => {
props.propTypes[name] = PropTypes.oneOf(SIZES);
props.defaultProps[name] = undefined;
Expand All @@ -28,16 +28,15 @@ export default {
),
classnames: (props) =>
classnames(
Object.keys(props).reduce((classes, propName) => {
if (MODIFIER_NAMES.includes(propName)) {
classes[`${propName}-${props[propName]}`] = props[propName];
}
SPACING_PROPS.reduce((classes, spacing) => {
const spacingValue = props[spacing];
classes[`${spacing}-${spacingValue}`] = spacingValue;
return classes;
}, {}),
),
clean: (props) =>
Object.keys(props).reduce((cleaned, propName) => {
if (!MODIFIER_NAMES.includes(propName)) {
if (!SPACING_PROPS.includes(propName)) {
cleaned[propName] = props[propName];
}
return cleaned;
Expand Down

0 comments on commit fbc25f7

Please sign in to comment.