-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add Flex shim and flex item mixins (#26997)
* cfeat: add Flex shim and flex item mixins * chore: add api * chore: remove lodash * chore: optional chaining
- Loading branch information
Showing
10 changed files
with
572 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
91 changes: 91 additions & 0 deletions
91
packages/react-components/react-migration-v0-v9/src/components/Flex/Flex.styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { makeStyles, shorthands } from '@fluentui/react-components'; | ||
|
||
const gapValues = { | ||
smaller: '8px', | ||
small: '10px', | ||
medium: '15px', | ||
large: '30px', | ||
}; | ||
|
||
const paddingValues = { | ||
medium: '10px', | ||
}; | ||
|
||
export const useFlexStyles = makeStyles({ | ||
flex: { | ||
display: 'flex', | ||
}, | ||
inline: { | ||
display: 'inline-flex', | ||
}, | ||
column: { | ||
flexDirection: 'column', | ||
}, | ||
alignItemsFlexStart: { | ||
alignItems: 'flex-start', | ||
}, | ||
alignItemsCenter: { | ||
alignItems: 'center', | ||
}, | ||
alignItemsFlexEnd: { | ||
alignItems: 'flex-end', | ||
}, | ||
alignItemsStretch: { | ||
alignItems: 'stretch', | ||
}, | ||
justifyContentFlexStart: { | ||
justifyContent: 'flex-start', | ||
}, | ||
justifyContentCenter: { | ||
justifyContent: 'center', | ||
}, | ||
justifyContentFlexEnd: { | ||
justifyContent: 'flex-end', | ||
}, | ||
justifyContentStretch: { | ||
justifyContent: 'stretch', | ||
}, | ||
justifyContentSpaceAround: { | ||
justifyContent: 'space-around', | ||
}, | ||
justifyContentSpaceBetween: { | ||
justifyContent: 'space-between', | ||
}, | ||
justifyContentSpaceEvenly: { | ||
justifyContent: 'space-evenly', | ||
}, | ||
wrap: { | ||
flexWrap: 'wrap', | ||
}, | ||
fill: { | ||
width: '100%', | ||
height: '100%', | ||
}, | ||
gapForColumnFlexSmall: { | ||
rowGap: gapValues.small, | ||
}, | ||
gapForColumnFlexSmaller: { | ||
rowGap: gapValues.smaller, | ||
}, | ||
gapForColumnFlexMedium: { | ||
rowGap: gapValues.medium, | ||
}, | ||
gapForColumnFlexLarge: { | ||
rowGap: gapValues.large, | ||
}, | ||
gapForRowFlexSmall: { | ||
columnGap: gapValues.small, | ||
}, | ||
gapForRowFlexSmaller: { | ||
columnGap: gapValues.smaller, | ||
}, | ||
gapForRowFlexMedium: { | ||
columnGap: gapValues.medium, | ||
}, | ||
gapForRowFlexLarge: { | ||
columnGap: gapValues.large, | ||
}, | ||
paddingMedium: { | ||
...shorthands.padding(paddingValues.medium), | ||
}, | ||
}); |
136 changes: 136 additions & 0 deletions
136
packages/react-components/react-migration-v0-v9/src/components/Flex/Flex.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import '@testing-library/jest-dom'; | ||
|
||
import { isConformant } from '@fluentui/react-conformance'; | ||
import { render } from '@testing-library/react'; | ||
import * as React from 'react'; | ||
|
||
import { Flex } from './Flex'; | ||
|
||
describe('Flex', () => { | ||
isConformant({ | ||
Component: Flex, | ||
componentPath: module!.filename.replace('.test', ''), | ||
displayName: 'Flex', | ||
disabledTests: ['has-docblock', 'has-top-level-file', 'component-has-static-classnames-object'], | ||
}); | ||
|
||
it('renders a default state', () => { | ||
const { getByText } = render(<Flex>Test</Flex>); | ||
const textElement = getByText('Test'); | ||
expect(textElement.nodeName).toBe('DIV'); | ||
expect(textElement).toHaveStyle(` | ||
display: flex | ||
`); | ||
}); | ||
|
||
it('applies the column style', () => { | ||
const { getByText } = render(<Flex column={true}>Test</Flex>); | ||
|
||
const textElement = getByText('Test'); | ||
expect(textElement).toHaveStyle(` | ||
flex-direction: column | ||
`); | ||
}); | ||
|
||
it('applies the fill style', () => { | ||
const { getByText } = render(<Flex fill={true}>Test</Flex>); | ||
|
||
const textElement = getByText('Test'); | ||
expect(textElement).toHaveStyle(` | ||
width: 100% | ||
`); | ||
expect(textElement).toHaveStyle(` | ||
height: 100% | ||
`); | ||
}); | ||
|
||
it('applies the gap style', () => { | ||
const { getByText } = render(<Flex gap="gap.small">Test</Flex>); | ||
|
||
const textElement = getByText('Test'); | ||
|
||
expect(textElement).toHaveStyle(` | ||
columnGap: 10px | ||
`); | ||
}); | ||
|
||
it('applies the hAlign style for row', () => { | ||
const { getByText } = render(<Flex hAlign="center">Test</Flex>); | ||
|
||
const textElement = getByText('Test'); | ||
expect(textElement).toHaveStyle(` | ||
justify-content: center | ||
`); | ||
}); | ||
|
||
it('applies the hAlign style for column', () => { | ||
const { getByText } = render( | ||
<Flex hAlign="center" column={true}> | ||
Test | ||
</Flex>, | ||
); | ||
|
||
const textElement = getByText('Test'); | ||
expect(textElement).toHaveStyle(` | ||
align-items: center | ||
`); | ||
}); | ||
|
||
it('applies the inline style', () => { | ||
const { getByText } = render(<Flex inline={true}>Test</Flex>); | ||
|
||
const textElement = getByText('Test'); | ||
expect(textElement).toHaveStyle(` | ||
display: inline-flex | ||
`); | ||
}); | ||
|
||
it('applies the padding style', () => { | ||
const { getByText } = render(<Flex padding="padding.medium">Test</Flex>); | ||
|
||
const textElement = getByText('Test'); | ||
expect(textElement).toHaveStyle(` | ||
padding-left: 10px | ||
`); | ||
}); | ||
|
||
it('applies the space style', () => { | ||
const { getByText } = render(<Flex space="around">Test</Flex>); | ||
|
||
const textElement = getByText('Test'); | ||
expect(textElement).toHaveStyle(` | ||
justify-content: space-around | ||
`); | ||
}); | ||
|
||
it('applies the vAlign style for row', () => { | ||
const { getByText } = render(<Flex vAlign="center">Test</Flex>); | ||
|
||
const textElement = getByText('Test'); | ||
expect(textElement).toHaveStyle(` | ||
align-items: center | ||
`); | ||
}); | ||
|
||
it('applies the vAlign style for column', () => { | ||
const { getByText } = render( | ||
<Flex vAlign="center" column={true}> | ||
Test | ||
</Flex>, | ||
); | ||
|
||
const textElement = getByText('Test'); | ||
expect(textElement).toHaveStyle(` | ||
justify-content: center | ||
`); | ||
}); | ||
|
||
it('applies the wrap style', () => { | ||
const { getByText } = render(<Flex wrap={true}>Test</Flex>); | ||
|
||
const textElement = getByText('Test'); | ||
expect(textElement).toHaveStyle(` | ||
flex-wrap: wrap | ||
`); | ||
}); | ||
}); |
112 changes: 112 additions & 0 deletions
112
packages/react-components/react-migration-v0-v9/src/components/Flex/Flex.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import { mergeClasses } from '@fluentui/react-components'; | ||
import * as React from 'react'; | ||
|
||
import { useFlexStyles } from './Flex.styles'; | ||
|
||
export interface FlexProps { | ||
/** Defines if container should be inline element. */ | ||
inline?: boolean; | ||
|
||
/** Sets vertical flow direction. */ | ||
column?: boolean; | ||
|
||
/** Allows overflow items to wrap on the next container's line. */ | ||
wrap?: boolean; | ||
|
||
/** Controls items alignment in horizontal direction. */ | ||
hAlign?: 'start' | 'center' | 'end' | 'stretch'; | ||
|
||
/** Controls items alignment in vertical direction. */ | ||
vAlign?: 'start' | 'center' | 'end' | 'stretch'; | ||
|
||
/** Defines strategy for distributing remaining space between items. */ | ||
space?: 'around' | 'between' | 'evenly'; | ||
|
||
/** Defines gap between each two adjacent child items. */ | ||
gap?: 'gap.smaller' | 'gap.small' | 'gap.medium' | 'gap.large'; | ||
|
||
/** Defines container's padding. */ | ||
padding?: 'padding.medium'; | ||
|
||
/** Orders container to fill all parent's space available. */ | ||
fill?: boolean; | ||
} | ||
|
||
export const flexClassName = 'fui-Flex'; | ||
|
||
export const Flex = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLElement> & FlexProps>((props, ref) => { | ||
const { children, column, fill, gap, hAlign, inline, padding, space, vAlign, wrap, className, ...rest } = props; | ||
const classes = useFlexStyles(); | ||
|
||
const classMaps = React.useMemo( | ||
() => ({ | ||
alignItems: { | ||
start: classes.alignItemsFlexStart, | ||
center: classes.alignItemsCenter, | ||
end: classes.alignItemsFlexEnd, | ||
stretch: classes.alignItemsCenter, | ||
}, | ||
justifyContent: { | ||
start: classes.justifyContentFlexStart, | ||
center: classes.justifyContentCenter, | ||
end: classes.justifyContentFlexEnd, | ||
stretch: classes.justifyContentStretch, | ||
}, | ||
justifyContentSpace: { | ||
around: classes.justifyContentSpaceAround, | ||
between: classes.justifyContentSpaceBetween, | ||
evenly: classes.justifyContentSpaceEvenly, | ||
}, | ||
gapForColumnFlex: { | ||
'gap.smaller': classes.gapForColumnFlexSmaller, | ||
'gap.small': classes.gapForColumnFlexSmall, | ||
'gap.medium': classes.gapForColumnFlexMedium, | ||
'gap.large': classes.gapForColumnFlexLarge, | ||
}, | ||
gapRow: { | ||
'gap.smaller': classes.gapForRowFlexSmaller, | ||
'gap.small': classes.gapForRowFlexSmall, | ||
'gap.medium': classes.gapForRowFlexMedium, | ||
'gap.large': classes.gapForRowFlexLarge, | ||
}, | ||
paddings: { | ||
'padding.medium': classes.paddingMedium, | ||
}, | ||
}), | ||
[classes], | ||
); | ||
|
||
const flexClasses = mergeClasses( | ||
flexClassName, | ||
classes.flex, | ||
inline && classes.inline, | ||
column && classes.column, | ||
hAlign && (column ? classMaps.alignItems[hAlign] : classMaps.justifyContent[hAlign]), | ||
vAlign && (column ? classMaps.justifyContent[vAlign] : classMaps.alignItems[vAlign]), | ||
space && classMaps.justifyContentSpace[space], | ||
wrap && classes.wrap, | ||
fill && classes.fill, | ||
gap && (column ? classMaps.gapForColumnFlex[gap] : classMaps.gapRow[gap]), | ||
padding && classMaps.paddings[padding], | ||
className, | ||
); | ||
|
||
const content = React.Children.map(children, child => { | ||
// @ts-expect-error __isFlexItem is added to the React type property by N* | ||
const isFlexItemElement: boolean = child?.type?.__isFlexItem; | ||
|
||
return isFlexItemElement | ||
? React.cloneElement(child as React.ReactElement, { | ||
flexDirection: column ? 'column' : 'row', | ||
}) | ||
: child; | ||
}); | ||
|
||
return ( | ||
<div ref={ref} className={flexClasses} {...rest}> | ||
{content} | ||
</div> | ||
); | ||
}); | ||
|
||
Flex.displayName = 'Flex'; |
Oops, something went wrong.