diff --git a/src/components/layout/Flex/component.tsx b/src/components/layout/Flex/func.tsx similarity index 75% rename from src/components/layout/Flex/component.tsx rename to src/components/layout/Flex/func.tsx index dac19ca..e68592e 100644 --- a/src/components/layout/Flex/component.tsx +++ b/src/components/layout/Flex/func.tsx @@ -16,7 +16,12 @@ import { import type { TFlexDirection, - TFlexOption, + TFlexOptionContent, + TFlexOptionItems, + TFlexWrap, + TFlexGrow, + TFlexShrink, + TFlexBasis, TFlexShorthandDimensions, TFlexPosition, } from "@components/layout/Flex/types"; @@ -25,9 +30,14 @@ type TProps = { children: ReactNode; position?: TFlexPosition; direction?: TFlexDirection; - justifyContent?: TFlexOption; - alignItems?: TFlexOption; - wrap?: boolean; + justifyContent?: TFlexOptionContent; + alignItems?: TFlexOptionItems; + alignContent?: TFlexOptionContent; + alignSelf?: TFlexOptionItems; + grow?: TFlexGrow; + noShrink?: TFlexShrink; + basis?: TFlexBasis; + wrap?: TFlexWrap; className?: string; gap?: TThemeSpacing; margin?: TThemeShorthandSpacing; @@ -49,8 +59,14 @@ type TProps = { * - `direction`: flex-direction property * - `justifyContent`: justify-content property * - `alignItems`: align-items property + * - `alignContent`: align-content property + * - `alignSelf`: align-self property * - `wrap`: flex-wrap property * - `gap`: gap between children, with fixed predefined values from the design system, not discriminating between horizontal and vertical gap (because there are literally the same values) + * - `grow`: shorthand for flex-grow property + * - `noShrink`: shorthand for flex-shrink property, noShrik is the same as flex-shrink: 0 + * - `basis`: shorthand for flex-basis property, only global values are supported, for specific values use className prop + * - `position`: position property, supports all global values * - `margin`: margin property, using the same values like gap, expects the shorthand notation * - `padding`: same like margin, but for padding, concrete example below * @@ -74,15 +90,33 @@ type TProps = { * * * @default - * direction = "row", justifyContent = "start", alignItems = "start", wrap = false, gap = "None", margin = ["None"], padding = ["None"], shHeight = "auto", shWidth = "auto" + * direction = "row", + * position = "static", + * justifyContent = "start", + * alignItems = "stretch", + * alignContent = "stretch", + * alignSelf = "auto", + * grow = false, + * noShrink = false, + * wrap = false, + * gap = "None", + * margin = ["None"], + * padding = ["None"], + * shHeight = "auto", + * shWidth = "auto" */ export default function Flex({ direction = "row", position = "static", justifyContent = "start", - alignItems = "start", + alignContent = "stretch", + alignItems = "stretch", + alignSelf = "auto", wrap = false, gap = "None", + grow = false, + noShrink = false, + basis = "auto", margin = ["None"], padding = ["None"], shHeight = "auto", @@ -92,7 +126,17 @@ export default function Flex({ children, ...rest }: TProps) { - const flexBoxClass = useFlexBox(justifyContent, alignItems, direction, wrap); + const flexBoxClass = useFlexBox( + justifyContent, + alignContent, + alignItems, + alignSelf, + direction, + wrap, + grow, + noShrink, + basis, + ); const gapClass = useGap(gap); const marginClass = useMargin(margin); const paddingClass = usePadding(padding); diff --git a/src/components/layout/Flex/hooks/useFlexBox.ts b/src/components/layout/Flex/hooks/useFlexBox.ts index 8cebc82..d7b85a7 100644 --- a/src/components/layout/Flex/hooks/useFlexBox.ts +++ b/src/components/layout/Flex/hooks/useFlexBox.ts @@ -4,33 +4,65 @@ import { useFlexBoxClasses } from "@components/layout/Flex/styles"; import type { TFlexDirection, - TFlexOption, + TFlexOptionContent, + TFlexOptionItems, + TFlexShrink, + TFlexGrow, + TFlexBasis, + TFlexWrap, } from "@components/layout/Flex/types"; export default function useFlexBox( - justifyContent?: TFlexOption, - alignItems?: TFlexOption, + justifyContent?: TFlexOptionContent, + alignContent?: TFlexOptionContent, + alignItems?: TFlexOptionItems, + alignSelf?: TFlexOptionItems, direction?: TFlexDirection, - wrap?: boolean, + wrap?: TFlexWrap, + grow?: TFlexGrow, + noShrink?: TFlexShrink, + basis?: TFlexBasis, ) { const classes = useFlexBoxClasses(); + const directionClass = direction ? classes[`${direction}Direction`] : undefined; + const justifyContentClass = justifyContent - ? classes[`${justifyContent}Content`] + ? classes[`${justifyContent}JustifyContent`] + : undefined; + + const alignContentClass = alignContent + ? classes[`${alignContent}AlignContent`] : undefined; + const alignItemsClass = alignItems - ? classes[`${alignItems}Items`] + ? classes[`${alignItems}AlignItems`] + : undefined; + + const alignSelfClass = alignSelf + ? classes[`${alignSelf}AlignSelf`] : undefined; - const wrapClass = wrap ? classes.wrap : classes.nowrap; + const basisClass = basis ? classes[`${basis}Basis`] : undefined; + + const growClass = grow ? classes.growOne : classes.growZero; + const shrinkClass = noShrink ? classes.shrinkZero : classes.shrinkOne; + const wrapClass = wrap + ? (wrap === "reverse" && classes.wrapReverse) || classes.wrap + : classes.nowrap; return mergeClasses( classes.base, directionClass, justifyContentClass, + alignContentClass, alignItemsClass, + alignSelfClass, + growClass, + shrinkClass, + basisClass, wrapClass, ); } diff --git a/src/components/layout/Flex/index.ts b/src/components/layout/Flex/index.ts index e26aa32..b101107 100644 --- a/src/components/layout/Flex/index.ts +++ b/src/components/layout/Flex/index.ts @@ -1,3 +1,3 @@ -import Flex from "@components/layout/Flex/component"; +import Flex from "@components/layout/Flex/func"; export default Flex; diff --git a/src/components/layout/Flex/styles/flexBox.ts b/src/components/layout/Flex/styles/flexBox.ts index 272f5a3..bae5fb7 100644 --- a/src/components/layout/Flex/styles/flexBox.ts +++ b/src/components/layout/Flex/styles/flexBox.ts @@ -10,49 +10,97 @@ const useFlexBoxClasses = makeStyles({ columnDirection: { flexDirection: "column", }, + rowReverseDirection: { + flexDirection: "row-reverse", + }, + columnReverseDirection: { + flexDirection: "column-reverse", + }, + // justify-content - centerContent: { + centerJustifyContent: { justifyContent: "center", }, - startContent: { + startJustifyContent: { justifyContent: "flex-start", }, - endContent: { + endJustifyContent: { justifyContent: "flex-end", }, - spaceBetweenContent: { + spaceBetweenJustifyContent: { justifyContent: "space-between", }, - spaceAroundContent: { + spaceAroundJustifyContent: { justifyContent: "space-around", }, - spaceEvenlyContent: { + spaceEvenlyJustifyContent: { justifyContent: "space-evenly", }, - stretchContent: { + stretchJustifyContent: { justifyContent: "stretch", }, + // align-items - centerItems: { + autoAlignItems: { + alignItems: "auto", + }, + centerAlignItems: { alignItems: "center", }, - startItems: { + startAlignItems: { alignItems: "flex-start", }, - endItems: { + endAlignItems: { alignItems: "flex-end", }, - stretchItems: { + stretchAlignItems: { alignItems: "stretch", }, - spaceBetweenItems: { - alignItems: "space-between", + baselineAlignItems: { + alignItems: "baseline", + }, + + // align-self + autoAlignSelf: { + alignSelf: "auto", + }, + startAlignSelf: { + alignSelf: "flex-start", + }, + endAlignSelf: { + alignSelf: "flex-end", }, - spaceAroundItems: { - alignItems: "space-around", + centerAlignSelf: { + alignSelf: "center", }, - spaceEvenlyItems: { - alignItems: "space-evenly", + stretchAlignSelf: { + alignSelf: "stretch", + }, + baselineAlignSelf: { + alignSelf: "baseline", + }, + + // align-content + centerAlignContent: { + alignContent: "center", + }, + startAlignContent: { + alignContent: "flex-start", + }, + endAlignContent: { + alignContent: "flex-end", + }, + stretchAlignContent: { + alignContent: "stretch", + }, + spaceBetweenAlignContent: { + alignContent: "space-between", + }, + spaceAroundAlignContent: { + alignContent: "space-around", + }, + spaceEvenlyAlignContent: { + alignContent: "space-evenly", }, // wrap @@ -62,6 +110,48 @@ const useFlexBoxClasses = makeStyles({ nowrap: { flexWrap: "nowrap", }, + wrapReverse: { + flexWrap: "wrap-reverse", + }, + + // grow + growOne: { + flexGrow: 1, + }, + growZero: { + flexGrow: 0, + }, + + // shrink + shrinkOne: { + flexShrink: 1, + }, + shrinkZero: { + flexShrink: 0, + }, + + // basis + autoBasis: { + flexBasis: "auto", + }, + "0Basis": { + flexBasis: 0, + }, + fillBasis: { + flexBasis: "fill", + }, + maxContentBasis: { + flexBasis: "max-content", + }, + minContentBasis: { + flexBasis: "min-content", + }, + fitContentBasis: { + flexBasis: "fit-content", + }, + contentBasis: { + flexBasis: "content", + }, }); export default useFlexBoxClasses; diff --git a/src/components/layout/Flex/tests.tsx b/src/components/layout/Flex/tests.tsx index 0be08c1..74a288f 100644 --- a/src/components/layout/Flex/tests.tsx +++ b/src/components/layout/Flex/tests.tsx @@ -8,14 +8,19 @@ describe("Flex", () => { it("should render without required props default config", () => { render(FlexChild); const FlexElement = screen.getByText("FlexChild"); - expect(FlexElement).toBeInTheDocument(); + expect(FlexElement).toHaveTextContent("FlexChild"); expect(FlexElement).toHaveStyle("display: flex"); expect(FlexElement).toHaveStyle("flex-direction: row"); expect(FlexElement).toHaveStyle("justify-content: flex-start"); - expect(FlexElement).toHaveStyle("align-items: flex-start"); + expect(FlexElement).toHaveStyle("align-items: stretch"); + expect(FlexElement).toHaveStyle("align-content: stretch"); + expect(FlexElement).toHaveStyle("align-self: auto"); + expect(FlexElement).toHaveStyle("flex-grow: 0"); + expect(FlexElement).toHaveStyle("flex-shrink: 1"); expect(FlexElement).toHaveStyle("flex-wrap: nowrap"); + expect(FlexElement).toHaveStyle("flex-basis: auto"); expect(FlexElement).toHaveStyle("gap: 0rem"); expect(FlexElement).toHaveStyle("margin: 0rem"); expect(FlexElement).toHaveStyle("padding: 0rem"); @@ -35,6 +40,16 @@ describe("Flex", () => { const FlexElement = screen.getByText("FlexChild"); expect(FlexElement).toHaveStyle("flex-direction: row"); }); + it("should render with direction rowReverse", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-direction: row-reverse"); + }); + it("should render with direction columnReverse", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-direction: column-reverse"); + }); }); describe("for justifyContent", () => { it("should render with justifyContent center", () => { @@ -68,6 +83,38 @@ describe("Flex", () => { expect(FlexElement).toHaveStyle("justify-content: stretch"); }); }); + describe("for alignContent", () => { + it("should render with alignContent center", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("align-content: center"); + }); + it("should render with alignContent end", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("align-content: flex-end"); + }); + it("should render with alignContent spaceBetween", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("align-content: space-between"); + }); + it("should render with alignContent spaceAround", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("align-content: space-around"); + }); + it("should render with alignContent spaceEvenly", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("align-content: space-evenly"); + }); + it("should render with alignContent stretch", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("align-content: stretch"); + }); + }); describe("for alignItems", () => { it("should render with alignItems center", () => { render(FlexChild); @@ -79,38 +126,55 @@ describe("Flex", () => { const FlexElement = screen.getByText("FlexChild"); expect(FlexElement).toHaveStyle("align-items: flex-end"); }); - it("should render with alignItems spaceBetween", () => { - render(FlexChild); + it("should render with alignItems stretch", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("align-items: stretch"); + }); + it("should render with alignItems baseline", () => { + render(FlexChild); const FlexElement = screen.getByText("FlexChild"); - expect(FlexElement).toHaveStyle("align-items: space-between"); + expect(FlexElement).toHaveStyle("align-items: baseline"); }); - it("should render with alignItems spaceAround", () => { - render(FlexChild); + }); + describe("for alignSelf", () => { + it("should render with alignSelf center", () => { + render(FlexChild); const FlexElement = screen.getByText("FlexChild"); - expect(FlexElement).toHaveStyle("align-items: space-around"); + expect(FlexElement).toHaveStyle("align-self: center"); }); - it("should render with alignItems spaceEvenly", () => { - render(FlexChild); + it("should render with alignSelf end", () => { + render(FlexChild); const FlexElement = screen.getByText("FlexChild"); - expect(FlexElement).toHaveStyle("align-items: space-evenly"); + expect(FlexElement).toHaveStyle("align-self: flex-end"); }); - it("should render with alignItems stretch", () => { - render(FlexChild); + it("should render with alignSelf stretch", () => { + render(FlexChild); const FlexElement = screen.getByText("FlexChild"); - expect(FlexElement).toHaveStyle("align-items: stretch"); + expect(FlexElement).toHaveStyle("align-self: stretch"); + }); + it("should render with alignSelf baseline", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("align-self: baseline"); }); }); describe("for wrap", () => { - it("should render with wrap wrap", () => { + it("should render with wrap: wrap", () => { render(FlexChild); const FlexElement = screen.getByText("FlexChild"); expect(FlexElement).toHaveStyle("flex-wrap: wrap"); }); - it("should render with wrap nowrap", () => { + it("should render with wrap: nowrap", () => { render(FlexChild); const FlexElement = screen.getByText("FlexChild"); expect(FlexElement).toHaveStyle("flex-wrap: nowrap"); }); + it("should render with wrap: reverse", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-wrap: wrap-reverse"); + }); }); describe("for gap", () => { it("should render with gap None", () => { @@ -169,6 +233,67 @@ describe("Flex", () => { expect(FlexElement).toHaveStyle("gap: 2rem"); }); }); + describe("for grow", () => { + it("should render with grow true", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-grow: 1"); + }); + it("should render with grow false", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-grow: 0"); + }); + }); + describe("for noShrink", () => { + it("should render with noShrink true", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-shrink: 0"); + }); + it("should render with noShrink false", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-shrink: 1"); + }); + }); + describe("for basis", () => { + it("should render with basis auto", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-basis: auto"); + }); + it("should render with basis fill", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-basis: fill"); + }); + it("should render with basis maxContent", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-basis: max-content"); + }); + it("should render with basis minContent", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-basis: min-content"); + }); + it("should render with basis fitContent", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-basis: fit-content"); + }); + it("should render with basis content", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-basis: content"); + }); + it("should render with basis 0", () => { + render(FlexChild); + const FlexElement = screen.getByText("FlexChild"); + expect(FlexElement).toHaveStyle("flex-basis: 0"); + }); + }); describe("for margin", () => { it("should render with margin None", () => { render(FlexChild); diff --git a/src/components/layout/Flex/types.ts b/src/components/layout/Flex/types.ts index 14334d5..a5843bf 100644 --- a/src/components/layout/Flex/types.ts +++ b/src/components/layout/Flex/types.ts @@ -1,6 +1,6 @@ -export type TFlexDirection = "row" | "column"; +export type TFlexDirection = "row" | "column" | "rowReverse" | "columnReverse"; -export type TFlexOption = +export type TFlexOptionContent = | "start" | "center" | "end" @@ -9,8 +9,28 @@ export type TFlexOption = | "spaceEvenly" | "stretch"; -export type TFlexWrap = "wrap" | "nowrap"; +export type TFlexOptionItems = + | "start" + | "center" + | "end" + | "stretch" + | "baseline" + | "auto"; + +export type TFlexGrow = boolean; + +export type TFlexShrink = boolean; + +export type TFlexWrap = boolean | "reverse"; +export type TFlexBasis = + | "auto" + | "fill" + | "maxContent" + | "minContent" + | "fitContent" + | "content" + | "0"; export type TFlexShorthandDimensions = "25%" | "50%" | "75%" | "100%" | "auto"; export type TFlexPosition =