@comicrelief/component-library 5.4.0 → 5.7.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/.circleci/config.yml +1 -1
- package/dist/components/Atoms/Button/Button.js +3 -1
- package/dist/components/Atoms/ButtonWithStates/ButtonWithStates.js +91 -0
- package/dist/components/Atoms/ButtonWithStates/ButtonWithStates.md +13 -0
- package/dist/components/Atoms/Checkbox/Checkbox.js +5 -3
- package/dist/components/Atoms/ErrorText/ErrorText.js +3 -1
- package/dist/components/Atoms/Icons/Arrow.js +3 -1
- package/dist/components/Atoms/Icons/AtSign.js +2 -1
- package/dist/components/Atoms/Icons/Chevron.js +3 -1
- package/dist/components/Atoms/Icons/Download.js +3 -1
- package/dist/components/Atoms/Icons/External.js +3 -1
- package/dist/components/Atoms/Icons/Favourite.js +3 -1
- package/dist/components/Atoms/Icons/Internal.js +3 -1
- package/dist/components/Atoms/Input/Input.js +6 -4
- package/dist/components/Atoms/Label/Label.js +5 -2
- package/dist/components/Atoms/Link/Link.js +31 -6
- package/dist/components/Atoms/Link/Link.style.js +3 -3
- package/dist/components/Atoms/Pagination/Item/Item.js +3 -1
- package/dist/components/Atoms/Pagination/List/List.js +5 -3
- package/dist/components/Atoms/Pagination/Pagination.js +3 -1
- package/dist/components/Atoms/Picture/Picture.js +3 -1
- package/dist/components/Atoms/RadioButton/RadioButton.js +3 -1
- package/dist/components/Atoms/RichText/RichText.js +4 -2
- package/dist/components/Atoms/Select/Select.js +3 -1
- package/dist/components/Atoms/SocialIcons/Icon/Icon.js +6 -4
- package/dist/components/Atoms/SocialIcons/SocialIcons.js +5 -3
- package/dist/components/Atoms/Text/Text.js +3 -1
- package/dist/components/Atoms/TextArea/TextArea.js +3 -1
- package/dist/components/Atoms/TextInputWithDropdown/TextInputWithDropdown.js +5 -2
- package/dist/components/Molecules/Accordion/Accordion.js +3 -1
- package/dist/components/Molecules/Box/Box.js +3 -1
- package/dist/components/Molecules/Card/Card.js +3 -1
- package/dist/components/Molecules/CardDs/CardDs.js +8 -6
- package/dist/components/Molecules/Countdown/Countdown.style.js +2 -2
- package/dist/components/Molecules/HeroBanner/HeroBanner.js +3 -3
- package/dist/components/Molecules/Lookup/Lookup.js +201 -0
- package/dist/components/Molecules/PartnerLink/PartnerLink.js +3 -1
- package/dist/components/Molecules/Promo/Promo.style.js +4 -4
- package/dist/components/Molecules/SchoolLookup/SchoolLookup.js +3 -1
- package/dist/components/Molecules/SearchInput/SearchInput.js +3 -1
- package/dist/components/Molecules/SearchResult/SearchResult.js +5 -5
- package/dist/components/Molecules/ShareButton/ShareButton.js +3 -1
- package/dist/components/Molecules/SimpleSchoolLookup/SimpleSchoolLookup.js +112 -0
- package/dist/components/Molecules/SimpleSchoolLookup/SimpleSchoolLookup.md +7 -0
- package/dist/components/Molecules/SingleMessage/SingleMessage.js +1 -1
- package/dist/components/Molecules/SingleMessage/__snapshots__/SingleMessage.test.js.snap +8 -0
- package/dist/components/Molecules/SingleMessageDS/SingleMessageDs.js +3 -1
- package/dist/components/Molecules/SingleMessageDS/SingleMessageDs.style.js +9 -9
- package/dist/components/Molecules/Typeahead/Typeahead.js +3 -1
- package/dist/components/Molecules/VideoBanner/VideoBanner.js +1 -1
- package/dist/components/Organisms/CookieBanner/CookieBanner.style.js +4 -4
- package/dist/components/Organisms/Donate/Form/Form.js +3 -1
- package/dist/components/Organisms/Donate/GivingSelector/GivingSelector.js +4 -4
- package/dist/components/Organisms/Donate/MoneyBox/MoneyBox.js +3 -1
- package/dist/components/Organisms/EmailSignUp/EmailSignUp.js +3 -1
- package/dist/components/Organisms/EmailSignUp/EmailSignUp.style.js +7 -7
- package/dist/components/Organisms/Footer/Footer.js +7 -4
- package/dist/components/Organisms/Footer/Footer.md +8 -1
- package/dist/components/Organisms/Footer/Nav/Nav.js +12 -7
- package/dist/components/Organisms/Footer/Nav/Nav.style.js +8 -8
- package/dist/components/Organisms/Footer/__snapshots__/Footer.test.js.snap +31 -6
- package/dist/components/Organisms/Header/Burger/BurgerMenu.style.js +3 -3
- package/dist/components/Organisms/Header/Header.js +3 -1
- package/dist/components/Organisms/Header/Nav/Nav.js +3 -3
- package/dist/components/Organisms/MarketingPreferencesDS/_TextInput.js +3 -1
- package/dist/components/Organisms/Membership/Form/Form.js +3 -1
- package/dist/components/Organisms/Membership/MoneyBox/MoneyBox.js +3 -1
- package/dist/index.js +24 -0
- package/dist/utils/internalLinkHelper.js +27 -5
- package/package.json +14 -13
- package/src/components/Atoms/ButtonWithStates/ButtonWithStates.js +64 -0
- package/src/components/Atoms/ButtonWithStates/ButtonWithStates.md +13 -0
- package/src/components/Atoms/Link/Link.js +23 -8
- package/src/components/Molecules/Lookup/Lookup.js +148 -0
- package/src/components/Molecules/SimpleSchoolLookup/SimpleSchoolLookup.js +63 -0
- package/src/components/Molecules/SimpleSchoolLookup/SimpleSchoolLookup.md +7 -0
- package/src/components/Molecules/SingleMessage/SingleMessage.js +1 -1
- package/src/components/Molecules/SingleMessage/__snapshots__/SingleMessage.test.js.snap +8 -0
- package/src/components/Organisms/Footer/Footer.js +5 -3
- package/src/components/Organisms/Footer/Footer.md +8 -1
- package/src/components/Organisms/Footer/Nav/Nav.js +4 -3
- package/src/components/Organisms/Footer/__snapshots__/Footer.test.js.snap +31 -6
- package/src/components/Organisms/Header/Nav/Nav.js +1 -1
- package/src/index.js +3 -0
- package/src/utils/internalLinkHelper.js +23 -5
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
|
|
4
4
|
import StyledLink, { HelperText, IconWrapper } from './Link.style';
|
|
5
5
|
import whiteListed from '../../../utils/whiteListed';
|
|
6
|
-
|
|
7
|
-
const domainRegEx = new RegExp(
|
|
8
|
-
'(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]'
|
|
9
|
-
);
|
|
6
|
+
import { getDomain } from '../../../utils/internalLinkHelper';
|
|
10
7
|
|
|
11
8
|
let window = '';
|
|
12
9
|
|
|
@@ -22,19 +19,37 @@ const Link = ({
|
|
|
22
19
|
iconFirst,
|
|
23
20
|
...rest
|
|
24
21
|
}) => {
|
|
22
|
+
const [documentHost, setDocumentHost] = useState('');
|
|
25
23
|
/**
|
|
26
24
|
* If we haven't specifically set the target via props, check if
|
|
27
25
|
* this is an internal link OR on our whitelist before making it a '_self' link
|
|
28
26
|
*/
|
|
29
27
|
if (target === null) {
|
|
30
|
-
|
|
28
|
+
// Use our helper function to determine the raw domains to compare
|
|
29
|
+
const currentDomain = getDomain(documentHost);
|
|
30
|
+
const linkDomain = getDomain(href);
|
|
31
|
+
|
|
32
|
+
// Additional check for applications that need more control
|
|
33
|
+
const isWhiteListOverridden = rest.overrideWhiteList === true;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* If the link has no domain supplied (likely '/' or '#')
|
|
37
|
+
* OR has the same domain as the current page, don't open
|
|
38
|
+
* in a new tab
|
|
39
|
+
*/
|
|
40
|
+
const isExternalLink = linkDomain !== '' && (currentDomain !== linkDomain);
|
|
31
41
|
const isWhiteListed = whiteListed(href);
|
|
32
|
-
|
|
42
|
+
|
|
43
|
+
window = isExternalLink && (isWhiteListOverridden || !isWhiteListed) ? '_blank' : '_self';
|
|
33
44
|
} else {
|
|
34
45
|
window = target === 'blank' ? '_blank' : '_self';
|
|
35
46
|
}
|
|
36
47
|
const hasIcon = icon !== null;
|
|
37
48
|
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
setDocumentHost(document.location.host);
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
38
53
|
return (
|
|
39
54
|
<StyledLink
|
|
40
55
|
{...rest}
|
|
@@ -46,7 +61,7 @@ const Link = ({
|
|
|
46
61
|
underline={underline}
|
|
47
62
|
>
|
|
48
63
|
{children}
|
|
49
|
-
{
|
|
64
|
+
{window === '_blank' && <HelperText>(opens in new window)</HelperText>}
|
|
50
65
|
{hasIcon && <IconWrapper type={type}>{icon}</IconWrapper>}
|
|
51
66
|
</StyledLink>
|
|
52
67
|
);
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styled, { css } from 'styled-components';
|
|
4
|
+
import TextInputWithDropdown from '../../Atoms/TextInputWithDropdown/TextInputWithDropdown';
|
|
5
|
+
import spacing from '../../../theme/shared/spacing';
|
|
6
|
+
import ButtonWithStates from '../../Atoms/ButtonWithStates/ButtonWithStates';
|
|
7
|
+
|
|
8
|
+
const StyledButton = styled(ButtonWithStates)`${({ theme }) => css`
|
|
9
|
+
color: ${theme.color('grey_dark')};
|
|
10
|
+
border: 2px solid ${theme.color('grey_dark')};
|
|
11
|
+
background-color: ${theme.color('white')};
|
|
12
|
+
padding-left: ${spacing('lg')};
|
|
13
|
+
padding-right: ${spacing('lg')};
|
|
14
|
+
|
|
15
|
+
&:hover {
|
|
16
|
+
color: ${theme.color('grey_dark')};
|
|
17
|
+
background-color: ${theme.color('white')};
|
|
18
|
+
}
|
|
19
|
+
`}`;
|
|
20
|
+
|
|
21
|
+
const KEY_CODE_ENTER = 13;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A simple lookup component
|
|
25
|
+
*
|
|
26
|
+
* The `lookupHandler` should be an async function which is called when a lookup is triggered
|
|
27
|
+
* (either by hitting enter or clicking the button)
|
|
28
|
+
*
|
|
29
|
+
* It will receive the current search term and should:
|
|
30
|
+
* - take care of any validation on the search term
|
|
31
|
+
* - perform the actual lookup request
|
|
32
|
+
* - return an array of options (or an empty array if none were found)
|
|
33
|
+
* - only throw errors with user-friendly messages
|
|
34
|
+
*
|
|
35
|
+
* Any errors thrown will be caught and the message will be displayed to the user.
|
|
36
|
+
*
|
|
37
|
+
* The `onSelect` function will receive the chosen option.
|
|
38
|
+
*
|
|
39
|
+
* @param name
|
|
40
|
+
* @param label
|
|
41
|
+
* @param placeholder
|
|
42
|
+
* @param buttonText
|
|
43
|
+
* @param lookupHandler
|
|
44
|
+
* @param mapOptionToString
|
|
45
|
+
* @param onSelect
|
|
46
|
+
* @param noResultsMessage
|
|
47
|
+
* @param dropdownInstruction
|
|
48
|
+
* @param rest
|
|
49
|
+
* @returns {JSX.Element}
|
|
50
|
+
* @constructor
|
|
51
|
+
*/
|
|
52
|
+
const Lookup = ({
|
|
53
|
+
name,
|
|
54
|
+
label,
|
|
55
|
+
placeholder,
|
|
56
|
+
buttonText,
|
|
57
|
+
lookupHandler,
|
|
58
|
+
mapOptionToString,
|
|
59
|
+
onSelect,
|
|
60
|
+
noResultsMessage,
|
|
61
|
+
dropdownInstruction,
|
|
62
|
+
...rest
|
|
63
|
+
}) => {
|
|
64
|
+
const [query, setQuery] = useState('');
|
|
65
|
+
const [errorMessage, setErrorMessage] = useState('');
|
|
66
|
+
const [options, setOptions] = useState([]);
|
|
67
|
+
const [isSearching, setIsSearching] = useState(false);
|
|
68
|
+
|
|
69
|
+
const handler = useCallback(async () => {
|
|
70
|
+
setErrorMessage('');
|
|
71
|
+
setIsSearching(true);
|
|
72
|
+
try {
|
|
73
|
+
// If lookupHandler throws an error, the message will be displayed to the user
|
|
74
|
+
const results = await lookupHandler(query);
|
|
75
|
+
setOptions(results);
|
|
76
|
+
if (results.length === 0) {
|
|
77
|
+
setErrorMessage(noResultsMessage);
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
setErrorMessage(error.message);
|
|
81
|
+
}
|
|
82
|
+
setIsSearching(false);
|
|
83
|
+
}, [query, setOptions, setErrorMessage, noResultsMessage, lookupHandler]);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<div {...rest}>
|
|
87
|
+
<TextInputWithDropdown
|
|
88
|
+
css={{ marginBottom: spacing('md') }}
|
|
89
|
+
name={name}
|
|
90
|
+
id={name}
|
|
91
|
+
value={query}
|
|
92
|
+
options={options.map(mapOptionToString)}
|
|
93
|
+
label={label}
|
|
94
|
+
placeholder={placeholder}
|
|
95
|
+
onChange={e => {
|
|
96
|
+
setQuery(e.target.value);
|
|
97
|
+
setErrorMessage('');
|
|
98
|
+
setOptions([]);
|
|
99
|
+
}}
|
|
100
|
+
onKeyPress={e => {
|
|
101
|
+
const keyCode = e.keyCode || e.which;
|
|
102
|
+
if (keyCode === KEY_CODE_ENTER) {
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
return handler();
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}}
|
|
108
|
+
onSelect={(text, index) => {
|
|
109
|
+
const selection = options[index];
|
|
110
|
+
onSelect(selection);
|
|
111
|
+
setQuery('');
|
|
112
|
+
setErrorMessage('');
|
|
113
|
+
setOptions([]);
|
|
114
|
+
}}
|
|
115
|
+
errorMsg={errorMessage}
|
|
116
|
+
dropdownInstruction={dropdownInstruction}
|
|
117
|
+
/>
|
|
118
|
+
<StyledButton
|
|
119
|
+
type="button"
|
|
120
|
+
onClick={() => handler()}
|
|
121
|
+
loading={isSearching}
|
|
122
|
+
disabled={isSearching}
|
|
123
|
+
loadingText="Searching"
|
|
124
|
+
>
|
|
125
|
+
{buttonText}
|
|
126
|
+
</StyledButton>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
Lookup.propTypes = {
|
|
132
|
+
name: PropTypes.string.isRequired,
|
|
133
|
+
label: PropTypes.string.isRequired,
|
|
134
|
+
placeholder: PropTypes.string.isRequired,
|
|
135
|
+
buttonText: PropTypes.string.isRequired,
|
|
136
|
+
lookupHandler: PropTypes.func.isRequired,
|
|
137
|
+
mapOptionToString: PropTypes.func.isRequired,
|
|
138
|
+
onSelect: PropTypes.func.isRequired,
|
|
139
|
+
noResultsMessage: PropTypes.string,
|
|
140
|
+
dropdownInstruction: PropTypes.string
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
Lookup.defaultProps = {
|
|
144
|
+
noResultsMessage: 'Sorry, could not find any results for your search',
|
|
145
|
+
dropdownInstruction: ''
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export default Lookup;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
|
|
5
|
+
import Lookup from '../Lookup/Lookup';
|
|
6
|
+
|
|
7
|
+
const schoolFetcher = async query => {
|
|
8
|
+
if (!query || !query.trim()) {
|
|
9
|
+
throw new Error('Please enter a search query');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (query.length < 2) {
|
|
13
|
+
throw new Error('Please enter at least two characters');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const res = await axios.get(
|
|
18
|
+
'https://lookups.sls.comicrelief.com/schools/lookup',
|
|
19
|
+
{ timeout: 10000, params: { query } }
|
|
20
|
+
);
|
|
21
|
+
return res.data.data.schools;
|
|
22
|
+
} catch (error) {
|
|
23
|
+
// if (typeof Sentry !== 'undefined') {
|
|
24
|
+
// Sentry.captureException(error);
|
|
25
|
+
// }
|
|
26
|
+
throw new Error('Sorry, something unexpected went wrong. Please try again or enter your school manually');
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const schoolToString = school => `${school.name}, ${school.post_code}`;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The component library's school lookup component uses a typeahead/search-as-you-type approach.
|
|
34
|
+
*
|
|
35
|
+
* Given the API we use is v flaky and can be slow, this isn't really ideal.
|
|
36
|
+
*
|
|
37
|
+
* This version just has a simple input + a button (or you can hit enter) to trigger the search.
|
|
38
|
+
*
|
|
39
|
+
* @param onSelect
|
|
40
|
+
* @param rest
|
|
41
|
+
* @returns {JSX.Element}
|
|
42
|
+
* @constructor
|
|
43
|
+
*/
|
|
44
|
+
const SimpleSchoolLookup = ({ onSelect, ...rest }) => (
|
|
45
|
+
<Lookup
|
|
46
|
+
name="school_lookup"
|
|
47
|
+
label="Enter the name or postcode of your organisation"
|
|
48
|
+
placeholder="Enter name or postcode..."
|
|
49
|
+
buttonText="Find school"
|
|
50
|
+
dropdownInstruction="Please select an organisation from the list below"
|
|
51
|
+
noResultsMessage="Sorry, could not find anything matching your search"
|
|
52
|
+
lookupHandler={schoolFetcher}
|
|
53
|
+
mapOptionToString={schoolToString}
|
|
54
|
+
onSelect={onSelect}
|
|
55
|
+
{...rest}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
SimpleSchoolLookup.propTypes = {
|
|
60
|
+
onSelect: PropTypes.func.isRequired
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default SimpleSchoolLookup;
|
|
@@ -896,6 +896,10 @@ exports[`renders Single Message with full width image and no text correctly 1`]
|
|
|
896
896
|
z-index: 1;
|
|
897
897
|
}
|
|
898
898
|
|
|
899
|
+
@media (min-width:740px) {
|
|
900
|
+
|
|
901
|
+
}
|
|
902
|
+
|
|
899
903
|
@media (min-width:740px) {
|
|
900
904
|
.c0 {
|
|
901
905
|
-webkit-flex-direction: row;
|
|
@@ -1010,6 +1014,10 @@ exports[`renders Single Message with no Image correctly 1`] = `
|
|
|
1010
1014
|
padding: 1rem;
|
|
1011
1015
|
}
|
|
1012
1016
|
|
|
1017
|
+
@media (min-width:740px) {
|
|
1018
|
+
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1013
1021
|
@media (min-width:740px) {
|
|
1014
1022
|
.c0 {
|
|
1015
1023
|
-webkit-flex-direction: row;
|
|
@@ -31,7 +31,7 @@ const Footer = ({
|
|
|
31
31
|
<Logo sizeSm="48px" sizeMd="72px" rotate={false} campaign={campaign} />
|
|
32
32
|
</Brand>
|
|
33
33
|
</FooterBranding>
|
|
34
|
-
<FooterNav navItems={navItems} />
|
|
34
|
+
<FooterNav navItems={navItems} {...rest} />
|
|
35
35
|
<FooterCopyright>
|
|
36
36
|
<Text tag="p" color="grey">
|
|
37
37
|
{footerCopy}
|
|
@@ -46,13 +46,15 @@ const Footer = ({
|
|
|
46
46
|
Footer.propTypes = {
|
|
47
47
|
navItems: PropTypes.objectOf(PropTypes.shape),
|
|
48
48
|
footerCopy: PropTypes.string,
|
|
49
|
-
campaign: PropTypes.string
|
|
49
|
+
campaign: PropTypes.string,
|
|
50
|
+
overrideWhiteList: PropTypes.bool
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
Footer.defaultProps = {
|
|
53
54
|
navItems: {},
|
|
54
55
|
footerCopy: '',
|
|
55
|
-
campaign: 'Comic Relief'
|
|
56
|
+
campaign: 'Comic Relief',
|
|
57
|
+
overrideWhiteList: false
|
|
56
58
|
};
|
|
57
59
|
|
|
58
60
|
export default Footer;
|
|
@@ -4,5 +4,12 @@
|
|
|
4
4
|
import data from './data/data';
|
|
5
5
|
import footerCopy from './data/footerCopy';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
<>
|
|
8
|
+
<p>Standard footer</p>
|
|
9
|
+
<Footer navItems={data} footerCopy={footerCopy.copy} campaign="Comic Relief" />
|
|
10
|
+
|
|
11
|
+
<p>Overrides whitelist functionality for external usage</p>
|
|
12
|
+
<Footer navItems={data} footerCopy={footerCopy.copy} campaign="Comic Relief" overrideWhiteList />
|
|
13
|
+
</>
|
|
14
|
+
|
|
8
15
|
```
|
|
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
|
|
|
4
4
|
import Text from '../../../Atoms/Text/Text';
|
|
5
5
|
import { sizes } from '../../../../theme/shared/breakpoint';
|
|
6
6
|
import NavHelper from '../../../../utils/navHelper';
|
|
7
|
-
import InternalLinkHelper from '../../../../utils/internalLinkHelper';
|
|
7
|
+
import { InternalLinkHelper } from '../../../../utils/internalLinkHelper';
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
Nav,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
SubNavLink
|
|
17
17
|
} from './Nav.style';
|
|
18
18
|
|
|
19
|
-
const FooterNav = ({ navItems }) => {
|
|
19
|
+
const FooterNav = ({ navItems, ...rest }) => {
|
|
20
20
|
const { menuGroups } = navItems;
|
|
21
21
|
const [isExpandable] = useState(false);
|
|
22
22
|
const [isSubMenuOpen, setIsSubMenuOpen] = useState({});
|
|
@@ -78,6 +78,7 @@ const FooterNav = ({ navItems }) => {
|
|
|
78
78
|
aria-haspopup="true"
|
|
79
79
|
role="button"
|
|
80
80
|
onClick={toggleSubMenu(group.id)}
|
|
81
|
+
{...rest}
|
|
81
82
|
>
|
|
82
83
|
<Text color="white">{group.title}</Text>
|
|
83
84
|
</NavLink>
|
|
@@ -103,7 +104,7 @@ const FooterNav = ({ navItems }) => {
|
|
|
103
104
|
group.links.length % 2 === 0 && group.links.length > 2
|
|
104
105
|
}
|
|
105
106
|
>
|
|
106
|
-
<SubNavLink href={thisUrl} inline role="menuitem">
|
|
107
|
+
<SubNavLink href={thisUrl} inline role="menuitem" {...rest}>
|
|
107
108
|
<Text color="white">{child.title}</Text>
|
|
108
109
|
</SubNavLink>
|
|
109
110
|
</SubNavItem>
|
|
@@ -27,7 +27,7 @@ exports[`renders correctly 1`] = `
|
|
|
27
27
|
font-family: 'Montserrat',Helvetica,Arial,sans-serif;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
.
|
|
30
|
+
.c26 {
|
|
31
31
|
color: #969598;
|
|
32
32
|
font-size: 1rem;
|
|
33
33
|
line-height: 1rem;
|
|
@@ -69,6 +69,21 @@ exports[`renders correctly 1`] = `
|
|
|
69
69
|
border-bottom: 2px solid #000000;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
.c24 {
|
|
73
|
+
border: 0;
|
|
74
|
+
-webkit-clip: rect(0 0 0 0);
|
|
75
|
+
clip: rect(0 0 0 0);
|
|
76
|
+
-webkit-clip-path: inset(50%);
|
|
77
|
+
clip-path: inset(50%);
|
|
78
|
+
height: 1px;
|
|
79
|
+
margin: -1px;
|
|
80
|
+
overflow: hidden;
|
|
81
|
+
padding: 0;
|
|
82
|
+
position: absolute;
|
|
83
|
+
white-space: nowrap;
|
|
84
|
+
width: 1px;
|
|
85
|
+
}
|
|
86
|
+
|
|
72
87
|
.c5 {
|
|
73
88
|
-webkit-text-decoration: none;
|
|
74
89
|
text-decoration: none;
|
|
@@ -313,7 +328,7 @@ exports[`renders correctly 1`] = `
|
|
|
313
328
|
align-items: center;
|
|
314
329
|
}
|
|
315
330
|
|
|
316
|
-
.
|
|
331
|
+
.c25 {
|
|
317
332
|
display: block;
|
|
318
333
|
width: 100%;
|
|
319
334
|
height: 100%;
|
|
@@ -321,7 +336,7 @@ exports[`renders correctly 1`] = `
|
|
|
321
336
|
margin-top: 3rem;
|
|
322
337
|
}
|
|
323
338
|
|
|
324
|
-
.
|
|
339
|
+
.c25 p {
|
|
325
340
|
font-size: 15px;
|
|
326
341
|
line-height: 24px;
|
|
327
342
|
margin-bottom: 5px;
|
|
@@ -528,7 +543,7 @@ exports[`renders correctly 1`] = `
|
|
|
528
543
|
}
|
|
529
544
|
|
|
530
545
|
@media (min-width:1024px) {
|
|
531
|
-
.
|
|
546
|
+
.c25 p {
|
|
532
547
|
font-size: 16px;
|
|
533
548
|
line-height: 27px;
|
|
534
549
|
}
|
|
@@ -1166,6 +1181,11 @@ exports[`renders correctly 1`] = `
|
|
|
1166
1181
|
>
|
|
1167
1182
|
Link comp with only URL
|
|
1168
1183
|
</span>
|
|
1184
|
+
<span
|
|
1185
|
+
className="c24"
|
|
1186
|
+
>
|
|
1187
|
+
(opens in new window)
|
|
1188
|
+
</span>
|
|
1169
1189
|
</a>
|
|
1170
1190
|
</li>
|
|
1171
1191
|
<li
|
|
@@ -1206,6 +1226,11 @@ exports[`renders correctly 1`] = `
|
|
|
1206
1226
|
>
|
|
1207
1227
|
Test non-whitelisted external link
|
|
1208
1228
|
</span>
|
|
1229
|
+
<span
|
|
1230
|
+
className="c24"
|
|
1231
|
+
>
|
|
1232
|
+
(opens in new window)
|
|
1233
|
+
</span>
|
|
1209
1234
|
</a>
|
|
1210
1235
|
</li>
|
|
1211
1236
|
</ul>
|
|
@@ -1213,10 +1238,10 @@ exports[`renders correctly 1`] = `
|
|
|
1213
1238
|
</ul>
|
|
1214
1239
|
</nav>
|
|
1215
1240
|
<div
|
|
1216
|
-
className="
|
|
1241
|
+
className="c25"
|
|
1217
1242
|
>
|
|
1218
1243
|
<p
|
|
1219
|
-
className="
|
|
1244
|
+
className="c26"
|
|
1220
1245
|
color="grey"
|
|
1221
1246
|
size="s"
|
|
1222
1247
|
>
|
|
@@ -5,7 +5,7 @@ import Text from '../../../Atoms/Text/Text';
|
|
|
5
5
|
import BurgerMenu from '../Burger/BurgerMenu';
|
|
6
6
|
import { sizes } from '../../../../theme/shared/breakpoint';
|
|
7
7
|
import NavHelper from '../../../../utils/navHelper';
|
|
8
|
-
import InternalLinkHelper from '../../../../utils/internalLinkHelper';
|
|
8
|
+
import { InternalLinkHelper } from '../../../../utils/internalLinkHelper';
|
|
9
9
|
import whiteListed from '../../../../utils/whiteListed';
|
|
10
10
|
|
|
11
11
|
import {
|
package/src/index.js
CHANGED
|
@@ -29,6 +29,7 @@ export { default as SocialIcons } from './components/Atoms/SocialIcons/SocialIco
|
|
|
29
29
|
export { default as TextInputWithDropdown } from './components/Atoms/TextInputWithDropdown/TextInputWithDropdown';
|
|
30
30
|
export { default as ErrorText } from './components/Atoms/ErrorText/ErrorText';
|
|
31
31
|
export { default as Label } from './components/Atoms/Label/Label';
|
|
32
|
+
export { default as ButtonWithStates } from './components/Atoms/ButtonWithStates/ButtonWithStates';
|
|
32
33
|
|
|
33
34
|
/* Molecules */
|
|
34
35
|
export { default as HeroBanner } from './components/Molecules/HeroBanner/HeroBanner';
|
|
@@ -58,6 +59,8 @@ export { default as Countdown } from './components/Molecules/Countdown/Countdown
|
|
|
58
59
|
export { default as Banner } from './components/Molecules/Banner/Banner';
|
|
59
60
|
export { default as Chip } from './components/Molecules/Chip/Chip';
|
|
60
61
|
export { default as Descriptor } from './components/Molecules/Descriptor/Descriptor';
|
|
62
|
+
export { default as Lookup } from './components/Molecules/Lookup/Lookup';
|
|
63
|
+
export { default as SimpleSchoolLookup } from './components/Molecules/SimpleSchoolLookup/SimpleSchoolLookup';
|
|
61
64
|
|
|
62
65
|
/* Organisms */
|
|
63
66
|
export { default as EmailSignUp } from './components/Organisms/EmailSignUp/EmailSignUp';
|
|
@@ -1,14 +1,32 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
);
|
|
1
|
+
const domainRegEx = new RegExp(
|
|
2
|
+
'(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]'
|
|
3
|
+
);
|
|
5
4
|
|
|
5
|
+
const InternalLinkHelper = link => {
|
|
6
6
|
// Check our URL for a domain pattern, if so return it
|
|
7
7
|
if (domainRegEx.test(link)) {
|
|
8
8
|
return link;
|
|
9
9
|
}
|
|
10
|
+
|
|
10
11
|
// If domain-free and internal, prefix it with slash if it doesn't already have one
|
|
11
12
|
return link.substring(0, 1) === '/' ? link : `/${link}`;
|
|
12
13
|
};
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
const getDomain = url => {
|
|
16
|
+
let thisURL = url;
|
|
17
|
+
|
|
18
|
+
// Strip out protocol
|
|
19
|
+
thisURL = url.replace(/(https?:\/\/)?(www.)?/i, '');
|
|
20
|
+
|
|
21
|
+
// Strip out subdirectory/path
|
|
22
|
+
if (thisURL.indexOf('/') !== -1) {
|
|
23
|
+
const [splitURL] = thisURL.split('/');
|
|
24
|
+
thisURL = splitURL;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return thisURL;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
InternalLinkHelper, getDomain
|
|
32
|
+
};
|