-
Notifications
You must be signed in to change notification settings - Fork 73
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
feat: library home page bare bones [FC-0059] #1076
feat: library home page bare bones [FC-0059] #1076
Conversation
Thanks for the pull request, @rpenido! Please note that it may take us up to several weeks or months to complete a review and merge your PR. Feel free to add as much of the following information to the ticket as you can:
All technical communication about the code itself will be done via the GitHub pull request interface. As a reminder, our process documentation is here. Please let us know once your PR is ready for our review and all tests are green. |
d212fa9
to
d1b529c
Compare
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #1076 +/- ##
==========================================
+ Coverage 92.67% 92.68% +0.01%
==========================================
Files 696 693 -3
Lines 12392 12347 -45
Branches 2714 2694 -20
==========================================
- Hits 11484 11444 -40
+ Misses 876 872 -4
+ Partials 32 31 -1 ☔ View full report in Codecov by Sentry. |
8ef4e0c
to
72edfac
Compare
const AppHeader = ({ | ||
courseNumber, courseOrg, courseTitle, courseId, | ||
}) => ( | ||
<Header | ||
courseNumber={courseNumber} | ||
courseOrg={courseOrg} | ||
courseTitle={courseTitle} | ||
courseId={courseId} | ||
/> | ||
); | ||
|
||
AppHeader.propTypes = { | ||
courseId: PropTypes.string.isRequired, | ||
courseNumber: PropTypes.string, | ||
courseOrg: PropTypes.string, | ||
courseTitle: PropTypes.string.isRequired, | ||
}; | ||
|
||
AppHeader.defaultProps = { | ||
courseNumber: null, | ||
courseOrg: null, | ||
}; | ||
|
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 component was just passing parameters to <Header />
.
src/header/Header.jsx
Outdated
contentId, | ||
org, | ||
number, | ||
title, | ||
isHiddenMainMenu, | ||
isLibrary, | ||
}) => { |
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 are using the same Header for courses and libraries now.
src/header/Header.jsx
Outdated
/> | ||
{ meiliSearchEnabled && ( | ||
<SearchModal | ||
isOpen={isShowSearchModalOpen} | ||
courseId={courseId} | ||
courseId={isLibrary ? undefined : contentId} |
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.
ToDo: We can make the search modal aware of whether the current library is the same way we do with courses. The search will work out of the box, but we need to revisit the page to change some wording ("all courses", "current course", etc..)
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.
For now, we want the modal to only search courses and we're not going to show "Search all Courses and Libraries" even though it's possible.
To search libraries, users will have to click on a specific library and use the new search UI we're building, which is not in a modal.
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.
Ack! @bradenmacdonald
For now, we want the modal to only search courses and we're not going to show "Search all Courses and Libraries" even though it's possible.
It is worth to mention that currently we show library blocks in the modal and redirect to the parent library if you click on a hit.
Would you like me to include a fix in this PR to limit it only to courses?
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.
Right, yes please - let's keep that functionality in the code but for now add an additional filter to limit the search modal results to courses. Thanks.
/** | ||
* Hook to fetch the count of components and collections in a library. | ||
* @param {string} libraryId - The ID of the library to fetch. | ||
* @param {string} searchKeywords - Keywords to search for. | ||
*/ | ||
export const useLibraryComponentCount = (libraryId, searchKeywords) => { | ||
// Meilisearch code to get Collection and Component counts | ||
const { data: connectionDetails } = useContentSearchConnection(); | ||
|
||
const indexName = connectionDetails?.indexName; | ||
const client = React.useMemo(() => { | ||
if (connectionDetails?.apiKey === undefined || connectionDetails?.url === undefined) { | ||
return undefined; | ||
} | ||
return new MeiliSearch({ host: connectionDetails.url, apiKey: connectionDetails.apiKey }); | ||
}, [connectionDetails?.apiKey, connectionDetails?.url]); | ||
|
||
const libFilter = `context_key = "${libraryId}"`; | ||
|
||
const { totalHits: componentCount } = useContentSearchResults({ | ||
client, | ||
indexName, | ||
searchKeywords, | ||
extraFilter: [libFilter], // ToDo: Add filter for components when collection is implemented | ||
}); | ||
|
||
const collectionCount = 0; // ToDo: Implement collections count | ||
|
||
return { | ||
componentCount, | ||
collectionCount, | ||
}; | ||
}; |
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 created this hook to get only the component count for the library. Maybe we should replace this with another instance of the SearchContext
when we add other filters. @ChrisChV @bradenmacdonald
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.
Yes, when we get there what I want to do is move most of the "generic" search code out of search-modal
into somewhere like generic/search/
and then we can use it in both search-modal/SearchModal.tsx
and src/library-authoring/LibraryHome.tsx
etc.
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.
@bradenmacdonald
I was thinking of exporting this directly from our search-modal
feature (as I did in this file here). I know the current name is not the best, but it would be our search-engine
feature that exports both the modal and the provider to use the search engine elsewhere. Maybe rename this feature to search-engine
to make it explicit.
Or do you think having two features (search-modal
and search-engine
) would work best?
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.
And, just to confirm: we are moving to use the search index as the primary source for listing content, right?
We might need to move some indexing tasks to run sync because if we add or remove a library, we could refetch before the index update and end up with out of sync data.
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.
Yes, exactly. When we make changes to libraries / library content, we can update the search index synchronously.
It's fine to export from search-modal for now, but I do think it's better to separate the library (search manager) from the two use cases: library home UI, and search modal.
Sandbox deployment successful 🚀 |
Sandbox deployment successful 🚀 |
Sandbox deployment successful 🚀 |
Sandbox deployment successful 🚀 |
18a8482
to
2d3be09
Compare
d197577
to
905dbb5
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.
👍 @rpenido Looks good to me! Great work!
- I tested this: I followed the testing instructions in the PR, and everything is working as expected.
- I read through the code
- I checked for accessibility issues
-
Includes documentation
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.
Just a few minor nits other than that it's good to go!
src/header/Header.tsx
Outdated
]; | ||
const outlineLink = `${studioBaseUrl}/course/${courseId}`; | ||
] : []; | ||
const outlineLink = !isLibrary ? `${studioBaseUrl}/course/${contentId}` : `/course-authoring/library/${contentId}`; |
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.
The /course-authoring/
part is configurable. I'd recommend using generatePath from react router for this.
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.
Done: 9937a59
<FormattedMessage {...messages.noComponents} /> | ||
<Button iconBefore={Add}> | ||
<FormattedMessage {...messages.addComponent} /> |
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 isn't specific to this code, but I've seen this pattern a lot and I don't personally see the value in it.
To me the advantage of using FormattedMessage is that you can see the actual text in the react code instead of looking at the messages file. In this case it's better to use intl.formatMessage
instead. FormattedMessage
just uses useIntl
internally anyway.
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.
Are you saying to write { intl.formatMessage(messages.addComponent) }
? That's totally fine, but requires changing this function to have a statement body to be able to include const intl = useIntl()
in scope, so writing it this way uses one less line of code, and I think is just as simple.
const TAB_LIST = { | ||
home: '', | ||
components: 'components', | ||
collections: 'collections', | ||
}; | ||
|
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.
Since we're using TypeScript now, this could be an enum?
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! Done: 3b2a6ad
isHiddenMainMenu={isHiddenMainMenu} | ||
mainMenuDropdowns={mainMenuDropdowns} | ||
outlineLink={outlineLink} | ||
searchButtonAction={meiliSearchEnabled && openSearchModal} | ||
searchButtonAction={meiliSearchEnabled ? openSearchModal : undefined} |
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.
Let's hide the search button for libraries please.
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 don't think we agree here. Shouldn't we have the "same" header on all pages? I thought the search modal was a quick jump to content from anywhere in the course authoring. Could you elaborate a bit more?
Currently, the search icon appears in "all" pages (i.e., Taxonomy pages). We would need to create a new parameter for our header to hide it. Let me know if you think we want to go to this route!
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.
Well I think people will be confused that the search icon on the library pages doesn't search libraries. But we can leave it for now and see what the product/UX folks say later when they review and try it out on the sandbox. It's a bit of an issue that the UX for searching libraries and the UX for searching courses are so completely different.
</QueryClientProvider> | ||
</IntlProvider> | ||
</AppProvider> | ||
); |
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.
Not for this PR, but we should really make a re-usable RootWrapper
for all our test files instead of writing this out in each one.
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.
Thank you @bradenmacdonald . I was a bit divided about this before.
This structure explicitly states what is needed for this particular component, but it also adds a lot of boilerplate to the tests.
For the next tests, I will add some helpers for this (and the meilisearch stuff).
|
||
return ( | ||
<Stack gap={3}> | ||
<Section title="Recently Modified"> |
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.
These titles need i18n
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.
Fixed: 2a63186
/** | ||
* Hook to fetch the count of components and collections in a library. | ||
*/ | ||
export const useLibraryComponentCount = (libraryId: string, searchKeywords: string) => { |
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 temporary right? In the future we should have a <SearchContextProvider>
on the Library Home page, and there should be no need to initialize a Meilisearch client manually.
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.
Yes! This was refactored here: #1141
src/search-modal/index.js
Outdated
@@ -0,0 +1,3 @@ | |||
// @ts-check |
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.
Just make this file .ts
please. Or do we really need this file?
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 this is good for helping us avoid inter-feature import. We should not import from ./seach-modal/Something
. We should always import from explicitly exported members from ./search-modal
9565a4b
to
3b2a6ad
Compare
This seems to fix the issue: 01e817a |
Thank you for the reviews @bradenmacdonald and @xitij2000! This is ready for another round. |
Nice work on addressing those comments, @rpenido. @xitij2000 did you want to look at this again before we merge it? |
@rpenido 🎉 Your pull request was merged! Please take a moment to answer a two question survey so we can improve your experience in the future. |
Description
This PR adds a "Bare Bones" version of the Library Home and updates the SearchResult component to redirect to this new page.
More Info
Testing Instructions
Private ref: FAL-3753
Settings
Tutor requirements