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

feat: improve search modal results [FC-0040] #946

Conversation

rpenido
Copy link
Contributor

@rpenido rpenido commented Apr 11, 2024

Description

This PR improves the SearchResult component, which shows the hits for the search feature.

search-results

More Infomation

Testing Instructions

Setup

  1. Run your local stack on this branch
  2. Make sure you have meilisearch setup locally, follow the setup instructions here https://github.com/open-craft/tutor-contrib-meilisearch
  3. Make sure you also have some sample taxonomies/tags, you can add some from here: https://github.com/open-craft/taxonomy-sample-data
  4. Also, verify if the contentstore.new_studio_mfe.use_new_course_outline_page waffle flag is enabled.

Testing

  1. Click on the search icon in the Studio Header
  2. Do some sample searches and verify the results
  3. Clicking in the result row must redirect to the course/library page of the content
  4. Clicking in the navigate icon at the right of the row should open the content context in a new tab
    NOTE If the content is visible on the page (not collapsed), the page will be scrolled and the content will be highlighted for 5 seconds. This will NOT happen if the content is inside a collapsed parent.

Private ref: FAL-3695

@openedx-webhooks
Copy link

openedx-webhooks commented Apr 11, 2024

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:

  • supporting documentation
  • Open edX discussion forum threads
  • timeline information ("this must be merged by XX date", and why that is)
  • partner information ("this is a course on edx.org")
  • any other information that can help Product understand the context for the PR

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.

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Apr 11, 2024
@rpenido rpenido force-pushed the rpenido/fal-3695-implement-search-modal-format-results branch 2 times, most recently from 5fc9b54 to 21e7cab Compare April 11, 2024 17:59
@rpenido rpenido force-pushed the rpenido/fal-3695-implement-search-modal-format-results branch 3 times, most recently from bb31469 to 6230900 Compare April 11, 2024 18:42
@rpenido rpenido force-pushed the rpenido/fal-3695-implement-search-modal-format-results branch from 6230900 to 14ded36 Compare April 11, 2024 19:15
Comment on lines 14 to 31

div.row:has(> div > div.highlight) {
animation: 5s glow;
}

@keyframes glow {
0% {
box-shadow: 0 0 5px 5px $primary-500;
}

90% {
box-shadow: 0 0 5px 5px $primary-500;
}

100% {
box-shadow: unset;
}
}
Copy link
Contributor Author

@rpenido rpenido Apr 11, 2024

Choose a reason for hiding this comment

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

This is the animation after scrolling to a searched component. Personally, I didn't like it. 😞
I am open to suggestions! @ali-hugo

PS: The highlight only happens if the parent item is expanded and the component is on screen. We could improve it with a follow-up task.

section-highlight

subsection-highlight

unit-highlight

Copy link
Contributor

Choose a reason for hiding this comment

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

@rpenido I agree that the scrolling is awkward.. but I can't think of a better place to send someone who's searched for a Section/Subsection.

However, if they search for a Unit, I think we should direct them to the Unit edit page instead of scrolling down the outline, because Units are very unlikely to be expanded on that page. So then it's just Sections and Subsections that use the scrolling thing, and Units and components redirect to the Unit edit page.

@ali-hugo What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The Unit redirect will be fixed in a follow-up task!

Copy link
Contributor

Choose a reason for hiding this comment

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

What seems to be the issue with the animation? I think one thing that can be done to make it smoother is to use a timing function.

i.e. remove the 90% step and add a timing function like:
animation-timing-function: cubic-bezier(1,0,.72,.04);

This will be a lot smoother I think since it'll follow a curve rather than a steep fall.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What seems to be the issue with the animation?

Nothing specific. I just didn't find it "wow" (I'm not sure if it was the color, the glow size..). I think it's a subjective feeling, and I'm open to options to improve it (if that's the case).

Changed the curve here: 12ba0c5

new-animation

}

navigate(url);
closeSearch?.();
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 had to pass the closeSearch function down the path SearchModal > SearchEndpointLoader > SearchUI > SearchResult to close the modal. Maybe we should add a context to handle this state?

Copy link
Contributor

Choose a reason for hiding this comment

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

Is that how our other modals work? If so, then yes, I agree that's worth doing here too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't have this problem in the other modals because we don't have a deep inner component interacting with it.
The current approach is the simple-but-not-so-beauty way. We could hear more from cc/upstream review if we can improve it without adding complexity.

Copy link
Contributor

Choose a reason for hiding this comment

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

I added it to a context in my PR when I merged it with this one - see here.

Comment on lines +1 to +4
declare module '*.svg' {
const content: string;
export default content;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

To support importing svg files as image source

@rpenido rpenido marked this pull request as ready for review April 11, 2024 21:59
@rpenido rpenido requested a review from a team as a code owner April 11, 2024 21:59
@rpenido rpenido requested a review from pomegranited April 11, 2024 22:44
Copy link
Contributor

@pomegranited pomegranited left a comment

Choose a reason for hiding this comment

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

Hi @rpenido , this is looking and working beautifully! I found a couple of minor nits and a couple of omissions from the Figma designs, noted inline.

const { context_key: contextKey, usage_key: usageKey } = hit;
if (contextKey.startsWith('course-v1:')) {
if (newWindow) {
return `${getPath(getConfig().PUBLIC_PATH)}/course/${contextKey}?show=${encodeURIComponent(usageKey)}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

getPath returns a trailing /, so this link ends up with //course/ in the path, and fails to load.

Suggested change
return `${getPath(getConfig().PUBLIC_PATH)}/course/${contextKey}?show=${encodeURIComponent(usageKey)}`;
return `${getPath(getConfig().PUBLIC_PATH)}course/${contextKey}?show=${encodeURIComponent(usageKey)}`;

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, since these returned URLs share a common path string, can that be reflected in the code too?

Copy link
Contributor

Choose a reason for hiding this comment

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

And finally: Figma says that when we click on a component (text, video, etc), we should be directed to the Unit edit page with the component highlighted. instead of just trying to scroll down to the Unit in the outline.

(I also think searching for a Unit should take us to the Unit edit page too, but that's up to UI/UX, see above :)

Copy link
Contributor Author

@rpenido rpenido Apr 12, 2024

Choose a reason for hiding this comment

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

Also, since these returned URLs share a common path string, can that be reflected in the code too?

Sure! Done here: 53d70e3

Copy link
Contributor Author

Choose a reason for hiding this comment

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

And finally: Figma says that when we click on a component (text, video, etc), we should be directed to the Unit edit page with the component highlighted. instead of just trying to scroll down to the Unit in the outline.

Ack! We will need a follow-up task to that because right now, we don't have the unit info in the component result.

return `/course/${contextKey}?show=${encodeURIComponent(usageKey)}`;
}
if (usageKey.includes('lb:')) {
return `${libraryAuthoringMfeUrl}library/${contextKey}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Not everyone has the library authoring MFE enabled.. and I don't think it'll be ready for Redwood either.. Is there some MFE way of detecting this and getting the right base URL to use?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes!
I made a refactor here: 6cb2d96
If we don't have the redirect URL the hit will be shown as disabled.

}
return `/course/${contextKey}?show=${encodeURIComponent(usageKey)}`;
}
if (usageKey.includes('lb:')) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit -- since you used startsWith above:

Suggested change
if (usageKey.includes('lb:')) {
if (usageKey.startsWith('lb:')) {

Someday soon we're going to want MFE libraries for opaque keys.. but that's out of scope here :)

Copy link
Contributor Author

@rpenido rpenido Apr 12, 2024

Choose a reason for hiding this comment

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

Done: 53d70e3

}

navigate(url);
closeSearch?.();
Copy link
Contributor

Choose a reason for hiding this comment

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

Is that how our other modals work? If so, then yes, I agree that's worth doing here too.

gap={2}
>
<div className="align-top">
<Icon className="align-top" src={ItemIcon[hit.block_type] || Article} />
Copy link
Contributor

Choose a reason for hiding this comment

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

The icons in Figma look to be a lighter grey, and maybe a bit more padding between the icon and the text?

Figma:

image

MFE:

image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done: f1b3b62

image

<Snippet attribute="content.capa_content" hit={hit} highlightedTagName="b" />
</div>
<div className="text-muted x-small">
<CustomHighlight attribute="breadcrumbsNames" separator=" / " hit={hit} />
Copy link
Contributor

Choose a reason for hiding this comment

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

The figma designs omit the Course Title from the breadcrumbs when searching just "This course".

The Course Title should only be shown if we're searching "All courses".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done: c152a42

Comment on lines 14 to 31

div.row:has(> div > div.highlight) {
animation: 5s glow;
}

@keyframes glow {
0% {
box-shadow: 0 0 5px 5px $primary-500;
}

90% {
box-shadow: 0 0 5px 5px $primary-500;
}

100% {
box-shadow: unset;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

@rpenido I agree that the scrolling is awkward.. but I can't think of a better place to send someone who's searched for a Section/Subsection.

However, if they search for a Unit, I think we should direct them to the Unit edit page instead of scrolling down the outline, because Units are very unlikely to be expanded on that page. So then it's just Sections and Subsections that use the scrolling thing, and Units and components redirect to the Unit edit page.

@ali-hugo What do you think?

@rpenido rpenido force-pushed the rpenido/fal-3695-implement-search-modal-format-results branch from b71b0b5 to 53d70e3 Compare April 12, 2024 17:44
@rpenido rpenido force-pushed the rpenido/fal-3695-implement-search-modal-format-results branch from f98778b to 6cb2d96 Compare April 12, 2024 18:14
@rpenido rpenido requested a review from pomegranited April 12, 2024 19:01
@bradenmacdonald
Copy link
Contributor

@rpenido I noticed a couple warnings in the console:

  1. Failed prop type: The prop alt is marked as required in ForwardRef(_c), but its value is undefined. on <IconButton> used in <SearchResult>
  2. validateDOMNesting(...): <button> cannot appear as a descendant of <button>. - seems to be this problem: Search Result contains a <Button> which contains a <Stack> which contains an <IconButton>. You can't do that - change the outer one to a <div>. If it needs to be a button give it role=button or use <Button as={div}> but it's probably better not to be a button if it contains a button.

Copy link
Contributor

@pomegranited pomegranited 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 addressing my comments @rpenido 👍

I've left a comment about missing tests, but I'm ok with them being covered by your follow-up task.

  • I tested this on my tutor dev stack using the PR test instructions
  • I read through the code
  • I checked for accessibility issues by navigating with my keyboard.
  • Includes documentation
  • User-facing strings are extracted for translation

@@ -59,7 +64,7 @@ describe('<SearchModal />', () => {
apiKey: 'test-api-key',
});
const { findByText } = render(<RootWrapper />);
expect(await findByText('Enter a keyword or select a filter to begin searching.')).toBeInTheDocument();
expect(await findByText('Start searching to find content')).toBeInTheDocument();
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add more tests to cover the added functionality for this SearchModal?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

More tests: 879a395, 66612bc, 58a5c8c

Copy link
Contributor

@xitij2000 xitij2000 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 this is good to go on my side. Just very few small issues that I think would be nice if addressed.

Comment on lines 67 to 69
.fs-large {
font-size: $font-size-base * 1.25;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

You can use the class lead instead of using this. ref

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice! Thank you!
Done: 78e0a81

Comment on lines 12 to 27
const EmptySearch = () => (
<Stack className="d-flex mt-6 align-items-center">
<p className="fs-large"> <FormattedMessage {...messages.emptySearchTitle} /> </p>
<p className="small text-muted"> <FormattedMessage {...messages.emptySearchSubtitle} /> </p>
<img src={EmptySearchImage} alt="" />
</Stack>
);

const NoResults = () => (
<Stack className="d-flex mt-6 align-items-center">
<p className="fs-large"> <FormattedMessage {...messages.noResultsTitle} /> </p>
<p className="small text-muted"> <FormattedMessage {...messages.noResultsSubtitle} /> </p>
<img src={NoResultImage} alt="" />
</Stack>
);

Copy link
Contributor

Choose a reason for hiding this comment

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

I think these should be deduplicated since they have the same structure, and will likely need to always have the same structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done: 78e0a81

Comment on lines 14 to 31

div.row:has(> div > div.highlight) {
animation: 5s glow;
}

@keyframes glow {
0% {
box-shadow: 0 0 5px 5px $primary-500;
}

90% {
box-shadow: 0 0 5px 5px $primary-500;
}

100% {
box-shadow: unset;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

What seems to be the issue with the animation? I think one thing that can be done to make it smoother is to use a timing function.

i.e. remove the 90% step and add a timing function like:
animation-timing-function: cubic-bezier(1,0,.72,.04);

This will be a lot smoother I think since it'll follow a curve rather than a steep fall.

@rpenido rpenido force-pushed the rpenido/fal-3695-implement-search-modal-format-results branch from 838c55e to 4860614 Compare April 16, 2024 13:39
@rpenido
Copy link
Contributor Author

rpenido commented Apr 16, 2024

@rpenido I noticed a couple warnings in the console:

1. Failed prop type: The prop `alt` is marked as required in `ForwardRef(_c)`, but its value is `undefined`. on `<IconButton>` used in `<SearchResult>`

2. `validateDOMNesting(...): <button> cannot appear as a descendant of <button>.` - seems to be this problem: Search Result contains a `<Button>` which contains a `<Stack>` which contains an `<IconButton>`. You can't do that - change the outer one to a `<div>`. If it needs to be a button give it `role=button` or use `<Button as={div}>` but it's probably better not to be a button if it contains a button.

Thank you for catching that @bradenmacdonald!
Fixed here: 8e468ef
It needed a bit more of a boilerplate to make it keyboard-friendly.

@rpenido rpenido force-pushed the rpenido/fal-3695-implement-search-modal-format-results branch from 4860614 to 8e468ef Compare April 16, 2024 13:59
@rpenido rpenido force-pushed the rpenido/fal-3695-implement-search-modal-format-results branch from 12ba0c5 to 079e52e Compare April 16, 2024 19:32
Copy link

codecov bot commented Apr 16, 2024

Codecov Report

Attention: Patch coverage is 96.72131% with 2 lines in your changes are missing coverage. Please review.

Project coverage is 92.09%. Comparing base (b119671) to head (7d037f2).

Files Patch % Lines
src/search-modal/SearchResult.jsx 93.93% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #946      +/-   ##
==========================================
+ Coverage   92.06%   92.09%   +0.02%     
==========================================
  Files         634      634              
  Lines       11029    11080      +51     
  Branches     2367     2387      +20     
==========================================
+ Hits        10154    10204      +50     
- Misses        846      847       +1     
  Partials       29       29              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@rpenido rpenido force-pushed the rpenido/fal-3695-implement-search-modal-format-results branch from 3e52a20 to 879a395 Compare April 16, 2024 21:05
@rpenido rpenido force-pushed the rpenido/fal-3695-implement-search-modal-format-results branch from 17e4012 to 66612bc Compare April 16, 2024 21:52
@rpenido rpenido force-pushed the rpenido/fal-3695-implement-search-modal-format-results branch from 8cd53fe to 7d037f2 Compare April 17, 2024 04:59
@xitij2000 xitij2000 merged commit 612d1d8 into openedx:master Apr 17, 2024
6 checks passed
@openedx-webhooks
Copy link

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

@rpenido rpenido deleted the rpenido/fal-3695-implement-search-modal-format-results branch April 17, 2024 12:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
open-source-contribution PR author is not from Axim or 2U
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

[Course Search] Search Modal: Results
5 participants