Skip to content
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

Conversation

Juyeong-Byeon
Copy link
Contributor

@Juyeong-Byeon Juyeong-Byeon commented Aug 14, 2021

Description

[implement] : FAB

  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

please comment about the structure of Component.

**Animation is not implemented**
**Need more implementation**

Related Issues

[FloatingActionButton] implementing floatingActionButton component

Tests

I did not add the test yet.
This PR needs more implementation.

Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • Run yarn test or yarn test -u if you need to update snapshot.
  • Run yarn lint
  • I am willing to follow-up on review comments in a timely manner.

@codecov
Copy link

codecov bot commented Aug 14, 2021

Codecov Report

Merging #67 (afd59c4) into master (0d8318d) will decrease coverage by 1.44%.
The diff coverage is 96.29%.

@@            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     

@Juyeong-Byeon Juyeong-Byeon force-pushed the main/FloatingActionButtonWrapper branch from f72bbc3 to 44089ee Compare August 14, 2021 14:58
@Juyeong-Byeon Juyeong-Byeon changed the title [implement] : FAB [Implement] : FAB Aug 14, 2021
@hyochan hyochan added the 🎯 feature New feature label Aug 15, 2021
@yujonglee
Copy link
Contributor

yujonglee commented Aug 17, 2021

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.
But It was totally fine.

Ok. As I understand, FABList is rendered only when Active. And DefaultFAB/ActiveFAB is chosen by Active state.
Also, ActiveFAB, DefaultFAB, and FABItem can have render function or it is rendered as default form.

I think overall structure is fine, but naming can be better.

  1. I think DefaultFAB is the most confusing part. Default <--> Active should be replaced with Inactive <--> Active or Expanded <--> Collapsed. And the word Default should be used for without render function situation.
  2. ActiveFAB and FABItem has Parent-Children relation, but it is not revealed by variable name.

BTW, this might be silly mistake of mine, but I'm seeing icon never changes.
I'm always seeing same icon whenever I change iconName string.

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},
  ]}
/>

@yujonglee
Copy link
Contributor

yujonglee commented Aug 17, 2021

It's hard to communicate many changes at once with review(and codecov warning is annoying 😄 ), so I pasted code below.
This is just a suggestion. Feel free to point out anything.

  1. My main goal was make code self-explainary by removing conditionals.
  2. I can't be sure this refactor guarantee same functionality as before because there's no test code. But I think It is sufficient for showing my intention.
  3. theme is not provided to defaultSubRenderer in code below. It should be changed. It's just a demo :)
// 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);

Copy link
Member

@hyochan hyochan left a 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 💯

Copy link
Member

@hyochan hyochan left a 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/>}
  />

main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
@hyochan
Copy link
Member

hyochan commented Aug 19, 2021

Also the generic can be extended like below

interface FABItemProp {
  icon: string;
  name: string;
}

type Props<T extends FABItemProp> = {
  data: T[];

@Juyeong-Byeon
Copy link
Contributor Author

@hyochan thanks
It is very good idea to use generic extended the FABItem.
It will give more flexibility to FAB component

@yujonglee
Copy link
Contributor

yujonglee commented Aug 19, 2021

but I think we should support default components such as with conditionals.
Because it makes it much easier to use components. It will improve developers' experience in use.

@wndudqus For sure. My code above supports default component, but I remove conditionals using default value feature which language provides.

// code from props destructing above
renderInactiveRootItem = defaultRootRenderer,
renderActiveRootItem = defaultRootRenderer,
renderSubItem = defaultSubRenderer,

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.

@Juyeong-Byeon
Copy link
Contributor Author

@yujong-lee
Oh, I see... I think this approach seems to simplify the code.
How do you think? @hyochan

@hyochan
Copy link
Member

hyochan commented Aug 20, 2021

@yujong-lee
Oh, I see... I think this approach seems to simplify the code.
How do you think? @hyochan

Yes for the default component~ You can try @yujong-lee 's proposal 👍

@hyochan hyochan mentioned this pull request Aug 20, 2021
4 tasks
@hyochan
Copy link
Member

hyochan commented Aug 27, 2021

@wndudqus Please rerequest the review when you think you are ready!

@Juyeong-Byeon Juyeong-Byeon force-pushed the main/FloatingActionButtonWrapper branch from a321bf0 to 7c1b410 Compare August 27, 2021 13:22
Copy link
Contributor

@yujonglee yujonglee left a 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.

main/__tests__/FAB.test.tsx Outdated Show resolved Hide resolved
let item1: FABItem = {icon: 'bell-solid', id: 'item1'};
let resItem: FABItem;

const testingLib = render(
Copy link
Contributor

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.

Copy link
Contributor

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.

Comment on lines 26 to 28
let count = 0;
let item1: FABItem = {icon: 'bell-solid', id: 'item1'};
let resItem: FABItem;
Copy link
Contributor

@yujonglee yujonglee Aug 27, 2021

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/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/__tests__/FAB.test.tsx Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
expect(json).toBeTruthy();
});

it('test mainFAB onPress callback', async () => {
Copy link
Contributor

@yujonglee yujonglee Aug 27, 2021

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.

@Juyeong-Byeon Juyeong-Byeon force-pushed the main/FloatingActionButtonWrapper branch 2 times, most recently from 57b9189 to 0982887 Compare August 28, 2021 13:14
Copy link
Member

@hyochan hyochan left a 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.

main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/__tests__/FAB.test.tsx Outdated Show resolved Hide resolved
stories/dooboo-ui/FloatingActionButton/FABStory.tsx Outdated Show resolved Hide resolved
stories/dooboo-ui/FloatingActionButton/FABStory.tsx Outdated Show resolved Hide resolved
main/index.ts Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
@hyochan
Copy link
Member

hyochan commented Aug 29, 2021

1uuezORGeM

When FAB first renders, it doesn't hide the children. It should not run the animation when it shouldn't and also should follow isActive prop.

When looking in your code I see that you are showing children with opacity animation.
Screen Shot 2021-08-29 at 9 33 28 PM

I have some issues with the above design.

  1. The FABItem should appear from the main FAB button but they are just appearing from their target positions. I think we need to change that.
  2. As told previously, it should behave with actual prop value.

@hyochan
Copy link
Member

hyochan commented Aug 29, 2021

Additionally, the custom render function would not want to receive the callback for the press event. It'd only want to provide a different UI element.

Below is just an example.

{FABList.map((item, idx) => {
  return (
    <TouchableOpacity onPress={onPressFABItem?.(item)}>
      {renderFABListItem ? (
        renderFABListItem(item, idx)
      ) : (
        <StyledIcon theme={theme} size={24} name={item.icon} />
      )}
    </TouchableOpacity>
  );
})}

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&gt;;// 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
before
	* isActive was props. but it makes it hard to provide an onPress callback.
	* mainFAB Button was evaluated out side of return

acter
	* make isActive props to state
	* pub mainFAB inside return
Animation was implemented
and Fix the story demo error
1. MainFAB animation
2. Implemented basic test that tests the callback
1. change onpress callback naming
before
onPressFABItem
after
onPressListItem

2.delete unnecessary import
before
React.ReactElement
after
deconstruct React and just import ReactElement

3. delete the comment

4. not to use index for the key of the list item
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
    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
@Juyeong-Byeon
Copy link
Contributor Author

@hyochan @yujong-lee

I think I covered most of your reviews

  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
  3. Bug that state was not synced with UI has fixed
Simulator.Screen.Recording.-.iPhone.12.Pro.Max.-.2021-09-16.at.22.38.29.mp4

Copy link
Contributor

@yujonglee yujonglee left a 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;
Copy link
Contributor

@yujonglee yujonglee Sep 16, 2021

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)

Copy link
Contributor Author

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.

main/FAB/index.tsx Outdated Show resolved Hide resolved
renderFAB?: () => ReactElement;
renderFabItem?: (item: Item, idx: number) => ReactElement;
size: ButtonSize;
style?: StyleProp<ViewStyle>;
Copy link
Contributor

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 Show resolved Hide resolved
onPressFabItem,
renderFAB,
renderFabItem,
size = 'large',
Copy link
Contributor

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.

main/FAB/index.tsx Show resolved Hide resolved
main/FAB/index.tsx Outdated Show resolved Hide resolved
main/FAB/index.tsx Outdated Show resolved Hide resolved
main/FAB/index.tsx Outdated Show resolved Hide resolved
main/FAB/index.tsx Outdated Show resolved Hide resolved
@yujonglee yujonglee self-requested a review September 16, 2021 16:35
main/FAB/index.tsx Outdated Show resolved Hide resolved
I reflected the contents of the code review.

1. all fab -> FAB
2. animation code improvement.
	* delete duplicate
3. some detail
@yujonglee
Copy link
Contributor

yujonglee commented Sep 19, 2021

@wndudqus

There's still some parts that is not reflected in code review. For example, renderProp, jest.fn or styles.(buttonWrapperStyle is better replaced with styles.)

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.
Thank you for great work on this!

@yujonglee yujonglee merged commit ba9009e into dooboolab-community:master Sep 19, 2021
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/FloatingActionButtonWrapper/index.tsx Outdated Show resolved Hide resolved
main/__tests__/FAB.test.tsx Outdated Show resolved Hide resolved
main/FAB/index.tsx Outdated Show resolved Hide resolved
main/FAB/index.tsx Outdated Show resolved Hide resolved
main/FAB/index.tsx Outdated Show resolved Hide resolved
main/FAB/index.tsx Outdated Show resolved Hide resolved
main/FAB/index.tsx Outdated Show resolved Hide resolved
@Juyeong-Byeon
Copy link
Contributor Author

Juyeong-Byeon commented Sep 19, 2021

@yujong-lee oh I left some comments before request a review, but I forgot to submit the review so.. my mistake sorry.
And thank you for reviewing my code!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🎯 feature New feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants