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
[Implement] : FAB #67
[Implement] : FAB #67
Conversation
Codecov Report
@@ Coverage Diff @@
## master #67 +/- ##
==========================================
- Coverage 95.53% 94.08% -1.45%
==========================================
Files 23 23
Lines 582 609 +27
Branches 267 275 +8
==========================================
+ Hits 556 573 +17
- Misses 26 36 +10 |
f72bbc3
to
44089ee
Compare
Nice Work! I slightly look into it. I think maybe it will be easier for me to understand if you provide some example code and output. Ok. As I understand, I think overall structure is fine, but
BTW, this might be silly mistake of mine, but I'm seeing icon never changes. I'm using like this. <FAB
active={true}
ActiveFAB={{icon: 'cross-light', onPress: () => null}}
DefaultFAB={{icon: 'cross-light', onPress: () => null}}
FABList={[
{icon: 'chevron-down-light', onPress: () => null},
{icon: 'chevron-down-light', onPress: () => null},
{icon: 'chevron-down-light', onPress: () => null},
]}
/> |
It's hard to communicate many changes at once with
// Above code untouched
const defaultRootRenderer = ({icon, onPress}: FABItem): React.ReactElement => (
<IconButton icon={<StyledIcon size={24} name={icon} />} onPress={onPress} />
);
const defaultSubRenderer = (
{icon, onPress}: FABItem,
_index: number,
): React.ReactElement => (
<IconButton
icon={<StyledIcon theme={theme} size={24} name={icon} />}
onPress={onPress}
/>
);
const FloatingActionButtons: FC<
FloatingActionButtonsWrapperProps & {theme: DoobooTheme}
> = ({
theme,
active,
inactiveRootItem,
activeRootItem,
subItems,
renderInactiveRootItem = defaultRootRenderer,
renderActiveRootItem = defaultRootRenderer,
renderSubItem = defaultSubRenderer,
zIndex = 0,
top = 'auto',
bottom = 'auto',
left = 'auto',
right = 'auto',
}) => {
return (
<FABContainer
style={{
top,
bottom,
left,
right,
zIndex,
}}>
<Animated.View>
{active &&
subItems.map((item, idx) => (
<ItemContainer key={item.icon + idx}>
{renderSubItem(item, idx)}
</ItemContainer>
))}
</Animated.View>
<ItemContainer>
{active
? renderInactiveRootItem(inactiveRootItem)
: renderActiveRootItem(activeRootItem)}
</ItemContainer>
</FABContainer>
);
};
export const FAB = withTheme(FloatingActionButtons); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like @yujong-lee covered most of my review already which I've communicated earlier. Thanks a lot! You are such a quick learner 💯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the spec would be simplified to below. How do you think 🤔 ?
<FAB
style={{ background: 'red' }}
data={[ 'main', 'settings', 'addItem' ]}
icon="plus"
isActive={isActive}
onPress={() => setIsActive(!isActive)}
onPressItem={(data) => console.log('pressed', data)}
renderFAB={() => <View/>}
renderFABItem={() => <View/>}
/>
Also the generic can be extended like below
|
@hyochan thanks |
@wndudqus For sure. My code above supports default component, but I remove conditionals using
Note that I set default render function that I defined above. User can provide render function, or omit it. If there's anything I misunderstood, please let me know. |
@yujong-lee |
Yes for the default component~ You can try @yujong-lee 's proposal 👍 |
@wndudqus Please rerequest the review when you think you are ready! |
a321bf0
to
7c1b410
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's good to see you working on it continuously. 👍
I left some comments. Feel free to ask, or point it out if I'm wrong.
let item1: FABItem = {icon: 'bell-solid', id: 'item1'}; | ||
let resItem: FABItem; | ||
|
||
const testingLib = render( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pattern from enzyme
, which is not good practice for testing library.
I recommend writing like this.
const {getByTestId} = render(...);
fireEvent.press(getByTestId('item1'));
or
render(...);
fireEvent.press(screen.getByTestId('item1'));
Although later one is new feature and recommended by author, I rarely use it. We can talk about it if you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See this for detailed guide for using testing-library
.
main/__tests__/FAB.test.tsx
Outdated
let count = 0; | ||
let item1: FABItem = {icon: 'bell-solid', id: 'item1'}; | ||
let resItem: FABItem; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should avoid using let
if possible.
If you use jest.fn()
, they are not needed.
See how I did it here.
You can use toBeCalled
for this case.
main/__tests__/FAB.test.tsx
Outdated
expect(json).toBeTruthy(); | ||
}); | ||
|
||
it('test mainFAB onPress callback', async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe async
is used due to animation duration?
For that, I think jest.useFakeTimers()
is available.
See this for detail.
57b9189
to
0982887
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work~! I've just pointed small fixes on naming
.
Additionally, the custom Below is just an example.
|
1. Implement basic FloationActionButotnWrapperProps * active: boolean; //active ===true “expend ActionButtons”:” hide ActionButtons” *DefaultMainActionButton: React.ReactElement; // show when active===false *ActiveMainActionButton: React.ReactElement; // show when active===true *ActionButtons: Array<React.ReactElement>;// show when active===true *top?: number; // fixedPosition of FAB *bottom?: number;// fixedPosition of FAB *left?: number; // fixedPosition of FAB *right?: number; // fixedPosition of FAB 2. Implement basic layout wrapper views for FloatingActionButtonWrapper(FAB) component. * FixedPositionWrapperView sets overall position of FAB * ExpendWrapperView makes expend animation along "active" value in props
1. Implemented the basic functions of the FAB * Can use IconButton that is a default button component of FAB by passing the icon types and callbacks. * Can replace the default button component of FAB by passing renderButton function * Can set absolute position of FAB * Can replace buttons by active Props Rendering conditions: if(active===false)render DefaultFAB if(active===true)render ActiveFAB and ActionButtons **Animation is not implemented** **Need more implementation**
1. I simplified the props. * DefaultFABItem, ActiveFABIItem were removed and renderIMainFAB function were provided for customization. * Removed props that receiving styles per attribute * abstracted the FabItem data. * provide onPressFABItem to receive onPress callback (it will pass generic item data) 2. Add FABStory
Animation was implemented and Fix the story demo error
1. MainFAB animation 2. Implemented basic test that tests the callback
1. change Props naming 2. changed behavior Previously, only the transparency of the button was adjusted depending on the state but now the actual position is adjusted. 3. Change state management. Previously, the active was managed as a "state", modified to manage it as a "props". 4. path name changed FloatingActionButtonWrapper> FAB
6bddf3d
to
f2a89e2
Compare
1. position translation has implemented 2. naming has been changed onPressListItem > onPressFabItem ItemList > fabItems 3. Bug that state was not synced with UI has fixed
fa6e4a3
to
4535cf5
Compare
@hyochan @yujong-lee I think I covered most of your reviews
Simulator.Screen.Recording.-.iPhone.12.Pro.Max.-.2021-09-16.at.22.38.29.mp4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was impressed by your clean code. 👍
I just left few more suggestions you can try!
fabItems: Item[]; | ||
onPressFAB: () => void; | ||
onPressFabItem: (item?: Item) => void; | ||
renderFAB?: () => ReactElement; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
renderProps is not for providing ReactElement
. It is for reusing render logic
.
Like you did, Items - renderItem
is proper use case here. Because it is providing render logic for each item. But If you are just providing ReactElement, children
or slot pattern
is for that use.
Additionally, it is better for performance too. Because ReactElement is always newly created for each render if you call () => ReactElement
function. (Although It has almost no impact here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is better to provide consistency on the methods on providing custom component.
Now in this component renderFABItem
should be provided as renderProps
so.. I think it is better to provide renderFAB
as renderProps
.
renderFAB?: () => ReactElement; | ||
renderFabItem?: (item: Item, idx: number) => ReactElement; | ||
size: ButtonSize; | ||
style?: StyleProp<ViewStyle>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can have additional styles
prop?
One for Animated.View
for main icon
and one for items
.
main/FAB/index.tsx
Outdated
onPressFabItem, | ||
renderFAB, | ||
renderFabItem, | ||
size = 'large', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although they might have same size in general, I think FABItem
is responsible for setting size, not FloatingActionButtons
component.
I reflected the contents of the code review. 1. all fab -> FAB 2. animation code improvement. * delete duplicate 3. some detail
@wndudqus There's still some parts that is not reflected in code review. For example, renderProp, jest.fn or If some reviews are not acceptable, feel free to tell me and have a discussion. It should not be one-sided. But since this pull request is old and big, let's take care of this in later PR. |
@yujong-lee oh I left some comments before request a review, but I forgot to submit the review so.. my mistake sorry. |
Description
[implement] : FAB
* Can use IconButton that is a default button component of FAB by passing the icon types and callbacks.
* Can replace the default button component of FAB by passing renderButton function
* Can set absolute position of FAB
* Can replace buttons by active Props
Rendering conditions:
if(active===false)render DefaultFAB
if(active===true)render ActiveFAB and ActionButtons
please comment about the structure of Component.
Related Issues
[FloatingActionButton] implementing floatingActionButton component
Tests
I did not add the test yet.
This PR needs more implementation.
Checklist
yarn test
oryarn test -u
if you need to update snapshot.yarn lint