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 #29307 - Add patternfly4 search to new CV page #8745

Merged
merged 7 commits into from
Jun 24, 2020

Conversation

johnpmitsch
Copy link
Contributor

@johnpmitsch johnpmitsch commented Jun 3, 2020

Adds new search at /labs/content_views

To-do:

  • tests
  • fix pf4 search icon to match mockups UPDATE: open question to UX
  • fix issue where you can't click on the first item in dropdown until you mouse over another item and back to it.
  • In the autocomplete there is an "operators" line in italics on the traditional CV page that needs to be added
    UPDATE: this is an existing issue in pf3 pages as well, I filed here https://projects.theforeman.org/issues/30071 and added to the feature tracker to look later.
  • Add tests for Search component (a nice-to-have, but will make the components easier to move to Foreman)

For this addition, we need to support pf3 and pf4 search. I originally tried to see what would happen if I updated the search to pf4, but it looks really out of place in the context of a pf3 page.

I updated the TypeAhead component to support both pf3 and pf4 so it could share the autocomplete functionality coming from the downshift package. The Search component now takes a patternfly4 prop and defaults to pf3 if not set.

I have some duplicated file names where the folder determines if the component is pf3 and pf4. This made sense to me, but I am open to suggestions. React (for better or worse) doesn't have a lot of opinions about file naming or conventions, and I don't see any strong opinions out there against this pattern, so it is really whatever we are comfortable with.

@theforeman-bot
Copy link

Issues: #29307

@johnpmitsch
Copy link
Contributor Author

@jeremylenz I'm opening this up as a draft, I put the remaining tasks in the PR message. If you have a chance to look, I would love to hear any feedback on the overall approach (especially before I write tests)

I moved the TypeAhead component out of the move_to_pf folder, that is in a separate commit to keep the changes separate. The reasoning for this is (from the commit message):

I would like to get away from the move_to_* folders. Our intent can still be to move the components to patternfly or foreman, but we don't necessarily have to call our folders that. These "temporary" folders have been here for years and we should take steps to be realistic about what we can and can't move and not organize our folders based on these assumptions, which makes things harder to find.

Copy link
Member

@jeremylenz jeremylenz left a comment

Choose a reason for hiding this comment

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

Thanks @johnpmitsch! A few initial comments below but I didn't get to play around too much, because I'm getting a console error when I search for "label = xxx" and then execute the search:

webcomponents-bundle.js:92 Uncaught TypeError: Cannot convert undefined or null to object
    at Function.getOwnPropertyNames (<anonymous>)
    at Function.../vendor-core/node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js.Object.keys (webcomponents-bundle.js:92)
    at tableDataGenerator.js:124
    at Array.forEach (<anonymous>)
    at tableDataGenerator (tableDataGenerator.js:120)
    at ContentViewsTable.js:20
    at commitHookEffectList (react-dom.development.js:19986)
    at commitPassiveHookEffects (react-dom.development.js:20016)
    at HTMLUnknownElement.callCallback (react-dom.development.js:347)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:397)
../vendor-core/node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js.Object.keys @ webcomponents-bundle.js:92
(anonymous) @ tableDataGenerator.js:123
tableDataGenerator @ tableDataGenerator.js:120
(anonymous) @ ContentViewsTable.js:19
commitHookEffectList @ react-dom.development.js:19986
commitPassiveHookEffects @ react-dom.development.js:20016
callCallback @ react-dom.development.js:347
invokeGuardedCallbackDev @ react-dom.development.js:397
invokeGuardedCallback @ react-dom.development.js:454
flushPassiveEffectsImpl @ react-dom.development.js:22868
unstable_runWithPriority @ scheduler.development.js:643
runWithPriority$2 @ react-dom.development.js:11305
flushPassiveEffects @ react-dom.development.js:22841
(anonymous) @ react-dom.development.js:22419
scheduler_flushTaskAtPriority_Normal @ scheduler.development.js:436
flushTask @ scheduler.development.js:482
flushWork @ scheduler.development.js:607
performWorkUntilDeadline @ scheduler.development.js:231
react_devtools_backend.js:6 The above error occurred in the <ContentViewTable> component:
    in ContentViewTable (created by ContentViewsPage)
    in ContentViewsPage (created by Context.Consumer)
    in withRouter(ContentViewsPage) (created by Headers)
    in Headers (created by Context.Consumer)
    in Route (created by _default)
    in div (created by _default)
    in _default (created by Application)
    in Router (created by BrowserRouter)
    in BrowserRouter (created by Application)
    in Application (created by ConnectFunction)
    in ConnectFunction (created by I18nProviderWrapper(Connect(Application)))
    in IntlProvider (created by I18nProviderWrapper(Connect(Application)))
    in I18nProviderWrapper(Connect(Application)) (created by StoreProvider(I18nProviderWrapper(Connect(Application))))
    in Provider (created by StoreProvider(I18nProviderWrapper(Connect(Application))))
    in StoreProvider(I18nProviderWrapper(Connect(Application))) (created by DataProvider(StoreProvider(I18nProviderWrapper(Connect(Application)))))
    in DataProvider(StoreProvider(I18nProviderWrapper(Connect(Application))))

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.

webpack/utils/useEventListener.js Show resolved Hide resolved
import { usePaginationOptions, useForemanSettings } from 'foremanReact/Root/Context/ForemanContext';

import EmptyStateMessage from './EmptyStateMessage';
import Loading from './Loading';
import MainTable from './MainTable';
Copy link
Member

Choose a reason for hiding this comment

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

👍 for breaking this out into its own file, makes it feel a little cleaner.

webpack/components/TypeAhead/TypeAhead.js Show resolved Hide resolved
webpack/components/TypeAhead/pf4Search/TypeAheadInput.js Outdated Show resolved Hide resolved
@johnpmitsch
Copy link
Contributor Author

  • fix pf4 search icon to match mockups

@MariSvirik Regarding the seach icon - from what I can tell the way our search and RH cloud search function is different. They search automatically and the magnifying glass is not a button, the search happens as you type. For instance:

image

With this search, I didn't click anything or hit enter to get this result, the search happens as you type.

Whereas for Foreman/Katello search, you have to click enter or click the search icon. So it seems like keeping it as a button is better, but I am wondering what you think is best. Right now I plan to copy the example in https://patternfly-react.surge.sh/documentation/react/components/toolbar but let me know if I should do something different!

@johnpmitsch johnpmitsch force-pushed the cvsearch branch 3 times, most recently from 489af1c to abacec2 Compare June 9, 2020 12:21
@johnpmitsch johnpmitsch marked this pull request as ready for review June 9, 2020 12:22
@johnpmitsch
Copy link
Contributor Author

Thanks @johnpmitsch! A few initial comments below but I didn't get to play around too much, because I'm getting a console error when I search for "label = xxx" and then execute the search:

@jeremylenz This is fixed now!

@johnpmitsch johnpmitsch force-pushed the cvsearch branch 2 times, most recently from f26b591 to 0b8c753 Compare June 9, 2020 14:35
@johnpmitsch
Copy link
Contributor Author

  • Add tests for Search component (a nice-to-have, but will make the components easier to move to Foreman)

I added one to make sure autocomplete shows, hopefully we can add more, but it is a start!

@johnpmitsch johnpmitsch force-pushed the cvsearch branch 2 times, most recently from 0627a55 to 5f3a09a Compare June 9, 2020 16:50
@johnpmitsch
Copy link
Contributor Author

  • fix issue where you can't click on the first item in dropdown until you mouse over another item and back to it.

Fixed this now, thanks @jeremylenz!

Btw https://projects.theforeman.org/issues/29907 should also be fixed in this PR

Copy link
Member

@jeremylenz jeremylenz left a comment

Choose a reason for hiding this comment

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

Thanks for moving it along!

Works mostly as expected, with a couple issues remaining. And I'm not seeing any more page flashes, so I think we're good with 29907.

  • When I change the per-page setting, the table reloads but executes a blank search, instead of the current active search. I would expect it to either clear out the search box, or re-execute the existing search.

  • If I click the X button, it displays the loading spinner and re-does the "search" for all items. As a user, I would expect clicking the X to be instant and not require waiting. At the least, we should store the "all items" search in memory (memoize it) so it doesn't have to make an API call every time. As to how, whatever makes the most sense to you -- Redux, useMemo, etc. Can be a follow-up PR.

  • A search for "not composite" is suggested when I type "not", but results in an error. (Btw, the error empty state looks great 😄)

inputValue: this.props.initialInputValue,
};

this.handleStateChange = this.handleStateChange.bind(this);
Copy link
Member

Choose a reason for hiding this comment

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

If you do

handleStateChange = ({inputValue}) => {
...

then you don't have to do the bind in the constructor here :)

Comment on lines 18 to 20
e, isOpen, activeItems, highlightedIndex,
selectItem, userInputValue, onSearch,
)}
Copy link
Member

Choose a reason for hiding this comment

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

Did eslint take a lunch break or something? 😆

Copy link
Member

Choose a reason for hiding this comment

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

This looks fine on my local system.. weird

Comment on lines 20 to 21
e, isOpen, activeItems, highlightedIndex,
selectItem, userInputValue, onSearch,
Copy link
Member

Choose a reason for hiding this comment

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

please format

return (
<TextInput
{...downshiftProps}
ref={inputRef}
Copy link
Member

Choose a reason for hiding this comment

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

👍 for switching to the useRef and non-function form of the prop. I was also going to suggest dropping ref in favor of the innerRef prop provided by PF4, but both ref and innerRef seem to put the event listener on the <input> element and both work fine. So I guess either is fine.

@johnpmitsch
Copy link
Contributor Author

  • A search for "not composite" is suggested when I type "not", but results in an error. (Btw, the error empty state looks great smile)

Thanks, and ya I saw that, it looks pre-existing and on the server side so I filed here https://projects.theforeman.org/issues/30079

It will definitely is more of a concern with autosearching on typing. Speaking with UX and others yesterday about it, it sounds like we would like to try out automatic searching on typing with an option to disable, and we can do more testing of this with a nightly box with a lot of content views to see how it performs.

So I'll update the PR with this as well as the other updates from review!

@johnpmitsch
Copy link
Contributor Author

@jeremylenz was able to update this and also include autosearch on typing, I'll follow up with a comment on how that works. I would like to add some more tests to Search, but otherwise it is ready for re-review 👍

  • When I change the per-page setting, the table reloads but executes a blank search, instead of the current active search. I would expect it to either clear out the search box, or re-execute the existing search.

Fixed!

  • If I click the X button, it displays the loading spinner and re-does the "search" for all items. As a user, I would expect clicking the X to be instant and not require waiting. At the least, we should store the "all items" search in memory (memoize it) so it doesn't have to make an API call every time. As to how, whatever makes the most sense to you -- Redux, useMemo, etc. Can be a follow-up PR.

This isn't how the existing pages work (they also make an API call when you clear the search) and afaik we didn't plan to change this behavior in this feature. If you want to file it and propose it for the future it could be a good addition, but I don't consider within scope of the new CV page.

@johnpmitsch
Copy link
Contributor Author

johnpmitsch commented Jun 16, 2020

@MariSvirik @Rohoover @jeremylenz
I added auto search typing similar to cloud.redhat.com. This is what it looks like:
basic_search

There are two new settings to disable it and also to configure the delay before searching. I defaulted to 500ms - 1 second felt too unresponsive and anything under 500ms sent too many requests if you typed slow. The settings are Automatic Search Typing Delay and Automatic Search while Typing seen below. I could see those with large amounts of content wanting higher delays or to disable the automatic searching completely.

here

If you disable the automatic search, it functions the same as our existing search, but with pf4 components:

basic_search_pf3

Let me know if you have any suggestions or questions!

@johnpmitsch johnpmitsch force-pushed the cvsearch branch 2 times, most recently from eba7ab1 to a77da06 Compare June 16, 2020 02:33
@johnpmitsch
Copy link
Contributor Author

@MariSvirik @Rohoover I forgot to show this, here is the empty search results and error state pages, I mostly followed patternfly's design guidelines, but let me know if anything should be tweaked :)

e

e
(this should only be seen if there is a bug or the server is down)

@johnpmitsch johnpmitsch reopened this Jun 16, 2020
@MariSvirik
Copy link

MariSvirik commented Jun 16, 2020

@johnpmitsch @Rohoover
Regarding empty states, I proposed this phrasing (consulted with Margot):

No matching content views found (RHtext medium, 20)
Try changing your search settings. (RHtext regular, 16)

Unable to connect
There was an error retrieving data from the server. Check your connection and try again.

@johnpmitsch
Copy link
Contributor Author

@johnpmitsch @Rohoover
Regarding empty states, I proposed this phrasing (consulted with Margot):

No matching content views found (RHtext medium, 20)
Try changing your search settings. (RHtext regular, 16)

Unable to connect
There was an error retrieving data from the server. Check your connection and try again.

@MariSvirik updated the text! I have to ask about the fonts, I think they are set in Foreman and would need to be changed there.

Here are the new pages:

e

d

@johnpmitsch
Copy link
Contributor Author

I would like to add some more tests to Search,

These are now added as well

@MariSvirik
Copy link

MariSvirik commented Jun 16, 2020

@johnpmitsch just a slight note about capitalization.
The name of the main page is 'Content Views' (both words capitalized) because it is also an item in the Navigation where everything should be in title case (the rule from Insights). We should use 'Content Views' when referring to the page itself. When you refer to a content view in general (not the page as such) you should use sentence case.
In our empty state, just a first letter of the first word of the sentence (n) should be capitalized: 'No matching content views found'

Thanks for doing such a good job 🥇

@johnpmitsch
Copy link
Contributor Author

In our empty state, just a first letter of the first word of the sentence (n) should be capitalized: 'No matching content views found'

thanks, I updated this!

Copy link
Member

@jeremylenz jeremylenz left a comment

Choose a reason for hiding this comment

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

Tested and works great! Love the debounced type-ahead search, though I really do miss the X button in the input. I'm guessing that's already decided on?

Comments below

webpack/components/Search/Search.js Show resolved Hide resolved
// Prevent empty search when page first loads, but search when input is cleared after that
this.setState({ autoSearchOnEmpty: true });
Copy link
Member

Choose a reason for hiding this comment

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

Why not just remove this.onInputUpdate() from componentDidMount? I'm guessing it's because you need to do part of this function on mount, but not all of it. If that's the case, could this function be split into parts to remove the need to track autoSearchOnEmpty?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removing it seems to work fine, I'm not sure why it was there in the first place 🤔 I checked RH repos and subscriptions page too and they work fine. Please double check me though!

Copy link
Member

Choose a reason for hiding this comment

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

I removed autoSearchOnEmpty from the state and also removed the 2 places where it's read and written, and everything seems to work fine. I'd say it's safe to remove

webpack/components/Search/Search.js Show resolved Hide resolved
webpack/components/Search/Search.js Show resolved Hide resolved
import { connect } from 'react-redux';
import * as settingActions from 'foremanReact/components/Settings/SettingsActions';
import { selectSettings } from '../../scenes/Settings/SettingsSelectors';
import Search from './Search';
Copy link
Member

Choose a reason for hiding this comment

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

Oh. This answers my question from above. Seems like <Search> is mostly moved code rather than new code. In that case, take your pick what to address 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ya its mostly moved out of the index.js. I'll keep it as a class component but maybe there are a couple places i can clean up like removing .bind(this) 🧹

Copy link
Member

Choose a reason for hiding this comment

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

+1 to removing the .binds 😄

self.set('autosearch_while_typing', N_('For pages that support it, automatically perform search while typing in search input.'),
true, N_('Automatic Search while Typing')),
self.set('autosearch_delay', N_('Delay in milliseconds before performing search for automatic searching.'),
500, N_('Automatic Search Typing Delay'))
Copy link
Member

Choose a reason for hiding this comment

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

This shows up in Settings before the other one:

Automatic Search Typing Delay
-- | -- | --
Automatic Search while Typing

It appears to be alphabetical. Would make more sense if the main setting showed first, followed by the delay setting. Would naming them "Search as you Type" and "Search as you Type Delay" work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@MariSvirik any thoughts on the naming of these settings?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

decided on Autosearch and Autosearch delay (May not be one word)

Choose a reason for hiding this comment

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

According to the content team, it should be one word (not even hyphened) in order to be consistent with the way IBM hyphens their words.

@johnpmitsch
Copy link
Contributor Author

I really do miss the X button in the input. I'm guessing that's already decided on?

@MariSvirik any thoughts on this? Should we keep the X clear button for the auto search as well?

@MariSvirik
Copy link

Decision after meeting: add X button to the right edge of the searchbar to provide the easy way for the query deletion.

John Mitsch added 5 commits June 19, 2020 17:43
I would like to get away from the move_to_* folders. Our intent can still be to move the components to patternfly or foreman, but we don't necessarily have to call our folders that. These "temporary" folders have been here for years and we should take steps to be realistic about what we can and can't move and not organize our folders based on these assumptions, which makes things harder to find.
@johnpmitsch
Copy link
Contributor Author

johnpmitsch commented Jun 19, 2020

@jeremylenz @MariSvirik Updated the setting names!

(@jeremylenz use Setting::Content.destroy_all! in the rails console and restart the server to update them)

here

Also added an "X" when autosearch is enabled and there is input

here

I still have to update the one test discussed here and fix some linting, but pushing up what I have so far for feedback :)
EDIT: updated!

@johnpmitsch
Copy link
Contributor Author

[test katello]

Copy link
Member

@jeremylenz jeremylenz left a comment

Choose a reason for hiding this comment

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

looking good @johnpmitsch! Just a few comments below

app/models/setting/content.rb Outdated Show resolved Hide resolved
webpack/components/Search/Search.js Show resolved Hide resolved
// Prevent empty search when page first loads, but search when input is cleared after that
this.setState({ autoSearchOnEmpty: true });
Copy link
Member

Choose a reason for hiding this comment

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

I removed autoSearchOnEmpty from the state and also removed the 2 places where it's read and written, and everything seems to work fine. I'd say it's safe to remove

import { connect } from 'react-redux';
import * as settingActions from 'foremanReact/components/Settings/SettingsActions';
import { selectSettings } from '../../scenes/Settings/SettingsSelectors';
import Search from './Search';
Copy link
Member

Choose a reason for hiding this comment

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

+1 to removing the .binds 😄

Comment on lines 18 to 20
e, isOpen, activeItems, highlightedIndex,
selectItem, userInputValue, onSearch,
)}
Copy link
Member

Choose a reason for hiding this comment

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

This looks fine on my local system.. weird

webpack/global_test_setup.js Show resolved Hide resolved
webpack/scenes/ContentViews/Table/ContentViewsTable.js Outdated Show resolved Hide resolved
@johnpmitsch
Copy link
Contributor Author

@jeremylenz updated!

Copy link
Member

@jeremylenz jeremylenz left a comment

Choose a reason for hiding this comment

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

Changes look good, thanks @johnpmitsch !

@johnpmitsch johnpmitsch merged commit 8f59266 into Katello:master Jun 24, 2020
@johnpmitsch johnpmitsch deleted the cvsearch branch June 24, 2020 19:11
Comment on lines +151 to +154
self.set('autosearch_while_typing', N_('For pages that support it, automatically perform search while typing in search input.'),
true, N_('Autosearch')),
self.set('autosearch_delay', N_('If Autosearch is enabled, delay in milliseconds before executing searches while typing.'),
500, N_('Autosearch delay'))
Copy link
Member

Choose a reason for hiding this comment

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

TBH i don't think these should be configurable settings. The delay should be set to a reasonable default and the auto search should only be enabled on pages where it can return result in a timely fashion without hammering the server with requests. (i.e. <100 ms to get results for a full db)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@tbrisker I am planning on running some production tests to see how well the autosearch performs on a servers with a lot of Content Views. I think that will allow us to make better decisions here on whether A) Autosearch should be enabled by default and B) what the delay should be. It's possible that we don't enable autosearch by default if it is not performant.

I agree we could do without the delay setting, but it would be nice to see how things perform in production first. I'm hesitant to remove it now because of how experimental this feature is. Does that sound ok to you?

Copy link
Member

Choose a reason for hiding this comment

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

My opinion is that since these settings only affect an experimental page while cluttering the settings page for all users, my preference would be to drop them completely and try to go with "[hopefully] sane default" first and only add a setting if needed in the future and we can't settle on a default that works for all users.

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.

5 participants