@ndla/ui 23.0.0 → 24.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/Article/ArticleAuthorContent.js +2 -4
- package/es/AuthorInfo/AuthorInfo.js +29 -16
- package/es/ContentCard/ContentCard.js +66 -25
- package/es/FileList/File.js +34 -8
- package/es/FileList/FileList.js +29 -3
- package/es/InfoBox/InfoBox.js +10 -3
- package/es/InfoWidget/InfoWidget.js +67 -22
- package/es/Portrait/Portrait.js +19 -13
- package/es/Search/ActiveFilterContent.js +4 -14
- package/es/Search/ActiveFilters.js +8 -19
- package/es/Search/SearchField.js +31 -52
- package/es/Search/SearchResult.js +113 -136
- package/es/Search/ToggleSearchButton.js +34 -43
- package/es/Search/index.js +2 -8
- package/es/all.css +1 -1
- package/es/index-javascript.js +0 -1
- package/es/index.js +2 -1
- package/lib/Article/ArticleAuthorContent.js +9 -4
- package/lib/AuthorInfo/AuthorInfo.d.ts +1 -11
- package/lib/AuthorInfo/AuthorInfo.js +36 -20
- package/lib/ContentCard/ContentCard.d.ts +1 -15
- package/lib/ContentCard/ContentCard.js +60 -28
- package/lib/FileList/File.js +36 -12
- package/lib/FileList/FileList.js +28 -5
- package/lib/InfoBox/InfoBox.js +11 -4
- package/lib/InfoWidget/InfoWidget.js +61 -25
- package/lib/Portrait/Portrait.js +19 -14
- package/lib/Search/ActiveFilterContent.d.ts +13 -0
- package/lib/Search/ActiveFilterContent.js +4 -15
- package/lib/Search/ActiveFilters.d.ts +13 -0
- package/lib/Search/ActiveFilters.js +8 -20
- package/lib/Search/SearchField.d.ts +19 -0
- package/lib/Search/SearchField.js +32 -56
- package/lib/Search/SearchResult.d.ts +36 -0
- package/lib/Search/SearchResult.js +116 -159
- package/lib/Search/ToggleSearchButton.d.ts +16 -0
- package/lib/Search/ToggleSearchButton.js +36 -46
- package/lib/Search/index.d.ts +12 -0
- package/lib/Search/index.js +0 -54
- package/lib/SearchTypeResult/SearchTypeHeader.d.ts +1 -1
- package/lib/all.css +1 -1
- package/lib/index-javascript.js +0 -74
- package/lib/index.d.ts +1 -0
- package/lib/index.js +38 -1
- package/package.json +5 -5
- package/src/Article/ArticleAuthorContent.tsx +1 -1
- package/src/AuthorInfo/AuthorInfo.tsx +53 -19
- package/src/ContentCard/ContentCard.tsx +127 -35
- package/src/FileList/File.tsx +47 -17
- package/src/FileList/FileList.tsx +37 -8
- package/src/InfoBox/InfoBox.tsx +24 -4
- package/src/InfoWidget/InfoWidget.tsx +83 -34
- package/src/Portrait/Portrait.tsx +25 -10
- package/src/Search/{ActiveFilterContent.jsx → ActiveFilterContent.tsx} +11 -12
- package/src/Search/{ActiveFilters.jsx → ActiveFilters.tsx} +20 -17
- package/src/Search/{SearchField.jsx → SearchField.tsx} +58 -68
- package/src/Search/SearchResult.tsx +360 -0
- package/src/Search/ToggleSearchButton.tsx +73 -0
- package/src/Search/component.search.scss +0 -4
- package/src/Search/index.ts +16 -0
- package/src/all.scss +0 -1
- package/src/index-javascript.js +0 -15
- package/src/index.ts +2 -0
- package/src/main.scss +0 -6
- package/es/Search/SearchFilter.js +0 -72
- package/es/Search/SearchFilterList.js +0 -115
- package/es/Search/SearchOverlay.js +0 -39
- package/es/Search/SearchPage.js +0 -178
- package/es/Search/SearchPopoverFilter.js +0 -152
- package/es/Search/SearchResultAuthor.js +0 -51
- package/lib/Search/SearchFilter.js +0 -88
- package/lib/Search/SearchFilterList.js +0 -137
- package/lib/Search/SearchOverlay.js +0 -62
- package/lib/Search/SearchPage.js +0 -207
- package/lib/Search/SearchPopoverFilter.js +0 -172
- package/lib/Search/SearchResultAuthor.js +0 -60
- package/src/AuthorInfo/component.author-info.scss +0 -54
- package/src/ContentCard/component.content-card.scss +0 -109
- package/src/FileList/component.file-list.scss +0 -102
- package/src/InfoBox/component.info-box.scss +0 -21
- package/src/InfoWidget/component.info-widget.scss +0 -52
- package/src/Portrait/component.portrait.scss +0 -29
- package/src/Search/SearchFilter.jsx +0 -82
- package/src/Search/SearchFilterList.jsx +0 -110
- package/src/Search/SearchOverlay.jsx +0 -38
- package/src/Search/SearchPage.jsx +0 -178
- package/src/Search/SearchPopoverFilter.jsx +0 -109
- package/src/Search/SearchResult.jsx +0 -239
- package/src/Search/SearchResultAuthor.jsx +0 -54
- package/src/Search/ToggleSearchButton.jsx +0 -64
- package/src/Search/component.search-filter.scss +0 -67
- package/src/Search/component.search-overlay.scss +0 -103
- package/src/Search/component.search-page.scss +0 -125
- package/src/Search/component.search-result-author.scss +0 -65
- package/src/Search/index.js +0 -34
|
@@ -1,13 +1,67 @@
|
|
|
1
|
+
import styled from '@emotion/styled';
|
|
2
|
+
import { css } from '@emotion/core';
|
|
1
3
|
import React, { ReactNode } from 'react';
|
|
2
|
-
import BEMHelper from 'react-bem-helper';
|
|
3
4
|
import { Forward } from '@ndla/icons/common';
|
|
4
|
-
|
|
5
5
|
import SafeLink from '@ndla/safelink';
|
|
6
|
+
import { colors, fonts, spacing } from '@ndla/core';
|
|
6
7
|
|
|
7
8
|
import SectionHeading from '../SectionHeading';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
interface InfoWidgetSectionProps {
|
|
11
|
+
center?: boolean;
|
|
12
|
+
}
|
|
13
|
+
const InfoWidgetSection = styled.section<InfoWidgetSectionProps>`
|
|
14
|
+
max-width: 600px;
|
|
15
|
+
margin: ${(p) => p.center && '0 auto'};
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const InfoWidgetDescription = styled.div`
|
|
19
|
+
padding: ${spacing.normal};
|
|
20
|
+
background: ${colors.brand.lighter};
|
|
21
|
+
color: ${colors.brand.dark};
|
|
22
|
+
${fonts.sizes('18px', '26px')};
|
|
23
|
+
|
|
24
|
+
p {
|
|
25
|
+
margin-top: 0;
|
|
26
|
+
&:last-child {
|
|
27
|
+
margin-bottom: 0;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
const InfoWidgetLinksContainer = styled.div`
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: flex-end;
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const iconLinkStyle = css`
|
|
39
|
+
width: 47px;
|
|
40
|
+
height: 47px;
|
|
41
|
+
border-radius: 100%;
|
|
42
|
+
background: ${colors.brand.lighter};
|
|
43
|
+
color: ${colors.brand.dark};
|
|
44
|
+
box-shadow: none;
|
|
45
|
+
display: flex;
|
|
46
|
+
justify-content: center;
|
|
47
|
+
align-items: center;
|
|
48
|
+
margin-right: ${spacing.small};
|
|
10
49
|
|
|
50
|
+
.c-icon {
|
|
51
|
+
width: 20px;
|
|
52
|
+
height: 20px;
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const IconSafeLink = styled(SafeLink)(iconLinkStyle);
|
|
57
|
+
|
|
58
|
+
const IconAnchor = styled.a(iconLinkStyle);
|
|
59
|
+
const IconSpan = styled.span(iconLinkStyle);
|
|
60
|
+
|
|
61
|
+
const StyledMainLink = styled.a`
|
|
62
|
+
color: ${colors.brand.dark};
|
|
63
|
+
${fonts.sizes('16px', '24px')};
|
|
64
|
+
`;
|
|
11
65
|
interface Props {
|
|
12
66
|
heading: string;
|
|
13
67
|
description: string;
|
|
@@ -26,36 +80,33 @@ interface Props {
|
|
|
26
80
|
}
|
|
27
81
|
|
|
28
82
|
const InfoWidget = ({ heading, description, mainLink, iconLinks, center = false }: Props) => (
|
|
29
|
-
<
|
|
30
|
-
<SectionHeading large
|
|
31
|
-
|
|
32
|
-
</SectionHeading>
|
|
33
|
-
<div {...classes('description')}>
|
|
83
|
+
<InfoWidgetSection center={center}>
|
|
84
|
+
<SectionHeading large>{heading}</SectionHeading>
|
|
85
|
+
<InfoWidgetDescription>
|
|
34
86
|
<p>{description}</p>
|
|
35
|
-
</
|
|
36
|
-
<
|
|
37
|
-
{iconLinks
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (link.href) {
|
|
47
|
-
return (
|
|
48
|
-
<a key={link.href} href={link.href} {...classes('icon-link')} aria-label={link.name}>
|
|
49
|
-
{link.icon}
|
|
50
|
-
</a>
|
|
51
|
-
);
|
|
52
|
-
}
|
|
87
|
+
</InfoWidgetDescription>
|
|
88
|
+
<InfoWidgetLinksContainer>
|
|
89
|
+
{iconLinks?.map((link) => {
|
|
90
|
+
if (link.url) {
|
|
91
|
+
return (
|
|
92
|
+
<IconSafeLink key={link.url} to={link.url} aria-label={link.name}>
|
|
93
|
+
{link.icon}
|
|
94
|
+
</IconSafeLink>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
if (link.href) {
|
|
53
98
|
return (
|
|
54
|
-
<
|
|
99
|
+
<IconAnchor key={link.href} href={link.href} aria-label={link.name}>
|
|
55
100
|
{link.icon}
|
|
56
|
-
</
|
|
101
|
+
</IconAnchor>
|
|
57
102
|
);
|
|
58
|
-
}
|
|
103
|
+
}
|
|
104
|
+
return (
|
|
105
|
+
<IconSpan key={link.name} aria-label={link.name}>
|
|
106
|
+
{link.icon}
|
|
107
|
+
</IconSpan>
|
|
108
|
+
);
|
|
109
|
+
})}
|
|
59
110
|
{mainLink.url ? (
|
|
60
111
|
<div className="o-text-link__wrapper o-text-link__wrapper--right">
|
|
61
112
|
<SafeLink className="o-text-link" to={mainLink.url}>
|
|
@@ -63,12 +114,10 @@ const InfoWidget = ({ heading, description, mainLink, iconLinks, center = false
|
|
|
63
114
|
</SafeLink>
|
|
64
115
|
</div>
|
|
65
116
|
) : (
|
|
66
|
-
<
|
|
67
|
-
{mainLink.name}
|
|
68
|
-
</a>
|
|
117
|
+
<StyledMainLink href={mainLink.href}>{mainLink.name}</StyledMainLink>
|
|
69
118
|
)}
|
|
70
|
-
</
|
|
71
|
-
</
|
|
119
|
+
</InfoWidgetLinksContainer>
|
|
120
|
+
</InfoWidgetSection>
|
|
72
121
|
);
|
|
73
122
|
|
|
74
123
|
export default InfoWidget;
|
|
@@ -6,13 +6,25 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import styled from '@emotion/styled';
|
|
9
10
|
import React from 'react';
|
|
10
|
-
import BEMHelper from 'react-bem-helper';
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
interface PortraitWrapperProps {
|
|
13
|
+
size: string;
|
|
14
|
+
}
|
|
15
|
+
const PortraitWrapper = styled.div<PortraitWrapperProps>`
|
|
16
|
+
width: ${(p) => p.size};
|
|
17
|
+
height: ${(p) => p.size};
|
|
18
|
+
span {
|
|
19
|
+
display: block;
|
|
20
|
+
border-radius: 50%;
|
|
21
|
+
background-color: rgba(0, 0, 0, 0.16);
|
|
22
|
+
background-size: cover;
|
|
23
|
+
background-position: center center;
|
|
24
|
+
width: ${(p) => p.size};
|
|
25
|
+
height: ${(p) => p.size};
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
16
28
|
|
|
17
29
|
interface Props {
|
|
18
30
|
src: string;
|
|
@@ -21,10 +33,13 @@ interface Props {
|
|
|
21
33
|
modifier?: 'small' | 'large';
|
|
22
34
|
}
|
|
23
35
|
|
|
24
|
-
const Portrait = ({ src, alt, modifier, className }: Props) =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
)
|
|
36
|
+
const Portrait = ({ src, alt, modifier, className }: Props) => {
|
|
37
|
+
const size = modifier === 'small' ? '4rem' : modifier === 'large' ? '10rem' : '7rem';
|
|
38
|
+
return (
|
|
39
|
+
<PortraitWrapper size={size} className={className}>
|
|
40
|
+
<span role="img" aria-label={alt} style={{ backgroundImage: `url(${src})` }} />
|
|
41
|
+
</PortraitWrapper>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
29
44
|
|
|
30
45
|
export default Portrait;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
2
|
import styled from '@emotion/styled';
|
|
4
3
|
import { Cross } from '@ndla/icons/action';
|
|
5
4
|
import { spacing, mq, breakpoints, colors, misc, fonts } from '@ndla/core';
|
|
@@ -32,7 +31,17 @@ export const StyledActiveFilterTitle = styled('span')`
|
|
|
32
31
|
padding-right: ${spacing.small};
|
|
33
32
|
`;
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
interface Props {
|
|
35
|
+
filter: {
|
|
36
|
+
value: string;
|
|
37
|
+
title: string;
|
|
38
|
+
filterName?: string;
|
|
39
|
+
};
|
|
40
|
+
onFilterRemove: (value: string, filterName?: string) => void;
|
|
41
|
+
ariaLabel: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ActiveFilterContent = ({ filter, onFilterRemove, ariaLabel }: Props) => (
|
|
36
45
|
<StyledActiveFilter
|
|
37
46
|
aria-label={ariaLabel}
|
|
38
47
|
type="button"
|
|
@@ -42,14 +51,4 @@ const ActiveFilterContent = ({ filter, onFilterRemove, ariaLabel }) => (
|
|
|
42
51
|
</StyledActiveFilter>
|
|
43
52
|
);
|
|
44
53
|
|
|
45
|
-
ActiveFilterContent.propTypes = {
|
|
46
|
-
filter: PropTypes.shape({
|
|
47
|
-
value: PropTypes.string.isRequired,
|
|
48
|
-
title: PropTypes.string.isRequired,
|
|
49
|
-
filterName: PropTypes.string,
|
|
50
|
-
}),
|
|
51
|
-
onFilterRemove: PropTypes.func.isRequired,
|
|
52
|
-
ariaLabel: PropTypes.string.isRequired,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
54
|
export default ActiveFilterContent;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
2
|
import styled from '@emotion/styled';
|
|
4
3
|
import { css } from '@emotion/core';
|
|
5
4
|
import { spacing, mq, breakpoints } from '@ndla/core';
|
|
@@ -7,7 +6,12 @@ import Tooltip from '@ndla/tooltip';
|
|
|
7
6
|
import { useTranslation } from 'react-i18next';
|
|
8
7
|
import ActiveFilterContent, { StyledActiveFilterTitle } from './ActiveFilterContent';
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
interface StyledActiveFiltersProps {
|
|
10
|
+
showOnSmallScreen?: boolean;
|
|
11
|
+
filterLength?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const StyledActiveFilters = styled('ul')<StyledActiveFiltersProps>`
|
|
11
15
|
margin: 0;
|
|
12
16
|
padding: 0;
|
|
13
17
|
flex-direction: column;
|
|
@@ -70,10 +74,22 @@ const StyledActiveFilterWrapper = styled('li')`
|
|
|
70
74
|
}
|
|
71
75
|
`;
|
|
72
76
|
|
|
73
|
-
const getFilterLength = (filters) =>
|
|
77
|
+
const getFilterLength = (filters: Filter[]) =>
|
|
74
78
|
filters.filter((filter) => filter.filterName === 'filter_subjects' && filter.title).length;
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
interface Filter {
|
|
81
|
+
title: string;
|
|
82
|
+
value: string;
|
|
83
|
+
filterName?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface Props {
|
|
87
|
+
filters: Filter[];
|
|
88
|
+
onFilterRemove: (value: string, filterName?: string) => void;
|
|
89
|
+
showOnSmallScreen?: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }: Props) => {
|
|
77
93
|
const { t } = useTranslation();
|
|
78
94
|
if (filters && filters.length > 0) {
|
|
79
95
|
const filterLength = getFilterLength(filters);
|
|
@@ -90,7 +106,6 @@ const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }) => {
|
|
|
90
106
|
})}>
|
|
91
107
|
<ActiveFilterContent
|
|
92
108
|
filter={filter}
|
|
93
|
-
filterLength={filterLength}
|
|
94
109
|
ariaLabel={t('searchPage.searchFilterMessages.removeFilter', {
|
|
95
110
|
filterName: filter.title,
|
|
96
111
|
})}
|
|
@@ -100,7 +115,6 @@ const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }) => {
|
|
|
100
115
|
) : (
|
|
101
116
|
<ActiveFilterContent
|
|
102
117
|
filter={filter}
|
|
103
|
-
filterLength={filterLength}
|
|
104
118
|
onFilterRemove={onFilterRemove}
|
|
105
119
|
ariaLabel={t('searchPage.searchFilterMessages.removeFilter', {
|
|
106
120
|
filterName: filter.title,
|
|
@@ -121,15 +135,4 @@ const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }) => {
|
|
|
121
135
|
return null;
|
|
122
136
|
};
|
|
123
137
|
|
|
124
|
-
ActiveFilters.propTypes = {
|
|
125
|
-
filters: PropTypes.arrayOf(
|
|
126
|
-
PropTypes.shape({
|
|
127
|
-
value: PropTypes.string.isRequired,
|
|
128
|
-
title: PropTypes.string.isRequired,
|
|
129
|
-
filterName: PropTypes.string,
|
|
130
|
-
}),
|
|
131
|
-
),
|
|
132
|
-
onFilterRemove: PropTypes.func.isRequired,
|
|
133
|
-
};
|
|
134
|
-
|
|
135
138
|
export default ActiveFilters;
|
|
@@ -6,27 +6,30 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import React from 'react';
|
|
10
|
-
import
|
|
9
|
+
import React, { FocusEvent, MouseEvent, RefObject } from 'react';
|
|
10
|
+
import { useTranslation } from 'react-i18next';
|
|
11
11
|
import BEMHelper from 'react-bem-helper';
|
|
12
|
-
import { Search as SearchIcon } from '@ndla/icons/common';
|
|
13
12
|
import { css } from '@emotion/core';
|
|
13
|
+
import styled from '@emotion/styled';
|
|
14
|
+
import { Search as SearchIcon } from '@ndla/icons/common';
|
|
14
15
|
import { colors, spacing, mq, breakpoints, misc, fonts } from '@ndla/core';
|
|
15
|
-
import { useTranslation } from 'react-i18next';
|
|
16
16
|
|
|
17
17
|
import ActiveFilters from './ActiveFilters';
|
|
18
18
|
import LoadingWrapper from './LoadingWrapper';
|
|
19
19
|
|
|
20
|
-
import { ContentTypeResultShape } from '../shapes';
|
|
21
|
-
|
|
22
20
|
const classes = new BEMHelper('c-search-field');
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
interface StyledInputProps {
|
|
23
|
+
frontPageSearch?: boolean;
|
|
24
|
+
hasFilters?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const StyledInput = styled.input<StyledInputProps>`
|
|
25
28
|
width: 100%;
|
|
26
29
|
height: 48px;
|
|
27
30
|
line-height: 28px;
|
|
28
31
|
border: 1px solid ${colors.brand.greyLight};
|
|
29
|
-
border-radius: ${frontPageSearch ? '100px' : misc.borderRadius};
|
|
32
|
+
border-radius: ${(p) => (p.frontPageSearch ? '100px' : misc.borderRadius)};
|
|
30
33
|
padding-right: ${spacing.large};
|
|
31
34
|
padding-left: ${spacing.normal};
|
|
32
35
|
flex-grow: 1;
|
|
@@ -41,61 +44,71 @@ const inputStyle = (frontPageSearch) => css`
|
|
|
41
44
|
line-height: 58px;
|
|
42
45
|
${fonts.sizes('18px', '24px')};
|
|
43
46
|
}
|
|
44
|
-
`;
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
${(p) =>
|
|
49
|
+
p.hasFilters &&
|
|
50
|
+
css`
|
|
51
|
+
${mq.range({ from: breakpoints.desktop })} {
|
|
52
|
+
padding-left: ${spacing.normal};
|
|
53
|
+
}
|
|
54
|
+
padding-left: 0;
|
|
55
|
+
border-left: 0;
|
|
56
|
+
border-top-left-radius: 0;
|
|
57
|
+
border-bottom-left-radius: 0;
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
&:focus {
|
|
60
|
+
border: 1px solid ${colors.brand.primary};
|
|
61
|
+
border-left: 0;
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
& + .c-search-field__filters {
|
|
64
|
+
border: 1px solid ${colors.brand.primary};
|
|
65
|
+
border-right: 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
`};
|
|
64
69
|
`;
|
|
65
70
|
|
|
71
|
+
interface Props {
|
|
72
|
+
value: string;
|
|
73
|
+
placeholder: string;
|
|
74
|
+
onChange: (value: string) => void;
|
|
75
|
+
filters?: { value: string; title: string }[];
|
|
76
|
+
onFilterRemove?: (value: string, filterName?: string) => void;
|
|
77
|
+
onFocus?: (event?: FocusEvent<HTMLInputElement>) => void;
|
|
78
|
+
onBlur?: () => void;
|
|
79
|
+
onClick?: (event: MouseEvent<HTMLInputElement>) => void;
|
|
80
|
+
loading?: boolean;
|
|
81
|
+
inputRef?: RefObject<HTMLInputElement>;
|
|
82
|
+
frontPageSearch?: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
66
85
|
const SearchField = ({
|
|
67
86
|
placeholder,
|
|
68
87
|
value,
|
|
69
88
|
onChange,
|
|
70
89
|
filters,
|
|
71
|
-
small,
|
|
72
90
|
onClick,
|
|
73
|
-
onFocus
|
|
74
|
-
onBlur
|
|
91
|
+
onFocus,
|
|
92
|
+
onBlur,
|
|
75
93
|
loading,
|
|
76
94
|
onFilterRemove,
|
|
77
95
|
inputRef,
|
|
78
96
|
frontPageSearch = false,
|
|
79
|
-
}) => {
|
|
97
|
+
}: Props) => {
|
|
80
98
|
const { t } = useTranslation();
|
|
81
|
-
const handleOnFilterRemove = (value, filterName) => {
|
|
82
|
-
onFilterRemove(value, filterName);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
onFocus();
|
|
99
|
+
const handleOnFilterRemove = (value: string, filterName?: string) => {
|
|
100
|
+
onFilterRemove?.(value, filterName);
|
|
101
|
+
inputRef?.current?.focus();
|
|
102
|
+
onFocus?.();
|
|
87
103
|
};
|
|
88
|
-
const hasFilters = filters && filters.length > 0;
|
|
89
104
|
return (
|
|
90
105
|
<div {...classes('input-wrapper')}>
|
|
91
106
|
{loading && <LoadingWrapper value={value} />}
|
|
92
|
-
<
|
|
107
|
+
<StyledInput
|
|
108
|
+
frontPageSearch={frontPageSearch}
|
|
109
|
+
hasFilters={!!filters?.length}
|
|
93
110
|
ref={inputRef}
|
|
94
111
|
type="search"
|
|
95
|
-
css={css`
|
|
96
|
-
${inputStyle(frontPageSearch)};
|
|
97
|
-
${hasFilters && filterStyle};
|
|
98
|
-
`}
|
|
99
112
|
aria-autocomplete="list"
|
|
100
113
|
autoComplete="off"
|
|
101
114
|
id="search"
|
|
@@ -108,7 +121,7 @@ const SearchField = ({
|
|
|
108
121
|
onFocus={onFocus}
|
|
109
122
|
onClick={onClick}
|
|
110
123
|
/>
|
|
111
|
-
{
|
|
124
|
+
{filters && filters.length > 0 && (
|
|
112
125
|
<div {...classes('filters')}>
|
|
113
126
|
<ActiveFilters filters={filters} onFilterRemove={handleOnFilterRemove} />
|
|
114
127
|
</div>
|
|
@@ -119,41 +132,18 @@ const SearchField = ({
|
|
|
119
132
|
type="button"
|
|
120
133
|
onClick={() => {
|
|
121
134
|
onChange('');
|
|
122
|
-
onFocus();
|
|
123
|
-
|
|
124
|
-
inputRef.current.focus();
|
|
125
|
-
}
|
|
135
|
+
onFocus?.();
|
|
136
|
+
inputRef?.current?.focus();
|
|
126
137
|
}}
|
|
127
138
|
onBlur={onBlur}>
|
|
128
139
|
{t('welcomePage.resetSearch')}
|
|
129
140
|
</button>
|
|
130
141
|
)}
|
|
131
|
-
<button tabIndex=
|
|
142
|
+
<button tabIndex={-1} {...classes('button', 'searchIcon')} type="submit" value="Search">
|
|
132
143
|
<SearchIcon />
|
|
133
144
|
</button>
|
|
134
145
|
</div>
|
|
135
146
|
);
|
|
136
147
|
};
|
|
137
148
|
|
|
138
|
-
SearchField.propTypes = {
|
|
139
|
-
value: PropTypes.string.isRequired,
|
|
140
|
-
placeholder: PropTypes.string.isRequired,
|
|
141
|
-
onChange: PropTypes.func.isRequired,
|
|
142
|
-
filters: PropTypes.arrayOf(
|
|
143
|
-
PropTypes.shape({
|
|
144
|
-
value: PropTypes.string.isRequired,
|
|
145
|
-
title: PropTypes.string.isRequired,
|
|
146
|
-
}),
|
|
147
|
-
),
|
|
148
|
-
searchResult: PropTypes.arrayOf(ContentTypeResultShape),
|
|
149
|
-
allResultUrl: PropTypes.string,
|
|
150
|
-
onFilterRemove: PropTypes.func,
|
|
151
|
-
small: PropTypes.bool,
|
|
152
|
-
onNavigate: PropTypes.func,
|
|
153
|
-
onFocus: PropTypes.func,
|
|
154
|
-
onBlur: PropTypes.func,
|
|
155
|
-
onClick: PropTypes.func,
|
|
156
|
-
loading: PropTypes.bool,
|
|
157
|
-
};
|
|
158
|
-
|
|
159
149
|
export default SearchField;
|