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

feat(react): add unstable_Stack component #9876

Merged
2 changes: 2 additions & 0 deletions packages/carbon-react/.storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ module.exports = {
'./Welcome/Welcome.stories.js',
'../src/**/*.stories.js',
'../src/**/*.stories.mdx',
'../../react/src/components/Stack/*.stories.js',
'../../react/src/components/Stack/*.stories.mdx',
],
webpack(config) {
const babelLoader = config.module.rules.find((rule) => {
Expand Down
1 change: 1 addition & 0 deletions packages/react/.storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ module.exports = {
'./Welcome/Welcome.stories.js',
'../src/**/*-story.js',
'../src/**/*.stories.mdx',
'!../src/components/Stack',
],

webpack(config) {
Expand Down
1 change: 1 addition & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"dependencies": {
"@carbon/feature-flags": "^0.6.0",
"@carbon/icons-react": "^10.41.0",
"@carbon/layout": "^10.33.0",
"@carbon/telemetry": "0.0.0-alpha.6",
"classnames": "2.3.1",
"copy-to-clipboard": "^3.3.1",
Expand Down
20 changes: 20 additions & 0 deletions packages/react/src/components/Stack/Stack-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright IBM Corp. 2016, 2018
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

describe('Stack', () => {
it.todo('should support alternate element types with the `as` prop');

it.todo('should support a custom className with the `className` prop');

it.todo('should support alternate orientations with the `orientation` prop');

it.todo('should support specifying the layout gap with the `step` prop');

it.todo('should support custom layout gap with the `gap` prop');

it.todo('should apply additional props to the outermost element');
});
106 changes: 106 additions & 0 deletions packages/react/src/components/Stack/Stack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* Copyright IBM Corp. 2016, 2018
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import { spacing } from '@carbon/layout';
import cx from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import { usePrefix } from '../../internal/usePrefix';

/**
* The number of steps in the spacing scale
* @type {number}
*/
const SPACING_STEPS = spacing.length;

/**
* The Stack component is a useful layout utility in a component-based model.
* This allows components to not use margin and instead delegate the
* responsibility of positioning and layout to parent components.
*
* In the case of the Stack component, it uses the spacing scale from the
* Design Language in order to determine how much space there should be between
* items rendered by the Stack component. It also supports a custom `gap` prop
* which will allow a user to provide a custom value for the gap of the layout.
*
* This component supports both horizontal and vertical orientations.
*
* Inspiration for this component:
*
* - https://paste.twilio.design/layout/stack/
* - https://github.com/Workday/canvas-kit/blob/f2f599654876700f483a1d8c5de82a41315c76f1/modules/labs-react/layout/lib/Stack.tsx
*/
const Stack = React.forwardRef(function Stack(props, ref) {
const {
as: BaseComponent = 'div',
children,
className: customClassName,
gap,
orientation = 'vertical',
step,
...rest
} = props;
const prefix = usePrefix();
const className = cx(customClassName, {
[`${prefix}--stack-${orientation}`]: true,
[`${prefix}--stack-scale-${step}`]: step !== undefined,
});
const style = {};

if (step === undefined && gap !== undefined) {
style[`--${prefix}-stack-gap`] = gap;
}

return (
<BaseComponent {...rest} ref={ref} className={className} style={style}>
{children}
</BaseComponent>
);
});

Stack.propTypes = {
/**
* Provide a custom element type to render as the outermost element in
* the Stack component. By default, this component will render a `div`.
*/
as: PropTypes.elementType,

/**
* Provide the elements that will be rendered as children inside of the Stack
* component. These elements will have having spacing between them according
* to the `step` and `orientation` prop
*/
children: PropTypes.node,

/**
* Provide a custom class name to be used by the outermost element rendered by
* Stack
*/
className: PropTypes.string,

/**
* Provide a custom value for the gap used by the Stack layout
*/
gap: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

/**
* Specify the orientation of them items in the Stack
*/
orientation: PropTypes.oneOf(['horizontal', 'vertical']),

/**
* Provide the step from the spacing scale to be used as the gap for the
* items rendered by the Stack
*/
step: PropTypes.oneOf(
joshblack marked this conversation as resolved.
Show resolved Hide resolved
Array.from({ length: SPACING_STEPS }).map((_, step) => {
return step;
})
),
};

export default Stack;
78 changes: 78 additions & 0 deletions packages/react/src/components/Stack/Stack.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* Copyright IBM Corp. 2016, 2018
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import { Stack } from '../Stack';

export default {
title: 'Layout/Stack',
component: Stack,
parameters: {
controls: {
hideNoControlsWarning: true,
},
},
argTypes: {
children: {
table: {
disable: true,
},
},
},
};

export const Default = () => {
return (
<Stack step={6}>
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</Stack>
);
};

export const Horizontal = () => {
return (
<Stack step={6} orientation="horizontal">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</Stack>
);
};

const PlaygroundStory = (props) => {
return (
<Stack {...props}>
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</Stack>
);
};

export const Playground = PlaygroundStory.bind({});

Playground.argTypes = {
gap: {
control: {
type: 'text',
},
},
orientation: {
options: ['horizontal', 'vertical'],
control: {
type: 'select',
},
},
step: {
options: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
control: {
type: 'select',
},
},
};
8 changes: 8 additions & 0 deletions packages/react/src/components/Stack/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright IBM Corp. 2016, 2018
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

export { default as Stack } from './Stack';
joshblack marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions packages/styles/scss/_spacing.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

@forward '@carbon/layout/scss/modules/spacing'
show
$spacing,
$spacing-01,
$spacing-02,
$spacing-03,
Expand Down
1 change: 1 addition & 0 deletions packages/styles/scss/components/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
@use 'select';
@use 'skeleton-styles';
@use 'slider';
@use 'stack';
@use 'structured-list';
@use 'tabs';
@use 'tag';
Expand Down
11 changes: 11 additions & 0 deletions packages/styles/scss/components/stack/_index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// Copyright IBM Corp. 2018, 2018
//
// This source code is licensed under the Apache-2.0 license found in the
// LICENSE file in the root directory of this source tree.
//

@forward 'stack';
@use 'stack';

@include stack.stack;
34 changes: 34 additions & 0 deletions packages/styles/scss/components/stack/_stack.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// Copyright IBM Corp. 2018, 2018
//
// This source code is licensed under the Apache-2.0 license found in the
// LICENSE file in the root directory of this source tree.
//

@use '../../config' as *;
@use '../../spacing';
@use '../../utilities/custom-property';

@mixin stack() {
.#{$prefix}--stack-horizontal {
display: inline-grid;
column-gap: custom-property.get-var('stack-gap', 0);
grid-auto-flow: column;
}

.#{$prefix}--stack-vertical {
display: grid;
grid-auto-flow: row;
row-gap: custom-property.get-var('stack-gap', 0);
}

$index: 1;

@each $step, $value in spacing.$spacing {
.#{$prefix}--stack-scale-#{$index} {
@include custom-property.declaration('stack-gap', $value);
}

$index: $index + 1;
}
}
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10310,6 +10310,7 @@ __metadata:
"@babel/preset-react": ^7.14.5
"@carbon/feature-flags": ^0.6.0
"@carbon/icons-react": ^10.41.0
"@carbon/layout": ^10.33.0
"@carbon/telemetry": 0.0.0-alpha.6
"@carbon/test-utils": ^10.19.0
"@cypress/react": ^5.4.0
Expand Down