Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
1230601
feat: create postcode lookup component in Component Library
michael-odonovan Oct 2, 2023
92b4291
setup basic component
michael-odonovan Oct 2, 2023
4b445ca
adjust visual appearance in Component Library
michael-odonovan Oct 2, 2023
351fe7f
add postcode package
michael-odonovan Oct 2, 2023
626aaf7
bring in FSU code
michael-odonovan Oct 2, 2023
de27d4c
get api returning data and add a little styling
michael-odonovan Oct 2, 2023
68c4211
refining example
michael-odonovan Oct 2, 2023
ebb4119
tidy up props
michael-odonovan Oct 2, 2023
3b85274
clean up
michael-odonovan Oct 2, 2023
b1e0337
snaps
michael-odonovan Oct 2, 2023
a061e7e
typo
michael-odonovan Oct 3, 2023
8a43e83
add props fro name and label
michael-odonovan Oct 3, 2023
476f482
sort snapshot re. styled-components-jest
michael-odonovan Oct 4, 2023
72b8fb6
update snapshots and props
michael-odonovan Oct 4, 2023
fc5092b
mock test in place and working
michael-odonovan Oct 12, 2023
fd83679
still working
michael-odonovan Oct 12, 2023
6868bf5
works with right postcode data : )
michael-odonovan Oct 12, 2023
a80c959
remove examples
michael-odonovan Oct 12, 2023
cd6d868
linting
michael-odonovan Oct 12, 2023
52888d3
Merge branch 'master' of https://github.com/comicrelief/component-lib…
michael-odonovan Mar 27, 2024
ea5c8ca
bank
michael-odonovan Mar 28, 2024
48cc0ad
setup Sentry function
michael-odonovan Mar 28, 2024
3ef69ea
tidy up
michael-odonovan Mar 28, 2024
0e850ac
cleanup
michael-odonovan Mar 28, 2024
2e3aa88
cleanup
michael-odonovan Mar 28, 2024
25c851d
cleanup
michael-odonovan Mar 28, 2024
370e9f7
Empty commit to trigger build
michael-odonovan May 7, 2024
af2515e
Merge branch 'master' of https://github.com/comicrelief/component-lib…
michael-odonovan May 7, 2024
8c06816
adjust failing default name props etc
michael-odonovan May 8, 2024
f2c97e3
name prop shinnigans
michael-odonovan May 8, 2024
cd1f930
redo name
michael-odonovan May 8, 2024
06dd6af
adjust button wander
michael-odonovan May 9, 2024
3c20086
add export for PostcodeLookup
michael-odonovan May 9, 2024
f0331dd
postcode lookup working with population of basic fields
michael-odonovan May 10, 2024
592a9b2
adjust spacing and streamline Lookup functionality
michael-odonovan May 13, 2024
924ced6
sort opening of manual address fields
michael-odonovan May 14, 2024
6980897
add extra props
michael-odonovan May 15, 2024
2739da5
snaps
michael-odonovan May 15, 2024
518fc18
adjust open fields link spacing / layout
michael-odonovan May 16, 2024
ce6e92a
snaps and lint
michael-odonovan May 16, 2024
9712dcb
Merge branch 'master' of https://github.com/comicrelief/component-lib…
michael-odonovan Jun 1, 2024
855d1fc
add Lookup button props
michael-odonovan Jun 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"lazysizes": "^5.3.2",
"lodash": "^4.17.11",
"moment": "^2.29.4",
"postcode": "^5.1.0",
"prop-types": "^15.8.1",
"pure-react-carousel": "1.30.1",
"react": "^17.0.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ const Dropdown = styled.div`
border: 1px solid;
margin-top: -1px;
width: 100%;

@media ${({ theme }) => theme.allBreakpoints('M')} {
max-width: 500px;
}
Expand Down
63 changes: 36 additions & 27 deletions src/components/Molecules/Lookup/Lookup.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,34 @@ import TextInputWithDropdown from '../../Atoms/TextInputWithDropdown/TextInputWi
import spacing from '../../../theme/shared/spacing';
import ButtonWithStates from '../../Atoms/ButtonWithStates/ButtonWithStates';

const StyledButton = styled(ButtonWithStates)`${({ theme }) => css`
color: ${theme.color('grey_dark')};
border: 2px solid ${theme.color('grey_dark')};
background-color: ${theme.color('white')};
padding-left: ${spacing('lg')};
padding-right: ${spacing('lg')};

const Button = styled(ButtonWithStates)`${({
theme,
buttonTextColour,
buttonBgColour,
buttonTextHoverColour,
buttonHoverBgColour
}) => css`
margin-top: ${spacing('m')};
color: ${buttonTextColour || theme.color('white')};
background-color: ${buttonBgColour || theme.color('red')};
&:hover {
color: ${theme.color('grey_dark')};
background-color: ${theme.color('white')};
color: ${buttonTextHoverColour || theme.color('white')};
background-color: ${buttonHoverBgColour || theme.color('red_dark')};
}
padding: 0 ${spacing('lg')};
`}`;

const KEY_CODE_ENTER = 13;

/**
* A simple lookup component
*
* The `lookupHandler` should be an async function which is called when a lookup is triggered
* (either by hitting enter or clicking the button)
*
* It will receive the current search term and should:
* - take care of any validation on the search term
* - perform the actual lookup request
* - return an array of options (or an empty array if none were found)
* - only throw errors with user-friendly messages
*
* Any errors thrown will be caught and the message will be displayed to the user.
*
* The `onSelect` function will receive the chosen option.
*
* @param name
* @param label
* @param placeholder
* @param buttonText
* @param buttonTextColour
* @param buttonBgColour
* @param buttonHoverBgColour
* @param buttonHoverTextColour
* @param lookupHandler
* @param mapOptionToString
* @param onSelect
Expand All @@ -54,6 +47,10 @@ const Lookup = ({
label,
placeholder,
buttonText,
buttonTextColour,
buttonBgColour,
buttonHoverBgColour,
buttonHoverTextColour,
lookupHandler,
mapOptionToString,
onSelect,
Expand Down Expand Up @@ -115,15 +112,19 @@ const Lookup = ({
errorMsg={errorMessage}
dropdownInstruction={dropdownInstruction}
/>
<StyledButton
<Button
type="button"
onClick={() => handler()}
loading={isSearching}
disabled={isSearching}
loadingText="Searching"
buttonTextColour={buttonTextColour}
buttonBgColour={buttonBgColour}
buttonHoverBgColour={buttonHoverBgColour}
buttonHoverTextColour={buttonHoverTextColour}
>
{buttonText}
</StyledButton>
</Button>
</div>
);
};
Expand All @@ -133,6 +134,10 @@ Lookup.propTypes = {
label: PropTypes.string.isRequired,
placeholder: PropTypes.string.isRequired,
buttonText: PropTypes.string.isRequired,
buttonTextColour: PropTypes.string,
buttonBgColour: PropTypes.string,
buttonHoverBgColour: PropTypes.string,
buttonHoverTextColour: PropTypes.string,
lookupHandler: PropTypes.func.isRequired,
mapOptionToString: PropTypes.func.isRequired,
onSelect: PropTypes.func.isRequired,
Expand All @@ -142,7 +147,11 @@ Lookup.propTypes = {

Lookup.defaultProps = {
noResultsMessage: 'Sorry, could not find any results for your search',
dropdownInstruction: ''
dropdownInstruction: '',
buttonTextColour: 'white',
buttonBgColour: '#E52630',
buttonHoverTextColour: 'white',
buttonHoverBgColour: '#890B11'
};

export default Lookup;
64 changes: 64 additions & 0 deletions src/components/Molecules/Lookup/Lookup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Lookup
This is a simple basic vanilla component and is rendered here mainly for dev purposes.
You may would be best served using one of these more substantial components in projects / production:
- PostcodeLookup
- SchoolLookup
- SimpleSchoolLookup

The `lookupHandler` should be an async function which is called when a lookup is triggered (either by hitting enter or clicking the button).

It will receive the current search term and should:
- take care of any validation on the search term
- perform the actual lookup request
- return an array of options (or an empty array if none were found)
- only throw errors with user-friendly messages

Any errors thrown will be caught and the message will be displayed to the user.

The `onSelect` function will receive the chosen option.

```js
import Lookup from './Lookup';

const schoolFetcher = async query => {
if (!query || !query.trim()) {
throw new Error('Please enter a search query');
}

if (query.length < 2) {
throw new Error('Please enter at least two characters');
}

try {
const res = await axios.get(
'https://lookups.sls.comicrelief.com/schools/lookup',
{ timeout: 10000, params: { query } }
);
return res.data.data.schools;
} catch (error) {
// if (typeof Sentry !== 'undefined') {
// Sentry.captureException(error);
// }
throw new Error('Sorry, something unexpected went wrong. Please try again or enter your school manually');
}
};

const schoolToString = school => `${school.name}, ${school.post_code}`;

<Lookup
name="Lookup"
label="Custom label"
placeholder="...placeholder"
buttonText="Lookup Button"
dropdownInstruction="Please select an organisation from the list below"
noResultsMessage='Sorry, we could not find anything matching your search; please use the manual entry option.'
lookupHandler={schoolFetcher}
mapOptionToString={schoolToString}
onSelect={address => alert(JSON.stringify(address, null, 2))}
// buttonTextColour='black'
// buttonBgColour='#f04257'
// buttonHoverTextColour='white'
// buttonHoverBgColour='orange'
/>
```

100 changes: 100 additions & 0 deletions src/components/Molecules/PostcodeLookup/PostcodeLookup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import Lookup from '../Lookup/Lookup';
import AddressInputs from './utils/AddressInputs';
import { addressToString, addressFetcher } from './utils/PostcodeFunctions';
import spacing from '../../../theme/shared/spacing';

// ${({ theme }) => theme.linkStyles('standard')};
const AddressManually = styled.button`
margin: ${spacing('md')} 0 0 ${spacing('sm')};
background: inherit;
border: none;
font-size: 1rem;
&:hover, &:active {
text-decoration: underline;
}
`;

export default function PostcodeLookup({
label,
name,
placeholder,
buttonText,
noResultsMessage,
reportError,
dropdownInstruction,
buttonColour
}) {
const [showFields, setShowFields] = useState(false);

// Address field state
const [addressFields, setAddressFields] = useState({
line1: '',
line2: '',
line3: '',
posttown: ''
});

// Function to update address fields
const handleAddressSelect = selectedAddress => {
setShowFields(true);
setAddressFields({
line1: selectedAddress.Line1 || '',
line2: selectedAddress.Line2 || '',
line3: selectedAddress.Line3 || '',
posttown: selectedAddress.posttown || ''
});
};

const handleManualClick = event => {
event.preventDefault();
setShowFields(true);
};

return (
<>
<Lookup
name={name}
label={label}
placeholder={placeholder}
buttonText={buttonText}
mapOptionToString={addressToString}
lookupHandler={postcode => addressFetcher(postcode, reportError)}
onSelect={handleAddressSelect}
dropdownInstruction={dropdownInstruction}
noResultsMessage={noResultsMessage}
buttonColour={buttonColour}
/>
<AddressManually onClick={handleManualClick}>
Or enter your address manually
</AddressManually>
{showFields && <AddressInputs addressFields={addressFields} />}
</>
);
}

PostcodeLookup.propTypes = {
name: PropTypes.string,
label: PropTypes.string,
placeholder: PropTypes.string,
buttonText: PropTypes.string,
noResultsMessage: PropTypes.string,
dropdownInstruction: PropTypes.string,
reportError: PropTypes.oneOfType([
PropTypes.func
]),
buttonColour: PropTypes.string
};

PostcodeLookup.defaultProps = {
name: 'postcode_lookup',
label: 'Find address by postcode',
placeholder: 'Enter postcode...',
buttonText: 'Find address',
noResultsMessage: 'Sorry, could not find any addresses for that postcode',
dropdownInstruction: 'Please select an organisation from the list below',
reportError: undefined,
buttonColour: '#f04257'
};
24 changes: 24 additions & 0 deletions src/components/Molecules/PostcodeLookup/PostcodeLookup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
```js
import PostcodeLookup from './PostcodeLookup';

// This is just an example of how a parent component might handle fetch errors within the component.
const [enterManually, setEnterManually] = React.useState(false);

const fetchErrorHandler = () => {
setEnterManually(true);
}

enterManually
? 'Sorry, there appears to be a problem. Please enter your details manually.'
: (
<PostcodeLookup
label="Postal Address"
buttonText="Find Address"
// Example for could not find address - n22 4qa
noResultsMessage='Sorry, we could not find your address.'
reportError='Custom report error'
dropdownInstruction="Dropdown instruction here..."
buttonColour='#f04257'
/>
)
```
12 changes: 12 additions & 0 deletions src/components/Molecules/PostcodeLookup/PostcodeLookup.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import 'jest-styled-components';

import renderWithTheme from '../../../hoc/shallowWithTheme';
import PostcodeLookup from './PostcodeLookup';

it('renders correctly', () => {
Comment thread
michael-odonovan marked this conversation as resolved.
const tree = renderWithTheme(
<PostcodeLookup onSelect={address => alert(JSON.stringify(address, null, 2))} />
).toJSON();
expect(tree).toMatchSnapshot()
});
Loading