Skip to content

Commit 28f2044

Browse files
authored
Datc 19 add flexbox abstraction (#45)
- migrated flex component - added better isolated tests - added storybook doc - added theme types / remodeled others
1 parent a3c8842 commit 28f2044

34 files changed

+1484
-99
lines changed

.vscode/settings.json

+10-1
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,20 @@
1111
"workbench.editor.customLabels.enabled": true,
1212
"workbench.editor.customLabels.patterns": {
1313
"**/config/ci/**/caller.mk": "${dirname}.caller",
14-
"**/config/ci/**/runner.ts": "${dirname}.runner"
14+
"**/config/ci/**/runner.ts": "${dirname}.runner",
15+
// react-func pattern
16+
"**/src/**/func.tsx": "${dirname(-3)}.${dirname}.func",
17+
"**/src/**/styles.ts": "${dirname}.styles",
18+
"**/src/**/stories.{ts,tsx}": "${dirname}.stories",
19+
"**/src/**/index.ts": "${dirname}.module",
20+
"**/src/**/constants.ts": "${dirname}.constants",
21+
"**/src/**/tests.tsx": "${dirname}.tests",
22+
"**/src/**/types.ts": "${dirname}.types"
1523
},
1624

1725
"material-icon-theme.files.associations": {
1826
"stories.ts": "Storybook",
27+
"stories.tsx": "Storybook",
1928
"tests.tsx": "Test-jsx",
2029
"styles.ts": "Css",
2130
"func.tsx": "SVGr",

apps/frontend/admin/src/App.tsx

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import { ThemeProvider } from "@lib-theme";
2-
import { Button } from "@lib-components";
32

43
export default function App() {
5-
return (
6-
<ThemeProvider>
7-
<Button appearance="primary"> Hello ADMIN </Button>
8-
</ThemeProvider>
9-
);
4+
return <ThemeProvider>Hello ADMIN</ThemeProvider>;
105
}

apps/frontend/ui/src/App.tsx

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import { ThemeProvider } from "@lib-theme";
2-
import { Button } from "@lib-components";
32

43
export default function App() {
5-
return (
6-
<ThemeProvider>
7-
<Button appearance="primary"> Hello My UI APP </Button>
8-
</ThemeProvider>
9-
);
4+
return <ThemeProvider>Hello My UI APP</ThemeProvider>;
105
}

libs/components/src/atoms/index.ts

-1
This file was deleted.

libs/components/src/defaults.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
propsValues: {
3+
undefinedClassString: "$$!!NoClassString!!$$",
4+
undefinedDataTestId: "$$!!NoDataTestId!!$$",
5+
},
6+
};

libs/components/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { Button } from "@lib-components/atoms";
1+
export { Flex } from "@lib-components/layout";
+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import type { ReactNode } from "react";
2+
3+
import constants from "@lib-components/defaults";
4+
5+
import { mergeClasses } from "@lib-theme";
6+
import type { TThemeSpacing, TThemeShorthandSpacing } from "@lib-theme";
7+
8+
import {
9+
useGap,
10+
useFlexBox,
11+
useMargin,
12+
usePadding,
13+
useShorthandDimension,
14+
} from "@lib-components/layout/Flex/hooks";
15+
16+
import type {
17+
TFlexDirection,
18+
TFlexOption,
19+
TFlexWrap,
20+
TFlexShorthandDimensions,
21+
} from "@lib-components/layout/Flex/types";
22+
23+
type TProps = {
24+
children: ReactNode;
25+
direction?: TFlexDirection;
26+
justifyContent?: TFlexOption;
27+
alignItems?: TFlexOption;
28+
wrap?: TFlexWrap;
29+
className?: string;
30+
gap?: TThemeSpacing;
31+
margin?: TThemeShorthandSpacing;
32+
padding?: TThemeShorthandSpacing;
33+
testId?: string;
34+
shWidth?: TFlexShorthandDimensions;
35+
shHeight?: TFlexShorthandDimensions;
36+
};
37+
38+
/**
39+
* @description
40+
* - fluent does not provide a `Flex` component for consistent layout (it was removed in the latest version)
41+
* - to have a straight forward implementation of `Flex` component, we have created this layout component
42+
* - having this allows to use fewer makeStyles call and repeting flex configurations in the code
43+
* - its especially usefull when certain layout styles have to be applied conditionally
44+
* - for this the entire conditional logic is abstracted inside this component, providing very much styled-component like ergonomics
45+
*
46+
*
47+
* @props
48+
* - `direction`: flex-direction property
49+
* - `justifyContent`: justify-content property
50+
* - `alignItems`: align-items property
51+
* - `wrap`: flex-wrap property
52+
* - `className`: to add additional classes to the component, will override all specified styles from props
53+
* - `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)
54+
* - `margin`: margin property, using the same values like gap, expects the shorthand notation
55+
* - `padding`: same like margin, but for padding
56+
*
57+
* ```
58+
* // the shorthand is not a simple string, but rather defined as an array that can be of size 1 up to 4
59+
* // each element provides additional restraint from the design system tokens
60+
* // following examples will only use padding, but the same applies to margin
61+
* // the * between tokens.spacing and the further specifier can be interpreted as either horizontal or vertical (both the same values, reasoning is explained in gap comment above)
62+
*
63+
* <Flex padding={["S"]} /> // like saying padding: tokens.spacing*S;
64+
* <Flex padding={["S", "M"]} /> // like saying padding: `${tokens.spacing*S} ${tokens.spacing*M}`;
65+
* <Flex padding={["S", "M", "L"]} /> // like saying padding: `${tokens.spacing*S} ${tokens.spacing*M} ${tokens.spacing*L}`;
66+
* <Flex padding={["S", "M", "L", "XL"]} /> // like saying padding: `${tokens.spacing*S} ${tokens.spacing*M} ${tokens.spacing*L} ${tokens.spacing*XL}`;
67+
*
68+
* ```
69+
* - `testId`: passed down the data-testid attribute
70+
* - `shWidth`: shorthand for width property
71+
* - `shHeight`: shorthand for height property
72+
*
73+
* @default
74+
* direction = "row", justifyContent = "start", alignItems = "start", wrap = "nowrap", gap = "None", margin = ["None"], padding = ["None"], className = "NoClass"
75+
*/
76+
export default function Flex({
77+
direction = "row",
78+
justifyContent = "start",
79+
alignItems = "start",
80+
wrap = "nowrap",
81+
gap = "None",
82+
margin = ["None"],
83+
padding = ["None"],
84+
shHeight = "auto",
85+
shWidth = "auto",
86+
className = constants.propsValues.undefinedClassString,
87+
testId = constants.propsValues.undefinedDataTestId,
88+
children,
89+
}: TProps) {
90+
const flexBoxClass = useFlexBox(justifyContent, alignItems, direction, wrap);
91+
const gapClass = useGap(gap);
92+
const marginClass = useMargin(margin);
93+
const paddingClass = usePadding(padding);
94+
const dimensionClass = useShorthandDimension(shWidth, shHeight);
95+
96+
return (
97+
<div
98+
data-testid={testId}
99+
className={mergeClasses(
100+
flexBoxClass,
101+
gapClass,
102+
marginClass,
103+
paddingClass,
104+
dimensionClass,
105+
className,
106+
)}
107+
>
108+
{children}
109+
</div>
110+
);
111+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import useGap from "@lib-components/layout/Flex/hooks/useGap";
2+
import useMargin from "@lib-components/layout/Flex/hooks/useMargin";
3+
import usePadding from "@lib-components/layout/Flex/hooks/usePadding";
4+
import useFlexBox from "@lib-components/layout/Flex/hooks/useFlexBox";
5+
import useShorthandDimension from "@lib-components/layout/Flex/hooks/useShorthandDimension";
6+
7+
export { useGap, useMargin, usePadding, useFlexBox, useShorthandDimension };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { mergeClasses } from "@lib-theme";
2+
3+
import { useFlexBoxClasses } from "@lib-components/layout/Flex/styles";
4+
5+
import type {
6+
TFlexDirection,
7+
TFlexOption,
8+
TFlexWrap,
9+
} from "@lib-components/layout/Flex/types";
10+
11+
export default function useFlexBox(
12+
justifyContent?: TFlexOption,
13+
alignItems?: TFlexOption,
14+
direction?: TFlexDirection,
15+
wrap?: TFlexWrap,
16+
) {
17+
const classes = useFlexBoxClasses();
18+
const directionClass = direction
19+
? classes[`${direction}Direction`]
20+
: undefined;
21+
const justifyContentClass = justifyContent
22+
? classes[`${justifyContent}Content`]
23+
: undefined;
24+
const alignItemsClass = alignItems
25+
? classes[`${alignItems}Items`]
26+
: undefined;
27+
const wrapClass = wrap ? classes[wrap] : undefined;
28+
29+
return mergeClasses(
30+
classes.base,
31+
directionClass,
32+
justifyContentClass,
33+
alignItemsClass,
34+
wrapClass,
35+
);
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { useGapClasses } from "@lib-components/layout/Flex/styles";
2+
import type { TThemeSpacing } from "@lib-theme";
3+
4+
function useGap(gap?: TThemeSpacing) {
5+
const classes = useGapClasses();
6+
return gap ? classes[`gap${gap}`] : undefined;
7+
}
8+
9+
export default useGap;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useMarginClasses } from "@lib-components/layout/Flex/styles";
2+
import { mergeClasses } from "@lib-theme";
3+
4+
import type { TThemeShorthandSpacing } from "@lib-theme";
5+
6+
function useMargin(margin?: TThemeShorthandSpacing) {
7+
const classes = useMarginClasses();
8+
9+
if (margin === undefined) {
10+
return "noMarginValue";
11+
}
12+
if (margin.length === 1) {
13+
return classes[`margin${margin[0]}`];
14+
}
15+
if (margin.length === 2) {
16+
return mergeClasses(
17+
classes[`marginTop${margin[0]}`],
18+
classes[`marginRight${margin[1]}`],
19+
classes[`marginBottom${margin[0]}`],
20+
classes[`marginLeft${margin[1]}`],
21+
);
22+
}
23+
if (margin.length === 3) {
24+
return mergeClasses(
25+
classes[`marginTop${margin[0]}`],
26+
classes[`marginRight${margin[1]}`],
27+
classes[`marginBottom${margin[2]}`],
28+
classes[`marginLeft${margin[1]}`],
29+
);
30+
}
31+
return mergeClasses(
32+
classes[`marginTop${margin[0]}`],
33+
classes[`marginRight${margin[1]}`],
34+
classes[`marginBottom${margin[2]}`],
35+
classes[`marginLeft${margin[3]}`],
36+
);
37+
}
38+
39+
export default useMargin;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { usePaddingClasses } from "@lib-components/layout/Flex/styles";
2+
import { mergeClasses } from "@lib-theme";
3+
4+
import type { TThemeShorthandSpacing } from "@lib-theme";
5+
6+
function usePadding(padding?: TThemeShorthandSpacing) {
7+
const classes = usePaddingClasses();
8+
9+
if (padding === undefined) {
10+
return "noPaddingValue";
11+
}
12+
if (padding.length === 1) {
13+
return classes[`padding${padding[0]}`];
14+
}
15+
if (padding.length === 2) {
16+
return mergeClasses(
17+
classes[`paddingTop${padding[0]}`],
18+
classes[`paddingRight${padding[1]}`],
19+
classes[`paddingBottom${padding[0]}`],
20+
classes[`paddingLeft${padding[1]}`],
21+
);
22+
}
23+
if (padding.length === 3) {
24+
return mergeClasses(
25+
classes[`paddingTop${padding[0]}`],
26+
classes[`paddingRight${padding[1]}`],
27+
classes[`paddingBottom${padding[2]}`],
28+
classes[`paddingLeft${padding[1]}`],
29+
);
30+
}
31+
return mergeClasses(
32+
classes[`paddingTop${padding[0]}`],
33+
classes[`paddingRight${padding[1]}`],
34+
classes[`paddingBottom${padding[2]}`],
35+
classes[`paddingLeft${padding[3]}`],
36+
);
37+
}
38+
39+
export default usePadding;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { mergeClasses } from "@lib-theme";
2+
3+
import { useDimensionClasses } from "@lib-components/layout/Flex/styles";
4+
5+
import type { TFlexShorthandDimensions } from "@lib-components/layout/Flex/types";
6+
7+
export default function useShorthandDimension(
8+
shorthandWidth: TFlexShorthandDimensions,
9+
shorthandHeight: TFlexShorthandDimensions,
10+
) {
11+
const classes = useDimensionClasses();
12+
13+
const widthClass = classes[`${shorthandWidth}Width`];
14+
const heightClass = classes[`${shorthandHeight}Height`];
15+
16+
return mergeClasses(widthClass, heightClass);
17+
}
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Flex from "@lib-components/layout/Flex/func";
2+
3+
export default Flex;

0 commit comments

Comments
 (0)