UI search UI a11y changes (#1253)

* fix: Updated UI a11y changes in Courseware Search

* feat: Removed result count for search

* fix: Added new line at the end of course-tabs-navigation.scss
This commit is contained in:
Marcos
2023-12-15 09:23:34 -03:00
committed by GitHub
parent e004ead21d
commit 2bf326fc67
10 changed files with 83 additions and 59 deletions

View File

@@ -44,6 +44,7 @@ export const CoursewareSearchResultsFilter = ({ intl }) => {
return (
<Tabs
id="courseware-search-results-tabs"
className="courseware-search-results-tabs"
data-testid="courseware-search-results-tabs"
variant="tabs"
activeKey={activeKey}

View File

@@ -103,7 +103,7 @@ const CoursewareSearch = ({ intl, ...sectionProps }) => {
</div>
<div className="courseware-search__outer-content">
<div className="courseware-search__content">
<h2>{intl.formatMessage(messages.searchModuleTitle)}</h2>
<h1 class="h2">{intl.formatMessage(messages.searchModuleTitle)}</h1>
<CoursewareSearchForm
searchTerm={searchKeyword}
onSubmit={handleSubmit}
@@ -122,15 +122,15 @@ const CoursewareSearch = ({ intl, ...sectionProps }) => {
)}
{status === 'results' ? (
<>
<div className="courseware-search__results-summary" data-testid="courseware-search-summary">{total > 0
? (
intl.formatMessage(
total === 1
? messages.searchResultsSingular
: messages.searchResultsPlural,
{ total, keyword: lastSearchKeyword },
)
) : intl.formatMessage(messages.searchResultsNone)}
<div
className="courseware-search__results-summary"
aria-live="polite"
aria-relevant="all"
aria-atomic="true"
data-testid="courseware-search-summary"
>{total > 0
? intl.formatMessage(messages.searchResultsLabel, { total, keyword: lastSearchKeyword })
: intl.formatMessage(messages.searchResultsNone)}
</div>
<CoursewareSearchResultsFilterContainer />
</>

View File

@@ -246,24 +246,14 @@ describe('CoursewareSearch', () => {
expect(screen.queryByTestId('courseware-search-summary').textContent).toBe('No results found.');
});
it('should show a summary for a single result', () => {
it('should show a summary for the results', () => {
mockModels({
searchKeyword: 'fubar',
total: 1,
});
renderComponent();
expect(screen.queryByTestId('courseware-search-summary').textContent).toBe('1 match found for "fubar":');
});
it('should show a summary for multiple results', () => {
mockModels({
searchKeyword: 'fubar',
total: 2,
});
renderComponent();
expect(screen.queryByTestId('courseware-search-summary').textContent).toBe('2 matches found for "fubar":');
expect(screen.queryByTestId('courseware-search-summary').textContent).toBe('Results for "fubar":');
});
});

View File

@@ -1,8 +1,11 @@
import React from 'react';
import { SearchField } from '@edx/paragon';
import PropTypes from 'prop-types';
import { SearchField } from '@edx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import messages from './messages';
const CoursewareSearchForm = ({
intl,
searchTerm,
onSubmit,
onChange,
@@ -14,17 +17,27 @@ const CoursewareSearchForm = ({
onChange={onChange}
submitButtonLocation="external"
className="courseware-search-form"
screenReaderText={{
label: intl.formatMessage(messages.searchSubmitLabel),
clearButton: intl.formatMessage(messages.searchClearAction),
submitButton: null, // Remove the sr-only label in the button.
}}
>
<div className="pgn__searchfield_wrapper" data-testid="courseware-search-form">
<SearchField.Label />
<SearchField.Input placeholder={placeholder} autoFocus />
<SearchField.ClearButton />
</div>
<SearchField.SubmitButton submitButtonLocation="external" data-testid="courseware-search-form-submit" />
<SearchField.SubmitButton
buttonText={intl.formatMessage(messages.searchSubmitLabel)}
submitButtonLocation="external"
data-testid="courseware-search-form-submit"
/>
</SearchField.Advanced>
);
CoursewareSearchForm.propTypes = {
intl: intlShape.isRequired,
searchTerm: PropTypes.string,
onSubmit: PropTypes.func,
onChange: PropTypes.func,
@@ -38,4 +51,4 @@ CoursewareSearchForm.defaultProps = {
placeholder: undefined,
};
export default CoursewareSearchForm;
export default injectIntl(CoursewareSearchForm);

View File

@@ -47,8 +47,8 @@ describe('CoursewareSearchToggle', () => {
it('should call onSubmit handler when submit is clicked', async () => {
await act(async () => renderComponent(placeholderText, onSubmitHandlerMock, onChangeHandlerMock));
await waitFor(() => {
const element = screen.queryAllByText('Search')[0];
await waitFor(async () => {
const element = await screen.findByTestId('courseware-search-form-submit');
fireEvent.click(element);
expect(onSubmitHandlerMock).toHaveBeenCalledTimes(1);
});

View File

@@ -35,7 +35,7 @@
&__results-summary {
font-size: .9rem;
color: $gray-400;
color: $gray-500;
padding: 1rem 0 .5rem;
}
@@ -111,7 +111,7 @@
&__breadcrumbs {
display: flex;
gap: 1.25rem;
color: $gray-400;
color: $gray-500;
overflow: hidden;
list-style: none;
padding: 0;
@@ -141,6 +141,14 @@
}
}
.courseware-search-results-tabs {
border-bottom-color: $gray-400 !important;
&.nav-tabs .nav-link.active {
border-bottom-width: 4px !important;
}
}
@media (min-width: map-get($grid-breakpoints, 'md')) {
.courseware-search__content {
padding-top: 8rem;

View File

@@ -6,6 +6,16 @@ const messages = defineMessages({
defaultMessage: 'Search within this course',
description: 'Aria-label for a button that will pop up Courseware Search.',
},
searchSubmitLabel: {
id: 'learn.coursewareSerch.submitLabel',
defaultMessage: 'Search',
description: 'Button label that will submit Courseware Search.',
},
searchClearAction: {
id: 'learn.coursewareSerch.clearAction',
defaultMessage: 'Clear search',
description: 'Button label that will the current Courseware Search input.',
},
searchCloseAction: {
id: 'learn.coursewareSerch.closeAction',
defaultMessage: 'Close the search form',
@@ -31,15 +41,10 @@ const messages = defineMessages({
defaultMessage: 'No results found.',
description: 'Text to show when the Courseware Search found no results matching the criteria.',
},
searchResultsSingular: {
id: 'learn.coursewareSerch.searchResultsSingular',
defaultMessage: '1 match found for "{keyword}":',
description: 'Text to show when the Courseware Search found only one result matching the criteria.',
},
searchResultsPlural: {
id: 'learn.coursewareSerch.searchResultsPlural',
defaultMessage: '{total} matches found for "{keyword}":',
description: 'Text to show when the Courseware Search found multiple results matching the criteria.',
searchResultsLabel: {
id: 'learn.coursewareSerch.searchResultsLabel',
defaultMessage: 'Results for "{keyword}":',
description: 'Text to show above the search results response list.',
},
searchResultsError: {
id: 'learn.coursewareSerch.searchResultsError',

View File

@@ -15,9 +15,6 @@ const CourseTabsNavigation = ({
return (
<div id="courseTabsNavigation" className={classNames('course-tabs-navigation', className)}>
<div className="float-right">
<CoursewareSearchToggle />
</div>
<div className="container-xl">
<Tabs
className="nav-underline-tabs"
@@ -34,6 +31,9 @@ const CourseTabsNavigation = ({
))}
</Tabs>
</div>
<div className="course-tabs-navigation__search-toggle">
<CoursewareSearchToggle />
</div>
{show && <CoursewareSearch />}
</div>
);

View File

@@ -0,0 +1,24 @@
.course-tabs-navigation {
position: relative;
border-bottom: solid 1px #eaeaea;
.nav a,
.nav button {
&:hover {
background-color: $light-400;
}
}
.nav a {
&:not(.active):hover {
background-color: $light-400;
border-bottom: none;
}
}
&__search-toggle {
position: absolute;
top: .05rem;
right: 0;
}
}

View File

@@ -38,24 +38,6 @@
}
}
.course-tabs-navigation {
border-bottom: solid 1px #eaeaea;
.nav a,
.nav button {
&:hover {
background-color: $light-400;
}
}
.nav a {
&:not(.active):hover {
background-color: $light-400;
border-bottom: none;
}
}
}
.nav-underline-tabs {
margin: 0 0 -1px;
@@ -396,3 +378,4 @@
@import "courseware/course/course-exit/CourseRecommendations";
@import "product-tours/newUserCourseHomeTour/NewUserCourseHomeTourModal.scss";
@import "course-home/courseware-search/courseware-search.scss";
@import "course-tabs/course-tabs-navigation.scss";