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

Fixes #24475 - Add EmptyState to React components #5850

Merged
merged 1 commit into from
Aug 9, 2018
Merged

Fixes #24475 - Add EmptyState to React components #5850

merged 1 commit into from
Aug 9, 2018

Conversation

boaz0
Copy link
Member

@boaz0 boaz0 commented Jul 18, 2018

This is part of #5755 - Adding Table React components to Hardware Models.
EmptyState is used in the Table component and since it didn't have tests or storybook in Katello I decided to push it as its own PR.

This PR introduces two EmptyState components - DefaultEmptyState which is what we are using in Foreman and EmptyStatePattern which allows to customize the empty state component (actually DefaultEmptyState is based on top of it).

If you would like to play with this a little bit the storybook can be found here

PRs that are related: #5755 and #5024.

PatternFly React

I saw that @sharvit asked to push EmptyStatePattern to pf-react, which is similar to this PR (read more here). Between those two PRs there are nuances but the major difference is that in this PR the primary action and the secondary actions can be rendered in any way you would like (it doesn't have to be a button). This is to allow other people to set their own action-buttons/actions part.

Katello

Katello is using a very similar EmptyState component. The only big difference that I see is that the action buttons are wrapped inside LinkContainer in order to interact with react-router.

Therefore, in order to use this component in Katello use EmptyStatePattern and pass a function that renders the buttons from title and url. Something like this:

const katelloAction = ({title, url}) => (
  <LinkContainer to={action.url}>
    <Button href={action.url} bsStyle="primary" bsSize="large">
      {action.title}
    </Button>
  </LinkContainer>
)

const {action ..} = props
return <EmptyStatePattern
  ...
  action={katelloAction(action)}
  secondaryAction={secondaryActions.map(action => katelloAction(action))}
/>

//cc @ohadlevy @sharvit @amirfefer @tstrachota @waldenraines

@@ -0,0 +1,67 @@
import React from 'react';
import DefaultEmptyState, { EmptyStatePattern } from './index';
import { testComponentSnapshotsWithFixtures } from '../../../common/testHelpers';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'toJson' is defined but never used no-unused-vars

@@ -0,0 +1,67 @@
import React from 'react';
import DefaultEmptyState, { EmptyStatePattern } from './index';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'shallow' is defined but never used no-unused-vars

</Button>
);

const documentationBlock = ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ohadlevy.
I uploaded here the storybook with EmptyState and DocumentationLink.

What bothers me is that DocumentationLink has the bullet in the beginning of the link. Is that supposed to be there?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is used today in the bookmarks dropdown, so I assume that needs to be extracted from the component and wrapped above it in bookmarks...?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think if I move it from the <MenuItem> it will look good. Let me check it out

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping @ohadlevy - here is the storybook with what you suggested. I will upload the changes if it looks fine to you

import PropTypes from 'prop-types';
import { EmptyState as PfEmptyState, Button } from 'patternfly-react';

const secondaryActionButtons = (actions = []) =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to create a component for each one of those? it can in theory make the snapshots smaller?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean something like:

const ActionButton = ({action: {title, url}, ...props}) => 
  <Button href={url}>{title}</Button>;
const PrimaryActionButton = (action) => 
  <ActionButton action={action} bsSize="large" bsStyle="primary" />
const SecondaryActionButtons = (actions = []) => 
  actions.map(action => <ActionButton action={action} />)

@boaz0
Copy link
Member Author

boaz0 commented Jul 25, 2018

@ohadlevy I updated the code and the storybook:

@boaz0
Copy link
Member Author

boaz0 commented Jul 25, 2018

@ohadlevy I have no idea why but when I am using the __ function outside a React component (f.e. in a defaultProps) tests fail:

ReferenceError: Can't find variable: __
ReferenceError: Can't find variable: __
    at http://127.0.0.1:36453/webpack/bundle-bbfc6206aec26c54f74a.js:1 in ./webpack/assets/javascripts/react_app/components/common/DocumentationLink/index.js
    at http://127.0.0.1:36453/webpack/vendor-255a8802f7a0349e7379.js:1 in a
    at http://127.0.0.1:36453/webpack/bundle-bbfc6206aec26c54f74a.js:1 in ./webpack/assets/javascripts/react_app/components/bookmarks/index.js
    at http://127.0.0.1:36453/webpack/vendor-255a8802f7a0349e7379.js:1 in a
    at http://127.0.0.1:36453/webpack/bundle-bbfc6206aec26c54f74a.js:1 in ./webpack/assets/javascripts/react_app/components/componentRegistry.js
    at http://127.0.0.1:36453/webpack/vendor-255a8802f7a0349e7379.js:1 in a
    at http://127.0.0.1:36453/webpack/bundle-bbfc6206aec26c54f74a.js:1 in ./webpack/assets/javascripts/react_app/common/MountingService.js
    at http://127.0.0.1:36453/webpack/vendor-255a8802f7a0349e7379.js:1 in a
    at http://127.0.0.1:36453/webpack/bundle-bbfc6206aec26c54f74a.js:1 in ./webpack/assets/javascripts/bundle.js
    at http://127.0.0.1:36453/webpack/vendor-255a8802f7a0349e7379.js:1 in a
    at http://127.0.0.1:36453/webpack/vendor-255a8802f7a0349e7379.js:1 in webpackJsonp
ReferenceError: Can't find variable: tfm
ReferenceError: Can't find variable: tfm
    at http://127.0.0.1:36453/assets/application-6496916c7a22421384ad3711aebc5b3026d4e153555adc51af5d7700547476d9.js:21485 in global code
ReferenceError: Can't find variable: tfm
ReferenceError: Can't find variable: tfm
    at http://127.0.0.1:36453/about:104 in global code
ReferenceError: Can't find variable: tfm
ReferenceError: Can't find variable: tfm
    at http://127.0.0.1:36453/about:307 in global code
ReferenceError: Can't find variable: tfm
ReferenceError: Can't find variable: tfm
    at http://127.0.0.1:36453/about:318 in global code
    test/integration/about_test.rb:31:in `block in <class:AboutIntegrationTest>'

@ohadlevy
Copy link
Member

@ripcurld does window.__ makes any difference? is it during tests (I guess not as i see webpack in your output) also, error about tfm indicate it failed to compile it?

@ohadlevy
Copy link
Member

@amirfefer can you review please? I remember you did something similar in the past?

@boaz0
Copy link
Member Author

boaz0 commented Jul 26, 2018

@ohadlevy nope window.__ didn't help. It's not only during tests but when going to any page (Hardware Models, Operating Systems, Hosts) bookmarks isn't displayed and a similar error message is displayed in the console log.

@boaz0
Copy link
Member Author

boaz0 commented Jul 26, 2018

I found moved jed(i18n) to webpack. I will try to see if that helps here too.

Thanks @ohadlevy & @amirfefer

@boaz0
Copy link
Member Author

boaz0 commented Jul 30, 2018

Update: @amirfefer's PR moved jed(i18n) to webpack does fix that problem.

@boaz0 boaz0 changed the title [WIP] Fixes #0000 - Add EmptyState to React components [WIP] Fixes #24475 - Add EmptyState to React components Jul 30, 2018
@boaz0 boaz0 changed the title [WIP] Fixes #24475 - Add EmptyState to React components Fixes #24475 - Add EmptyState to React components Jul 30, 2018
Copy link
Member

@amirfefer amirfefer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good @ripcurld !
few minor questions

};

DocumentationLink.defaultProps = {
id: 'documentationLink',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't it be hard coded? for instance if we have some css rules on this id, the consumer might break that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops you're right. 😱

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amirfefer actually to my surprise BookmarkContainer is passing id to DocumentationLink.
I only noticed it when I ran tests on my computer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and should the consumer be able to change the id?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decided not to expose the "id" props as it's used for CSS/jQuery any where in the code base.

import { DocumentLinkContent } from '../DocumentationLink';

const ActionButton = ({ url, children, ...props }) => (
<Button href={url} {...props}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it only wraps a button component with no extra logic or extension, why not just use Button directly in secondaryActionButtons and primaryActionButton

}
description="Printers print a file from the computer"
documentation={
<Unknown>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unknown?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because I am using React.Fragment. Let me what I can do about it.

Copy link
Member Author

@boaz0 boaz0 Jul 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that this fixed in jest 23. For more information read this PR.

We currently are using jest v20.0.4 so I probably have to update the package, right? or is there a better way?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to update to 23.0.0 and all tests were failing:

SecurityError: localStorage is not available for opaque origins
 at Window.get localStorage [as localStorage] (node_modules/jest-environment-
          jsdom/node_modules/jsdom/lib/jsdom/browser/Window.js:257:15)
                  at Array.forEach (<anonymous>)

Copy link
Member Author

@boaz0 boaz0 Jul 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

n/m need to add "testURL" to jest section in package.json in order to fix that

update and yet didn't help. I got many tests failing (some on snapshot and others because turbolink's visit is not called)... I will figure a different way. 😪

@boaz0
Copy link
Member Author

boaz0 commented Jul 31, 2018

/ping @ohadlevy @amirfefer - ready for next iteration of reviews.

Thanks 😀

};

DocumentationLink.defaultProps = {
id: 'documentationLink',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and should the consumer be able to change the id?

describe('Default Empty State', () => {
// React.Fragment is rendered as <Unknown>:
// https://github.com/facebook/jest/pull/5816
// To fix this, I am using TestRenderer.create.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, I tried to mock React.Fragment using jest (mock and setMock) none was working for me.
In addition, I found TestRenderer much easier to follow and to test.

If someone is capable to mock React.Fragment please let me know. 🍡

</a>
</SafeAnchor>
</li>
</MenuItem>
</Component>
</DocumentationLink>
<MenuItem
bsClass="dropdown"
disabled={false}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: this is too large snapshot, is there a way to reduce its size? (divide it ?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will have a solution for that hopefully in 2 hours 👍

Copy link
Contributor

@sharvit sharvit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @ripcurld, I just got one small comment.

@@ -0,0 +1,116 @@
import React from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you change this file so it will contain single component per file?
e.g.

  • common/EmptyState/EmptyState.js
  • common/EmptyState/EmptyStatePattern.js
  • common/EmptyState/EmptyStatePropTypes.js
  • common/EmptyState/EmptyStateSecondaryActionButtons.js
  • common/EmptyState/EmptyStatePrimaryActionButton.js

And you can namely expose the relevant component from the common/EmptyState/index.js

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem - you got it!

amirfefer
amirfefer previously approved these changes Aug 2, 2018
Copy link
Member

@amirfefer amirfefer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@boaz0
Copy link
Member Author

boaz0 commented Aug 3, 2018

[test foreman]

sharvit
sharvit previously approved these changes Aug 6, 2018
Copy link
Contributor

@sharvit sharvit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks @ripcurld 👍

@boaz0
Copy link
Member Author

boaz0 commented Aug 6, 2018

@ohadlevy @sharvit @amirfefer I rebased (there was a conflict with #5872)

@boaz0
Copy link
Member Author

boaz0 commented Aug 7, 2018

Rebased (due to #5921) ☘️

label = __('For more information please see'),
buttonLabel = __('Documentation'),
}) =>
(url ? (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: why not url && <...> instead ? e.g.

url && (<React.Fragment>{label}{' '}<a href={url} target="_blank"><DocumentLinkContent>{buttonLabel}</DocumentLinkContent></a></React.Fragment>)

@ohadlevy
Copy link
Member

ohadlevy commented Aug 7, 2018

looks good, one nitpick :)

Signed-off-by: Boaz Shuster <boaz.shuster.github@gmail.com>
@boaz0
Copy link
Member Author

boaz0 commented Aug 7, 2018

[test upgrade]

@ohadlevy
Copy link
Member

ohadlevy commented Aug 9, 2018

@sharvit & @amirfefer mind giving final ack? thanks

Copy link
Member

@amirfefer amirfefer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍
Thanks @ripcurld

Copy link
Contributor

@sharvit sharvit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks @ripcurld

@ohadlevy ohadlevy merged commit 285fd58 into theforeman:develop Aug 9, 2018
@boaz0
Copy link
Member Author

boaz0 commented Aug 9, 2018

🎉 SWEET 🎉
img

@boaz0 boaz0 deleted the empty_state branch September 2, 2018 10:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants