diff --git a/.storybook/preview.js b/.storybook/preview.js
index c807e83697..8c491b6775 100644
--- a/.storybook/preview.js
+++ b/.storybook/preview.js
@@ -74,7 +74,11 @@ addParameters({
"*",
"Accessibility",
"Hooks"
- ]
+ ],
+ includeName: false,
+ // currently there is a bug that makes any componet stories to be order alphabetical even so
+ // it is not the default settings. This settings purpose is to sort all the stories of any component by their load time
+ storySort: (a, b) => 0
}
}
});
diff --git a/src/components/Flex/Flex.jsx b/src/components/Flex/Flex.jsx
new file mode 100644
index 0000000000..5187ffacdf
--- /dev/null
+++ b/src/components/Flex/Flex.jsx
@@ -0,0 +1,88 @@
+import React, { useRef, forwardRef, useMemo } from "react";
+import PropTypes from "prop-types";
+import cx from "classnames";
+import useMergeRefs from "../../hooks/useMergeRefs";
+import { FLEX_POSITIONS, FLEX_GAPS, FLEX_DIRECTIONS } from "./FlexConstants";
+import { BASE_POSITIONS } from "../../constants/positions";
+import Clickable from "../Clickable/Clickable";
+import classes from "./Flex.module.scss";
+
+const Flex = forwardRef(
+ ({ className, id, elementType, direction, wrap, children, justify, align, gap, onClick, style }, ref) => {
+ const componentRef = useRef(null);
+ const mergedRef = useMergeRefs({ refs: [ref, componentRef] });
+ const overrideStyle = useMemo(() => ({ ...style, gap: `${gap}px` }), [style, gap]);
+ const Element = onClick ? Clickable : elementType;
+
+ return (
+
+ {children}
+
+ );
+ }
+);
+
+Flex.justify = FLEX_POSITIONS;
+Flex.align = BASE_POSITIONS;
+Flex.gaps = FLEX_GAPS;
+Flex.directions = FLEX_DIRECTIONS;
+
+Flex.propTypes = {
+ /**
+ * class name to be add to the wrapper
+ */
+ className: PropTypes.string,
+ /**
+ * id to be add to the wrapper
+ */
+ id: PropTypes.string,
+ style: PropTypes.object,
+ direction: PropTypes.oneOf([Flex.directions.ROW, Flex.directions.COLUMN]),
+ elementType: PropTypes.string,
+ wrap: PropTypes.bool,
+ children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),
+ justify: PropTypes.oneOf([
+ Flex.justify.START,
+ Flex.justify.CENTER,
+ Flex.justify.END,
+ Flex.justify.SPACE_BETWEEN,
+ Flex.justify.SPACE_AROUND
+ ]),
+ align: PropTypes.oneOf([Flex.align.START, Flex.align.CENTER, Flex.align.END]),
+ gap: PropTypes.oneOfType([
+ PropTypes.oneOf([Flex.gaps.NONE, Flex.gaps.SMALL, Flex.gaps.MEDIUM, Flex.gaps.LARGE]),
+ PropTypes.number
+ ])
+};
+
+Flex.defaultProps = {
+ className: "",
+ id: undefined,
+ elementType: "div",
+ style: undefined,
+ wrap: false,
+ children: undefined,
+ direction: Flex.directions.ROW,
+ justify: Flex.justify.START,
+ align: Flex.align.CENTER,
+ gap: Flex.gaps.NONE
+};
+
+export default Flex;
diff --git a/src/components/Flex/Flex.module.scss b/src/components/Flex/Flex.module.scss
new file mode 100644
index 0000000000..4b774dfe88
--- /dev/null
+++ b/src/components/Flex/Flex.module.scss
@@ -0,0 +1,48 @@
+@import "../../styles/themes.scss";
+
+.container {
+ display: flex;
+ flex-direction: row;
+ &.justify {
+ &Start {
+ justify-content: flex-start;
+ }
+
+ &End {
+ justify-content: flex-end;
+ }
+
+ &Center {
+ justify-content: center;
+ }
+
+ &SpaceBetween {
+ justify-content: space-between;
+ }
+
+ &SpaceAround {
+ justify-content: space-around;
+ }
+ }
+ &.align {
+ &Start {
+ align-items: flex-start;
+ }
+
+ &End {
+ align-items: flex-end;
+ }
+
+ &Center {
+ align-items: center;
+ }
+ }
+ &.direction {
+ &Column {
+ flex-direction: column;
+ }
+ }
+ &.wrap {
+ flex-wrap: wrap;
+ }
+}
\ No newline at end of file
diff --git a/src/components/Flex/FlexConstants.js b/src/components/Flex/FlexConstants.js
new file mode 100644
index 0000000000..f725d6c643
--- /dev/null
+++ b/src/components/Flex/FlexConstants.js
@@ -0,0 +1,20 @@
+import { BASE_POSITIONS } from "../../constants/positions";
+
+export const FLEX_POSITIONS = Object.freeze({
+ ...BASE_POSITIONS,
+ SPACE_AROUND: "SpaceAround",
+ SPACE_BETWEEN: "SpaceBetween"
+});
+
+export const FLEX_GAPS = Object.freeze({
+ XS: 4,
+ SMALL: 8,
+ MEDIUM: 16,
+ LARGE: 24,
+ NONE: 0
+});
+
+export const FLEX_DIRECTIONS = Object.freeze({
+ ROW: "Row",
+ COLUMN: "Column"
+});
diff --git a/src/components/Flex/__stories__/Flex.stories.mdx b/src/components/Flex/__stories__/Flex.stories.mdx
new file mode 100644
index 0000000000..e43c12dfdb
--- /dev/null
+++ b/src/components/Flex/__stories__/Flex.stories.mdx
@@ -0,0 +1,299 @@
+import Flex from "../Flex";
+import { ArgsTable, Story, Canvas, Meta } from "@storybook/addon-docs";
+import { Add, Search, Person, Filter, Sort } from "../../Icon/Icons";
+import Button from "../../Button/Button";
+import classes from "./Flex.stories.module.scss";
+import Chips from "../../Chips/Chips";
+import { StoryDescription } from "../../../storybook/components/story-description/story-description";
+import { LIST, MENU, TABS } from "../../../storybook/components/related-components/component-description-map";
+
+
+
+
+
+export const flexTemplate = (args) => {
+ return
+
+
+
+
+}
+
+
+
+# Flex
+- [Overview](#overview)
+- [Props](#props)
+- [Usage](#usage)
+- [Variants](#variants)
+- [Do’s and don’ts](#dos-and-donts)
+- [Use cases and examples](#use-cases-and-examples)
+- [Related components](#related-components)
+- [Feedback](#feedback)
+
+## Overview
+Use Flex component to position group of sub-elements in one dimension, horizontal or vertical, without being dependent on a custom CSS file for positioning the sub-elements.
+
+
+
+## Props
+
+
+## Usage
+
+
+## Variants
+### Directions
+
+
+### Horizontal spacing between items
+
+
+### Vertical spacing between items
+
+
+### Horizontal positions
+
+
+### Vertical positions
+
+
+### Support multi lines layout
+You can display a layout that includes multiple lines using the flex component wrap mode.
+This mode allows the layout to break into multiple lines if all the component children cannot fit into one only.
+
+
+## Use cases and examples
+
+### Flex as toolbar container
+You can use flex component for create responsive toolbars
+
+
+
+## Related components
+
\ No newline at end of file
diff --git a/src/components/Flex/__stories__/Flex.stories.module.scss b/src/components/Flex/__stories__/Flex.stories.module.scss
new file mode 100644
index 0000000000..4d14119536
--- /dev/null
+++ b/src/components/Flex/__stories__/Flex.stories.module.scss
@@ -0,0 +1,15 @@
+@import "../../../styles/themes.scss";
+
+.multi-lines-story-search {
+ width: 200px;
+}
+
+.flex-chip {
+ margin: 0;
+}
+
+.story-container {
+ & > * {
+ margin-bottom: var(--spacing-large);
+ }
+}
\ No newline at end of file
diff --git a/src/components/Flex/__tests__/__snapshots__/flex-snapshot-tests.jest.js.snap b/src/components/Flex/__tests__/__snapshots__/flex-snapshot-tests.jest.js.snap
new file mode 100644
index 0000000000..4abd4b8f24
--- /dev/null
+++ b/src/components/Flex/__tests__/__snapshots__/flex-snapshot-tests.jest.js.snap
@@ -0,0 +1,233 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Flex renders correctly Horizontal display with align 1`] = `
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+`;
+
+exports[`Flex renders correctly Horizontal display with align 2`] = `
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+`;
+
+exports[`Flex renders correctly Horizontal display with children 1`] = `
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+`;
+
+exports[`Flex renders correctly Horizontal display with gap 1`] = `
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+`;
+
+exports[`Flex renders correctly Horizontal display with justify 1`] = `
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+`;
+
+exports[`Flex renders correctly Horizontal display with wrap 1`] = `
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+`;
+
+exports[`Flex renders correctly Vertical display with align 1`] = `
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+`;
+
+exports[`Flex renders correctly Vertical display with children 1`] = `
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+`;
+
+exports[`Flex renders correctly Vertical display with justify 1`] = `
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+`;
+
+exports[`Flex renders correctly Vertical display with wrap 1`] = `
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+`;
+
+exports[`Flex renders correctly with empty props 1`] = `
+
+`;
diff --git a/src/components/Flex/__tests__/flex-snapshot-tests.jest.js b/src/components/Flex/__tests__/flex-snapshot-tests.jest.js
new file mode 100644
index 0000000000..d7cb58cde0
--- /dev/null
+++ b/src/components/Flex/__tests__/flex-snapshot-tests.jest.js
@@ -0,0 +1,135 @@
+import React from "react";
+import renderer from "react-test-renderer";
+import Flex from "../Flex";
+
+describe("Flex renders correctly", () => {
+ it("with empty props", () => {
+ const tree = renderer.create().toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+ describe("Horizontal display", () => {
+ it("with children", () => {
+ const tree = renderer
+ .create(
+
+ 1
+ 2
+ 3
+
+ )
+ .toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+ it("with align", () => {
+ const tree = renderer
+ .create(
+
+ 1
+ 2
+ 3
+
+ )
+ .toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+ it("with justify", () => {
+ const tree = renderer
+ .create(
+
+ 1
+ 2
+ 3
+
+ )
+ .toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+ it("with align", () => {
+ const tree = renderer
+ .create(
+
+ 1
+ 2
+ 3
+
+ )
+ .toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+ it("with gap", () => {
+ const tree = renderer
+ .create(
+
+ 1
+ 2
+ 3
+
+ )
+ .toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+ it("with wrap", () => {
+ const tree = renderer
+ .create(
+
+ 1
+ 2
+ 3
+
+ )
+ .toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+ });
+ describe("Vertical display", () => {
+ it("with children", () => {
+ const tree = renderer
+ .create(
+
+ 1
+ 2
+ 3
+
+ )
+ .toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("with justify", () => {
+ const tree = renderer
+ .create(
+
+ 1
+ 2
+ 3
+
+ )
+ .toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+ it("with align", () => {
+ const tree = renderer
+ .create(
+
+ 1
+ 2
+ 3
+
+ )
+ .toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+ it("with wrap", () => {
+ const tree = renderer
+ .create(
+
+ 1
+ 2
+ 3
+
+ )
+ .toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+ });
+});
diff --git a/src/components/index.js b/src/components/index.js
index 182b574f82..128149edfb 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -73,3 +73,4 @@ export { default as AccordionItem } from "./Accordion/AccordionItem/AccordionIte
export { default as Clickable } from "./Clickable/Clickable";
export { default as ColorUtils } from "../utils/colors-utils";
export { default as IconButton } from "./IconButton/IconButton";
+export { default as Flex } from "./Flex/Flex";
diff --git a/src/constants/positions.js b/src/constants/positions.js
new file mode 100644
index 0000000000..210b2dd4be
--- /dev/null
+++ b/src/constants/positions.js
@@ -0,0 +1,5 @@
+export const BASE_POSITIONS = {
+ START: "Start",
+ CENTER: "Center",
+ END: "End"
+};
diff --git a/src/constants/sizes.js b/src/constants/sizes.js
index 4b1dd73d7f..92a5f3668e 100644
--- a/src/constants/sizes.js
+++ b/src/constants/sizes.js
@@ -1,9 +1,11 @@
+export const PASCAL_BASE_SIZE = Object.freeze({ SMALL: "Small", MEDIUM: "Medium", LARGE: "Large" });
+
+export const BASE_SIZES = Object.freeze({ SMALL: "small", MEDIUM: "medium", LARGE: "large" });
+
export const SIZES = Object.freeze({
XXS: "xxs",
XS: "xs",
- SMALL: "small",
- MEDIUM: "medium",
- LARGE: "large"
+ ...BASE_SIZES
});
export const DialogPositions = Object.freeze({
diff --git a/src/storybook/components/related-components/component-description-map.js b/src/storybook/components/related-components/component-description-map.js
index 24a227890d..d0bffb8a78 100644
--- a/src/storybook/components/related-components/component-description-map.js
+++ b/src/storybook/components/related-components/component-description-map.js
@@ -36,6 +36,7 @@ import { IconButtonDescription } from "./descriptions/icon-button-description";
import { MenuButtonDescription } from "./descriptions/menu-button-description";
import { ClickableDescription } from "./descriptions/clickable-description/clickable-description";
import { HiddenTextDescription } from "./descriptions/hidden-text-description";
+import { ListDescription } from "./descriptions/list";
export const SPLIT_BUTTON = "split-button";
export const BUTTON_GROUP = "button-group";
@@ -71,6 +72,7 @@ export const ICON_BUTTON = "icon-button";
export const MENU_BUTTON = "menu-button";
export const CLICKABLE = "clickable";
export const HIDDEN_TEXT = "hidden-text-description";
+export const LIST = "list";
// General description names (not related to specific components)
export const COLORS = "colors";
@@ -114,6 +116,7 @@ descriptionTypesMap.set(EDITABLE_HEADING, );
descriptionTypesMap.set(HEADING, );
descriptionTypesMap.set(CLICKABLE, );
descriptionTypesMap.set(HIDDEN_TEXT, );
+descriptionTypesMap.set(LIST, );
// General description (not related to specific components)
descriptionTypesMap.set(COLORS, );
diff --git a/src/storybook/components/related-components/descriptions/list.jsx b/src/storybook/components/related-components/descriptions/list.jsx
new file mode 100644
index 0000000000..16bf01aeb0
--- /dev/null
+++ b/src/storybook/components/related-components/descriptions/list.jsx
@@ -0,0 +1,29 @@
+import { useMemo } from "react";
+import { RelatedComponent } from "../../related-component/related-component";
+import DialogContentContainer from "../../../../components/DialogContentContainer/DialogContentContainer";
+import ListItem from "../../../../components/ListItem/ListItem";
+import List from "../../../../components/List/List";
+
+export const ListDescription = () => {
+ const component = useMemo(() => {
+ return (
+
+
+
+ List item 1
+ List item 2
+ List item 3
+
+
+
+ );
+ }, []);
+ return (
+
+ );
+};
diff --git a/src/storybook/components/story-description/story-description.jsx b/src/storybook/components/story-description/story-description.jsx
new file mode 100644
index 0000000000..bb76d07d96
--- /dev/null
+++ b/src/storybook/components/story-description/story-description.jsx
@@ -0,0 +1,34 @@
+import { useMemo } from "react";
+import cx from "classnames";
+import PropTypes from "prop-types";
+import Flex from "../../../components/Flex/Flex";
+import classes from "./story-description.module.scss";
+
+export const StoryDescription = ({ description, children, vertical }) => {
+ const direction = useMemo(() => (vertical ? Flex.directions.COLUMN : Flex.directions.ROW), [vertical]);
+ return (
+
+
+ {description}
+
+ {children}
+
+ );
+};
+
+StoryDescription.propTypes = {
+ description: PropTypes.string,
+ children: PropTypes.element,
+ vertical: PropTypes.bool
+};
+
+StoryDescription.defaultProps = {
+ description: "",
+ children: null,
+ vertical: false
+};
diff --git a/src/storybook/components/story-description/story-description.module.scss b/src/storybook/components/story-description/story-description.module.scss
new file mode 100644
index 0000000000..f1646f2334
--- /dev/null
+++ b/src/storybook/components/story-description/story-description.module.scss
@@ -0,0 +1,7 @@
+.description {
+ font-weight: 500;
+
+ &.vertical {
+ text-align: center;
+ }
+}
\ No newline at end of file