Skip to content

Commit 9943628

Browse files
committedDec 2, 2024
more doc; working on MUI integration
1 parent e156bd5 commit 9943628

36 files changed

+839
-98
lines changed
 

‎DEVELOPER.md

+5-17
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,11 @@
11
## Developer Notes
22

3-
This was a very old script which I revamped in Dec 2024 to bring it into the 21st century. Version `4.0.0` now uses Typescript,
4-
modern rollup + babel and jest for testing, plus uses this crazy new thing called "hooks" in React.
3+
This was a very old script which I revamped in Dec 2024 to bring it into the 21st century. Version `4.0.0` now uses Turborepo, Typescript,
4+
modern rollup + babel, jest for testing, modern React, Docusaurus for the doc, eslint and prettier.
55

6-
The dev environment is functional but needs work.
7-
8-
`yarn build` -> builds the script.
9-
`yarn` - bootstraps the app. Also builds it.
10-
`yarn test` - runs the tests. Note, if you're altering the code run `yarn && yarn test` (see "Ugly bits" below).
11-
12-
Ugly bits:
13-
14-
- The country-regions data set is very large, so to keep bundle sizes down, the rollup build for this script parses
15-
the data and minifies it as much as it can. That's done in via a custom rollup plugin. It works fine, but makes local dev a
16-
bit awkward, e.g. running the tests while tweaking the code. For that you have to run `yarn && yarn test` to first rebuild,
17-
then run the tests - and the tests are ran on the `/dist` result. Awkward. This'd be nice to rethink.
18-
- The `/example` folder is a mess. It's very data, requires a bit of custom setup to bootstrap both the main repo and that. Plus,
19-
just like the tests it won't auto-reload when you change the source: you have to do a rebuild and even restart the server.
20-
- needs linting + prettier set up.
6+
`pnpm install` - bootstraps the monorepo
7+
`npm run dev` - starts dev mode.
8+
`npm run test` - runs the tests
219

2210
### Notes for next 4.x release
2311

‎apps/docs/docs/Demos/BasicUsage.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Basic Usage
33
sidebar_position: 1
44
---
55

6-
import BasicUsage from '@site/src/components/examples/BasicUsage';
6+
import BasicUsage from '@site/src/components/demos/BasicUsage';
77

88
Let's get started! This first demo shows the basic usage of the script, using minimal settings. As you can see, you're
99
responsible for tracking state, but the tools handles rendering the appropriate countries and regions.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
title: Country Blacklist
3+
sidebar_position: 9
4+
---
5+
6+
import CountryBlacklist from '@site/src/components/demos/features/CountryBlacklist';
7+
8+
The `blacklist` prop omits those countries from the dropdown. This demo hides all countries starting with `A`.
9+
The values are found in the `countryShortCode` value in the [source data package](https://github.com/country-regions/country-region-data/blob/master/data.json).
10+
11+
<CountryBlacklist />
12+
13+
---
14+
15+
```tsx
16+
import React, { useState } from 'react';
17+
import { CountryDropdown, RegionDropdown } from 'react-country-region-selector';
18+
19+
const CountryBlacklist = () => {
20+
const [country, setCountry] = useState('');
21+
const [region, setRegion] = useState('');
22+
23+
return (
24+
<>
25+
<CountryDropdown
26+
value={country}
27+
onChange={(val) => setCountry(val)}
28+
// highlight-start
29+
blacklist={[
30+
'AF',
31+
'AX',
32+
'AL',
33+
'DZ',
34+
'AS',
35+
'AD',
36+
'AO',
37+
'AI',
38+
'AQ',
39+
'AG',
40+
'AR',
41+
'AM',
42+
'AW',
43+
'AU',
44+
'AT',
45+
'AZ',
46+
]}
47+
// highlight-end
48+
/>
49+
<RegionDropdown
50+
country={country}
51+
value={region}
52+
onChange={(val) => setRegion(val)}
53+
/>
54+
</>
55+
);
56+
};
57+
58+
export default CountryBlacklist;
59+
```

‎apps/docs/docs/Demos/CountryWhitelist.mdx ‎apps/docs/docs/Demos/features/CountryWhitelist.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Country Whitelist
33
sidebar_position: 8
44
---
55

6-
import CountryWhitelist from '@site/src/components/examples/CountryWhitelist';
6+
import CountryWhitelist from '@site/src/components/demos/features/CountryWhitelist';
77

88
The `whitelist` prop on the CountryDropdown component limits the listed countries to those that you specify. The
99
values are found in the `countryShortCode` value in the [source data package](https://github.com/country-regions/country-region-data/blob/master/data.json).

‎apps/docs/docs/Demos/CustomDefaultOptionLabels.mdx ‎apps/docs/docs/Demos/features/CustomDefaultOptionLabels.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Custom Default Option Labels
33
sidebar_position: 4
44
---
55

6-
import CustomDefaultOptionLabels from '@site/src/components/examples/CustomDefaultOptionLabels';
6+
import CustomDefaultOptionLabels from '@site/src/components/demos/features/CustomDefaultOptionLabels';
77

88
In case you want to localize the strings or just change the defaults, take a look at the following props:
99

‎apps/docs/docs/Demos/DisableEmptyRegionField.mdx ‎apps/docs/docs/Demos/features/DisableEmptyRegionField.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Disable Empty Region Field
33
sidebar_position: 3
44
---
55

6-
import DisableEmptyRegionField from '@site/src/components/examples/DisableEmptyRegionField';
6+
import DisableEmptyRegionField from '@site/src/components/demos/features/DisableEmptyRegionField';
77

88
When the user hasn't selected a country, nothing's going to show up in the Region dropdown. If you want to disable it,
99
use the `disableWhenEmpty` prop.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
title: Disable Fields
3+
sidebar_position: 12
4+
---
5+
6+
import DisableFields from '@site/src/components/demos/features/DisableFields';
7+
8+
The `disabled` prop can be used on either component.
9+
10+
<DisableFields />
11+
12+
---
13+
14+
```tsx
15+
import React, { useState } from 'react';
16+
import { CountryDropdown, RegionDropdown } from 'react-country-region-selector';
17+
18+
const DisableFields = () => {
19+
const [country, setCountry] = useState('');
20+
const [region, setRegion] = useState('');
21+
22+
return (
23+
<>
24+
<CountryDropdown
25+
value={country}
26+
onChange={(val) => setCountry(val)}
27+
// highlight-next-line
28+
disabled={true}
29+
/>
30+
<RegionDropdown
31+
country={country}
32+
value={region}
33+
onChange={(val) => setRegion(val)}
34+
// highlight-next-line
35+
disabled={true}
36+
/>
37+
</>
38+
);
39+
};
40+
41+
export default DisableFields;
42+
```

‎apps/docs/docs/Demos/NameIdClassAttrs.mdx ‎apps/docs/docs/Demos/features/NameIdClassAttrs.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Name, ID, Class attributes
33
sidebar_position: 5
44
---
55

6-
import NameIdClassAttrs from '@site/src/components/examples/NameIdClassAttrs';
6+
import NameIdClassAttrs from '@site/src/components/demos/features/NameIdClassAttrs';
77

88
This demo shows how to set `name`, `id` and `class` attributes on each component.
99

‎apps/docs/docs/Demos/NoDefaultOption.mdx ‎apps/docs/docs/Demos/features/NoDefaultOption.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: No Default Option
33
sidebar_position: 2
44
---
55

6-
import NoDefaultOption from '@site/src/components/examples/NoDefaultOption';
6+
import NoDefaultOption from '@site/src/components/demos/features/NoDefaultOption';
77

88
The `showDefaultOption` boolean prop (default: `true`) can be set on both `CountryDropdown` and `RegionDropdown`. When set to
99
`false` the default "Select Country" / "Select Region" option won't appear.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
title: Region Blacklist
3+
sidebar_position: 11
4+
---
5+
6+
import RegionBlacklist from '@site/src/components/demos/features/RegionBlacklist';
7+
8+
The `blacklist` prop on the RegionDropdown copmonent lets you hide specific regions from the list. This hides
9+
Alberta and BC from Canada's regions and Washington and Texas from US. The
10+
values are found in the `countryShortCode` value in the [source data package](https://github.com/country-regions/country-region-data/blob/master/data.json).
11+
12+
<RegionBlacklist />
13+
14+
---
15+
16+
```tsx
17+
import React, { useState } from 'react';
18+
import { CountryDropdown, RegionDropdown } from 'react-country-region-selector';
19+
20+
const RegionBlacklist = () => {
21+
const [country, setCountry] = useState('');
22+
const [region, setRegion] = useState('');
23+
24+
return (
25+
<>
26+
<CountryDropdown
27+
value={country}
28+
onChange={(val) => setCountry(val)}
29+
whitelist={['CA', 'US']}
30+
/>
31+
<RegionDropdown
32+
country={country}
33+
value={region}
34+
onChange={(val) => setRegion(val)}
35+
// highlight-start
36+
blacklist={{
37+
CA: ['AB', 'BC'],
38+
US: ['WA', 'TX'],
39+
}}
40+
// highlight-end
41+
/>
42+
</>
43+
);
44+
};
45+
46+
export default RegionBlacklist;
47+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
title: Region Whitelist
3+
sidebar_position: 10
4+
---
5+
6+
import RegionWhitelist from '@site/src/components/demos/features/RegionWhitelist';
7+
8+
The `whitelist` prop on the `RegionDropdown` component limits the listed regions to those that you specify. The
9+
values are found in the `countryShortCode` value in the [source data package](https://github.com/country-regions/country-region-data/blob/master/data.json).
10+
Note the order will always be alphabetical.
11+
12+
<RegionWhitelist />
13+
14+
---
15+
16+
```tsx
17+
import React, { useState } from 'react';
18+
import { CountryDropdown, RegionDropdown } from 'react-country-region-selector';
19+
20+
const RegionWhitelist = () => {
21+
const [country, setCountry] = useState('');
22+
const [region, setRegion] = useState('');
23+
24+
return (
25+
<>
26+
<CountryDropdown
27+
value={country}
28+
onChange={(val) => setCountry(val)}
29+
whitelist={['CA', 'US']}
30+
/>
31+
<RegionDropdown
32+
country={country}
33+
value={region}
34+
onChange={(val) => setRegion(val)}
35+
// highlight-start
36+
whitelist={{
37+
CA: ['AB', 'BC'],
38+
US: ['WA', 'TX'],
39+
}}
40+
// highlight-end
41+
/>
42+
</>
43+
);
44+
};
45+
46+
export default RegionWhitelist;
47+
```

‎apps/docs/docs/Demos/ShortcodeLabels.mdx ‎apps/docs/docs/Demos/features/ShortcodeLabels.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Shortcode Labels
33
sidebar_position: 6
44
---
55

6-
import ShortcodeLabels from '@site/src/components/examples/ShortcodeLabels';
6+
import ShortcodeLabels from '@site/src/components/demos/features/ShortcodeLabels';
77

88
The `CountryDropdown` and `RegionDropdown` fields both let you customize the display values for the
99
dropdowns. Either `full` (default) or `short`. You can mix and match. Note that this is purely for the _label_.

‎apps/docs/docs/Demos/ShortcodeValues.mdx ‎apps/docs/docs/Demos/features/ShortcodeValues.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Shortcode Values
33
sidebar_position: 7
44
---
55

6-
import ShortcodeLabels from '@site/src/components/examples/ShortcodeLabels';
6+
import ShortcodeLabels from '@site/src/components/demos/features/ShortcodeLabels';
77

88
The `valueType` prop on both components alters the _value_ of each option to be a short code rather than the full
99
country or region name, like with the visible label. Note that when you use `valueType` as `short` for the
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
title: Features
3+
---
4+
5+
This section demos all the various features of the script. You can read the descriptions in the sidebar in the left if
6+
that works for you, but here's a simple mapping of prop to demo - in case that's more useful.
7+
8+
| `CountryDropdown` | `RegionDropdown` |
9+
| --------------------------- | --------------------------- |
10+
| [`value`](../BasicUsage) | [`value`](../BasicUsage) |
11+
| [`onChange`](../BasicUsage) | [`onChange`](../BasicUsage) |
12+
13+
[TODO]

‎apps/docs/docs/Demos/index.mdx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
title: Demos
3+
---
4+
5+
The following pages demo all the features of the script.
6+
7+
- [Basic Usage](./BasicUsage): start here for a quick demonstration of how to use the components.
8+
- [Features](./Features): this section contains demos of all the available features.
9+
- [Integrations](./Integrations): by default, the components output by this package output plain HTML `<select>` and
10+
`<option>` elements. This section shows how you can use the package with other libraries like Material UI, using their
11+
own components instead.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
title: Material UI
3+
sidebar_position: 1
4+
---
5+
6+
import MaterialUI from '@site/src/components/demos/integrations/MaterialUI';
7+
8+
<MaterialUI />
9+
10+
---
11+
12+
```tsx
13+
14+
```

‎apps/docs/docusaurus.config.ts

+2-31
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import { themes as prismThemes } from 'prism-react-renderer';
22
import type { Config } from '@docusaurus/types';
33
import type * as Preset from '@docusaurus/preset-classic';
44

5-
// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...)
6-
75
const config: Config = {
86
title: 'react-country-region-selector',
97
tagline: '',
@@ -48,16 +46,9 @@ const config: Config = {
4846
],
4947

5048
themeConfig: {
51-
// image: 'img/docusaurus-social-card.jpg',
5249
navbar: {
53-
title: 'React-Country-Region-Selector',
50+
title: 'react-country-region-selector',
5451
items: [
55-
{
56-
type: 'docSidebar',
57-
sidebarId: 'tutorialSidebar',
58-
position: 'left',
59-
label: 'Documentation',
60-
},
6152
{
6253
href: 'https://github.com/country-regions/react-country-region-selector',
6354
label: 'GitHub',
@@ -67,29 +58,9 @@ const config: Config = {
6758
},
6859
footer: {
6960
style: 'dark',
70-
// links: [
71-
// {
72-
// title: 'Docs',
73-
// items: [
74-
// {
75-
// label: 'Tutorial',
76-
// to: '/docs/intro',
77-
// },
78-
// ],
79-
// },
80-
// {
81-
// title: 'Community',
82-
// items: [
83-
// {
84-
// label: 'Stack Overflow',
85-
// href: 'https://stackoverflow.com/questions/tagged/docusaurus',
86-
// },
87-
// ],
88-
// },
89-
// ],
9061
},
9162
/*
92-
theme$j as dracula,
63+
theme$j as dracula,
9364
theme$i as duotoneDark,
9465
theme$h as duotoneLight,
9566
theme$g as github,

‎apps/docs/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
"@docusaurus/core": "3.6.3",
1919
"@docusaurus/preset-classic": "3.6.3",
2020
"@mdx-js/react": "^3.0.0",
21+
"@mui/material": "^6.1.9",
22+
"@emotion/react": "^11.13.5",
23+
"@emotion/styled": "^11.13.5",
2124
"clsx": "^2.0.0",
2225
"prism-react-renderer": "^2.3.0",
2326
"react": "^18.0.0",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React, { useState } from 'react';
2+
import { CountryDropdown, RegionDropdown } from 'react-country-region-selector';
3+
4+
const CountryBlacklist = () => {
5+
const [country, setCountry] = useState('');
6+
const [region, setRegion] = useState('');
7+
8+
return (
9+
<>
10+
<CountryDropdown
11+
value={country}
12+
onChange={(val) => setCountry(val)}
13+
blacklist={[
14+
'AF',
15+
'AX',
16+
'AL',
17+
'DZ',
18+
'AS',
19+
'AD',
20+
'AO',
21+
'AI',
22+
'AQ',
23+
'AG',
24+
'AR',
25+
'AM',
26+
'AW',
27+
'AU',
28+
'AT',
29+
'AZ',
30+
]}
31+
/>
32+
<RegionDropdown
33+
country={country}
34+
value={region}
35+
onChange={(val) => setRegion(val)}
36+
/>
37+
</>
38+
);
39+
};
40+
41+
export default CountryBlacklist;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React, { useState } from 'react';
2+
import { CountryDropdown, RegionDropdown } from 'react-country-region-selector';
3+
4+
const DisableFields = () => {
5+
const [country, setCountry] = useState('');
6+
const [region, setRegion] = useState('');
7+
8+
return (
9+
<>
10+
<CountryDropdown
11+
value={country}
12+
onChange={(val) => setCountry(val)}
13+
disabled={true}
14+
/>
15+
<RegionDropdown
16+
country={country}
17+
value={region}
18+
onChange={(val) => setRegion(val)}
19+
disabled={true}
20+
/>
21+
</>
22+
);
23+
};
24+
25+
export default DisableFields;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React, { useState } from 'react';
2+
import { CountryDropdown, RegionDropdown } from 'react-country-region-selector';
3+
4+
const RegionBlacklist = () => {
5+
const [country, setCountry] = useState('');
6+
const [region, setRegion] = useState('');
7+
8+
return (
9+
<>
10+
<CountryDropdown
11+
value={country}
12+
onChange={(val) => setCountry(val)}
13+
whitelist={['CA', 'US']}
14+
/>
15+
<RegionDropdown
16+
country={country}
17+
value={region}
18+
onChange={(val) => setRegion(val)}
19+
blacklist={{
20+
CA: ['AB', 'BC'],
21+
US: ['WA', 'TX'],
22+
}}
23+
/>
24+
</>
25+
);
26+
};
27+
28+
export default RegionBlacklist;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React, { useState } from 'react';
2+
import { CountryDropdown, RegionDropdown } from 'react-country-region-selector';
3+
4+
const RegionWhitelist = () => {
5+
const [country, setCountry] = useState('');
6+
const [region, setRegion] = useState('');
7+
8+
return (
9+
<>
10+
<CountryDropdown
11+
value={country}
12+
onChange={(val) => setCountry(val)}
13+
whitelist={['CA', 'US']}
14+
/>
15+
<RegionDropdown
16+
country={country}
17+
value={region}
18+
onChange={(val) => setRegion(val)}
19+
whitelist={{
20+
CA: ['AB', 'BC'],
21+
US: ['WA', 'TX'],
22+
}}
23+
/>
24+
</>
25+
);
26+
};
27+
28+
export default RegionWhitelist;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import * as React from 'react';
2+
import InputLabel from '@mui/material/InputLabel';
3+
import MenuItem from '@mui/material/MenuItem';
4+
import FormControl from '@mui/material/FormControl';
5+
import Select from '@mui/material/Select';
6+
import Grid from '@mui/material/Grid2';
7+
import { CountryDropdown, RegionDropdown } from 'react-country-region-selector';
8+
9+
const renderSelect = ({ onChange, value, children }) => (
10+
<Select value={value} label="Age" onChange={onChange}>
11+
{children}
12+
</Select>
13+
);
14+
15+
const renderOption = ({ value, label }) => (
16+
<MenuItem value={value}>{label}</MenuItem>
17+
);
18+
19+
// https://mui.com/material-ui/react-select/
20+
const MaterialUISelect = () => {
21+
const [country, setCountry] = React.useState('');
22+
const [region, setRegion] = React.useState('');
23+
24+
return (
25+
<Grid container spacing={2}>
26+
<Grid size={6}>
27+
<FormControl fullWidth>
28+
<InputLabel id="mui-country-field">Country</InputLabel>
29+
<CountryDropdown
30+
value={country}
31+
id="mui-country-field"
32+
onChange={(val) => setCountry(val)}
33+
renderSelect={renderSelect}
34+
renderOption={renderOption}
35+
/>
36+
</FormControl>
37+
</Grid>
38+
<Grid size={6}>
39+
<FormControl fullWidth>
40+
<InputLabel id="mui-region-field">Region</InputLabel>
41+
<RegionDropdown
42+
country={country}
43+
value={region}
44+
id="mui-region-field"
45+
onChange={(val) => setRegion(val)}
46+
disableWhenEmpty={true}
47+
renderSelect={renderSelect}
48+
renderOption={renderOption}
49+
/>
50+
</FormControl>
51+
</Grid>
52+
</Grid>
53+
);
54+
};
55+
56+
export default MaterialUISelect;

‎packages/react-country-region-selector/src/CountryDropdown.tsx

+23-20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { FC, useMemo } from 'react';
22
import CountryRegionData from 'country-region-data/data.json';
3-
import { filterCountries } from './helpers';
3+
import {
4+
filterCountries,
5+
defaultRenderOption,
6+
defaultRenderSelect,
7+
} from './helpers';
48
import type { CountryDropdownProps } from './rcrs.types';
59

610
export const CountryDropdown: FC<CountryDropdownProps> = ({
@@ -18,6 +22,8 @@ export const CountryDropdown: FC<CountryDropdownProps> = ({
1822
whitelist = [],
1923
blacklist = [],
2024
disabled = false,
25+
renderSelect = defaultRenderSelect,
26+
renderOption = defaultRenderOption,
2127
...arbitraryProps
2228
}) => {
2329
const countries = useMemo(() => {
@@ -28,25 +34,24 @@ export const CountryDropdown: FC<CountryDropdownProps> = ({
2834
blacklist
2935
);
3036

31-
return countries.map(([countryName, countrySlug]) => (
32-
<option
33-
value={valueType === 'short' ? countrySlug : countryName}
34-
key={countrySlug}
35-
>
36-
{labelType === 'short' ? countrySlug : countryName}
37-
</option>
38-
));
37+
return countries.map(([countryName, countrySlug]) =>
38+
renderOption({
39+
value: valueType === 'short' ? countrySlug : countryName,
40+
key: countrySlug,
41+
label: labelType === 'short' ? countrySlug : countryName,
42+
})
43+
);
3944
}, [priorityOptions, whitelist, blacklist, valueType, labelType]);
4045

4146
const defaultOption = useMemo(() => {
4247
if (!showDefaultOption) {
4348
return null;
4449
}
45-
return (
46-
<option value="" key="default">
47-
{defaultOptionLabel}
48-
</option>
49-
);
50+
return renderOption({
51+
value: '',
52+
key: 'default',
53+
label: defaultOptionLabel,
54+
});
5055
}, [showDefaultOption, defaultOptionLabel]);
5156

5257
const attrs: any = {
@@ -64,10 +69,8 @@ export const CountryDropdown: FC<CountryDropdownProps> = ({
6469
attrs.className = className;
6570
}
6671

67-
return (
68-
<select {...attrs}>
69-
{defaultOption}
70-
{countries}
71-
</select>
72-
);
72+
return renderSelect({
73+
...attrs,
74+
children: [defaultOption, countries],
75+
});
7376
};

‎packages/react-country-region-selector/src/RegionDropdown.tsx

+26-20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { FC, useMemo } from 'react';
22
import CountryRegionData from '../node_modules/country-region-data/data.json';
3-
import { filterRegions, findDuplicates } from './helpers';
3+
import {
4+
defaultRenderSelect,
5+
defaultRenderOption,
6+
filterRegions,
7+
findDuplicates,
8+
} from './helpers';
49
import * as C from './constants';
510
import type { RegionDropdownProps } from './rcrs.types';
611

@@ -23,6 +28,8 @@ export const RegionDropdown: FC<RegionDropdownProps> = ({
2328
customOptions = [],
2429
whitelist = {},
2530
blacklist = {},
31+
renderSelect = defaultRenderSelect,
32+
renderOption = defaultRenderOption,
2633
...arbitraryProps
2734
}) => {
2835
const regions = useMemo(() => {
@@ -66,11 +73,9 @@ export const RegionDropdown: FC<RegionDropdownProps> = ({
6673
}, [country, countryValueType, whitelist, blacklist]);
6774

6875
const regionsJsx = useMemo(() => {
69-
return regions.map(({ label, value }) => (
70-
<option value={value} key={value}>
71-
{label}
72-
</option>
73-
));
76+
return regions.map(({ label, value }) =>
77+
renderOption({ value, key: value, label })
78+
);
7479
}, [regions]);
7580

7681
const customRegionsJsx = useMemo(() => {
@@ -90,11 +95,7 @@ export const RegionDropdown: FC<RegionDropdownProps> = ({
9095

9196
return customOptions.map((option) => {
9297
if (option) {
93-
return (
94-
<option value={option} key={option}>
95-
{option}
96-
</option>
97-
);
98+
return renderOption({ value: option, key: option, label: option });
9899
}
99100
});
100101
}, [regions, country, customOptions]);
@@ -103,10 +104,18 @@ export const RegionDropdown: FC<RegionDropdownProps> = ({
103104
// a "default" option which shows
104105
const getDefaultOption = () => {
105106
if (!country) {
106-
return <option value="">{blankOptionLabel}</option>;
107+
return renderOption({
108+
value: '',
109+
key: 'default',
110+
label: blankOptionLabel,
111+
});
107112
}
108113
if (showDefaultOption) {
109-
return <option value="">{defaultOptionLabel}</option>;
114+
return renderOption({
115+
value: '',
116+
key: 'default',
117+
label: defaultOptionLabel,
118+
});
110119
}
111120
return null;
112121
};
@@ -128,11 +137,8 @@ export const RegionDropdown: FC<RegionDropdownProps> = ({
128137
attrs.className = className;
129138
}
130139

131-
return (
132-
<select {...attrs}>
133-
{getDefaultOption()}
134-
{regionsJsx}
135-
{customRegionsJsx}
136-
</select>
137-
);
140+
return renderSelect({
141+
...attrs,
142+
children: [getDefaultOption(), regionsJsx, customRegionsJsx],
143+
});
138144
};

‎packages/react-country-region-selector/src/helpers.ts ‎packages/react-country-region-selector/src/helpers.tsx

+21
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,24 @@ export const findDuplicates = (regions, customOptions: string[]) => {
8989
.filter(({ value }) => customOptions.indexOf(value) !== -1)
9090
.map(({ label }) => label);
9191
};
92+
93+
export const defaultRenderSelect = (props: any) => {
94+
const { children, ...rest } = props;
95+
return (
96+
<select {...rest}>
97+
<>{children}</>
98+
</select>
99+
);
100+
};
101+
102+
export const defaultRenderOption = ({
103+
label,
104+
value,
105+
key,
106+
}: {
107+
label: string;
108+
value: string;
109+
key: string;
110+
}) => {
111+
return <option value={value}>{label}</option>;
112+
};

‎packages/react-country-region-selector/src/rcrs.types.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ export interface CountryDropdownProps extends NativeDropdownProps {
114114
* Default value: []
115115
*/
116116
readonly blacklist?: object;
117+
118+
readonly renderSelect: any;
119+
readonly renderOption: any;
117120
}
118121

119122
export interface RegionDropdownProps extends NativeDropdownProps {
@@ -257,6 +260,9 @@ export interface RegionDropdownProps extends NativeDropdownProps {
257260
readonly blacklist?: {
258261
[countryCode: string]: string[];
259262
};
263+
264+
readonly renderSelect: any;
265+
readonly renderOption: any;
260266
}
261267

262-
export type CountryRegionData = [string[]];
268+
export type CountryRegionDataMinified = [string[]];

‎pnpm-lock.yaml

+333-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
Please sign in to comment.