├── .travis.yml
├── _example
├── js
│ ├── index.js
│ └── Address.js
├── example.png
├── demo
│ ├── css
│ │ └── demo.css
│ ├── .babelrc
│ ├── index.html
│ ├── webpack.config.js
│ ├── scripts
│ │ ├── start.js
│ │ └── build.js
│ ├── package.json
│ └── app.js
├── .babelrc
├── app.js
├── index.html
├── webpack.config.js
├── package.json
├── scripts
│ └── build.js
└── README.md
├── demo.png
├── .coveralls.yml
├── es
├── index.js
├── config_test
│ └── mockStore.js
├── data
│ ├── RawDataSort.js
│ └── RawData.js
├── zipcode
│ ├── District.js
│ ├── County.js
│ ├── ZipCode.js
│ └── ZipCodeTW.js
└── _test_
│ ├── District.test.js
│ ├── County.test.js
│ ├── ZipCode.test.js
│ └── ZipCodeTW.test.js
├── .gitignore
├── .babelrc
├── index.html
├── webpack.config.js
├── LICENSE
├── .eslintrc.js
├── scripts
├── start.js
└── build.js
├── app.js
├── package.json
└── README.md
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "10"
4 |
--------------------------------------------------------------------------------
/_example/js/index.js:
--------------------------------------------------------------------------------
1 | import Address from "./Address";
2 | export {Address};
--------------------------------------------------------------------------------
/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chris-Tsai/zipcode-tw-react/HEAD/demo.png
--------------------------------------------------------------------------------
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | service_name: travis-pro
2 | repo_token: MAKslNrlkcOQVdeH0UTe6JoDBPpHJEewL
3 |
--------------------------------------------------------------------------------
/_example/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chris-Tsai/zipcode-tw-react/HEAD/_example/example.png
--------------------------------------------------------------------------------
/_example/demo/css/demo.css:
--------------------------------------------------------------------------------
1 | .pull-right {
2 | float: right !important;
3 | vertical-align: bottom !important;
4 | }
--------------------------------------------------------------------------------
/es/index.js:
--------------------------------------------------------------------------------
1 | import ZipCodeTW from "./zipcode/ZipCodeTW";
2 | export {ZipCodeTW};
3 | import ZipCode from "./zipcode/ZipCode";
4 | export {ZipCode};
--------------------------------------------------------------------------------
/es/config_test/mockStore.js:
--------------------------------------------------------------------------------
1 | import configureMockStore from 'redux-mock-store';
2 |
3 | const mockStore = (state) => {
4 | const mockStore = configureMockStore();
5 | const store = mockStore(state);
6 | return store;
7 | };
8 |
9 | export default mockStore;
10 |
11 |
--------------------------------------------------------------------------------
/es/data/RawDataSort.js:
--------------------------------------------------------------------------------
1 | export default {
2 | "基隆市": 1,
3 | "台北市": 2,
4 | "新北市": 3,
5 | "桃園市": 4,
6 | "新竹市": 5,
7 | "新竹縣": 6,
8 | "苗栗縣": 7,
9 | "台中市": 8,
10 | "彰化縣": 9,
11 | "南投縣": 10,
12 | "雲林縣": 11,
13 | "嘉義市": 12,
14 | "嘉義縣": 13,
15 | "台南市": 14,
16 | "高雄市": 15,
17 | "屏東縣": 16,
18 | "台東縣": 17,
19 | "花蓮縣": 18,
20 | "宜蘭縣": 19,
21 | "澎湖縣": 20,
22 | "金門縣": 21,
23 | "連江縣": 22
24 | };
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 |
6 | # production
7 | build
8 | dist
9 | lib
10 |
11 | # misc
12 | .DS_Store
13 | npm-debug.log
14 | js/bundle.js
15 | build_watch
16 |
17 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
18 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
19 |
20 | .idea
21 | *.iml
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-proposal-class-properties",
4 | "@babel/plugin-syntax-dynamic-import",
5 | "@babel/plugin-syntax-import-meta",
6 | "@babel/plugin-proposal-json-strings"
7 | ],
8 | "presets": [
9 | [
10 | "@babel/preset-env",
11 | {
12 | "targets": {
13 | "browsers": [
14 | "chrome >= 52",
15 | "ie >= 10"
16 | ]
17 | }
18 | }
19 | ],
20 | "@babel/preset-react"
21 | ]
22 | }
--------------------------------------------------------------------------------
/_example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-proposal-class-properties",
4 | "@babel/plugin-syntax-dynamic-import",
5 | "@babel/plugin-syntax-import-meta",
6 | "@babel/plugin-proposal-json-strings"
7 | ],
8 | "presets": [
9 | [
10 | "@babel/preset-env",
11 | {
12 | "targets": {
13 | "browsers": [
14 | "chrome >= 52",
15 | "ie >= 10"
16 | ]
17 | }
18 | }
19 | ],
20 | "@babel/preset-react"
21 | ]
22 | }
--------------------------------------------------------------------------------
/_example/demo/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-proposal-class-properties",
4 | "@babel/plugin-syntax-dynamic-import",
5 | "@babel/plugin-syntax-import-meta",
6 | "@babel/plugin-proposal-json-strings"
7 | ],
8 | "presets": [
9 | [
10 | "@babel/preset-env",
11 | {
12 | "targets": {
13 | "browsers": [
14 | "chrome >= 52",
15 | "ie >= 10"
16 | ]
17 | }
18 | }
19 | ],
20 | "@babel/preset-react"
21 | ]
22 | }
--------------------------------------------------------------------------------
/_example/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import Address from "./js/Address";
4 |
5 | class ZipCodeTWTest extends React.Component {
6 |
7 | constructor() {
8 | super();
9 | }
10 |
11 | render() {
12 | return (
13 |
17 | );
18 | }
19 | }
20 |
21 | window.app = {};
22 |
23 | app.create = (dom) => {
24 | ReactDOM.render(
25 | ,
26 | dom
27 | )
28 | };
--------------------------------------------------------------------------------
/_example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZipcodeTW Example
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/_example/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = function (app = "app.js") {
4 | return {
5 | entry: {
6 | app: ['@babel/polyfill', path.resolve(__dirname, app)]
7 | },
8 | output: {
9 | path: path.resolve(__dirname, 'build'),
10 | filename: 'zipcode-tw-react-example.js',
11 | chunkFilename: '[name].js'
12 | },
13 | module: {
14 | loaders: [{
15 | test: /\.(js|jsx)$/,
16 | exclude: [
17 | path.resolve(__dirname, 'node_modules')
18 | ],
19 | loaders: ['babel-loader']
20 | }]
21 | },
22 | plugins: [],
23 | node: {}
24 | };
25 | };
26 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZipCodeTW Demo
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/_example/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZipCodeTW Demo
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = function (app = "app.js") {
4 | return {
5 | entry: {
6 | app: ['@babel/polyfill', path.resolve(__dirname, app)]
7 | },
8 | output: {
9 | path: path.resolve(__dirname, 'build'),
10 | filename: 'zipcode-tw-react.js',
11 | chunkFilename: '[name].js'
12 | },
13 | module: {
14 | loaders: [{
15 | test: /\.(js|jsx)$/,
16 | exclude: [
17 | path.resolve(__dirname, 'node_modules')
18 | ],
19 | loaders: ['babel-loader']
20 | }, {
21 | test: /\.css$/,
22 | loader: "style-loader!css-loader"
23 | }]
24 | },
25 | plugins: [],
26 | node: {}
27 | };
28 | };
29 |
--------------------------------------------------------------------------------
/_example/demo/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = function (app = "app.js") {
4 | return {
5 | entry: {
6 | app: ['@babel/polyfill', path.resolve(__dirname, app)]
7 | },
8 | output: {
9 | path: path.resolve(__dirname, 'build'),
10 | filename: 'zipcode-tw-react.js',
11 | chunkFilename: '[name].js'
12 | },
13 | module: {
14 | loaders: [{
15 | test: /\.(js|jsx)$/,
16 | exclude: [
17 | path.resolve(__dirname, 'node_modules')
18 | ],
19 | loaders: ['babel-loader']
20 | }, {
21 | test: /\.css$/,
22 | loader: "style-loader!css-loader"
23 | }]
24 | },
25 | plugins: [],
26 | node: {}
27 | };
28 | };
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Chris-Tsai
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "node": true
6 | },
7 | "extends": [
8 | "eslint:recommended",
9 | "plugin:react/recommended",
10 | ],
11 | "parser": "babel-eslint",
12 | "parserOptions": {
13 | "ecmaFeatures": {
14 | "experimentalObjectRestSpread": true,
15 | "jsx": true
16 | },
17 | "ecmaVersion": 2018,
18 | "sourceType": "module"
19 | },
20 | "plugins": [
21 | "react",
22 | ],
23 | "rules": {
24 | // "indent": [
25 | // "error",
26 | // 2
27 | // ],
28 | "linebreak-style": [
29 | "error",
30 | "windows"
31 | ],
32 | // "quotes": [
33 | // "error",
34 | // "single"
35 | // ],
36 | // "semi": [
37 | // "error",
38 | // "always"
39 | // ]
40 | "no-unused-vars": ["error", {
41 | "ignoreRestSiblings": true
42 | }],
43 | "no-console": "warn",
44 | "react/prop-types": [0, {
45 | ignore: ['history', 'dispatch'],
46 | }],
47 | // "react/no-deprecated": "warn",
48 | },
49 | "settings": {
50 | "react": {
51 | "pragma": "React", // Pragma to use, default to "React"
52 | "version": "16.2", // React version, default to the latest React stable release
53 | },
54 | },
55 | };
--------------------------------------------------------------------------------
/scripts/start.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 | const WebpackDevServer = require("webpack-dev-server");
3 | const config = require("../webpack.config");
4 | const fs = require("fs");
5 | const path = require("path");
6 |
7 | const port = process.env.PORT || 3001;
8 | const host = process.env.HOST || 'localhost';
9 |
10 | const cfg = config();
11 |
12 | fs.unlink(path.resolve(cfg.output.path, cfg.output.filename), function() {});
13 |
14 | cfg.entry.app.unshift(
15 | "webpack-dev-server/client?http://"+host+":"+port+"/",
16 | "webpack/hot/dev-server"
17 | );
18 |
19 | cfg.plugins.unshift(
20 | new webpack.HotModuleReplacementPlugin()
21 | );
22 |
23 | // console.log(JSON.stringify(cfg));
24 |
25 | const compiler = webpack(cfg);
26 |
27 | // webpack-dev-server options: https://webpack.github.io/docs/webpack-dev-server.html#api
28 | const server = new WebpackDevServer(compiler, {
29 | hot: true,
30 | before: function(app) {
31 | // Here you can access the Express app object and add your own custom middleware to it.
32 | // For example, to define custom handlers for some paths:
33 | // app.get('/some/path', function(req, res) {
34 | // res.json({ custom: 'response' });
35 | // });
36 | },
37 | quiet: true,
38 | publicPath: '/build/',
39 | stats: { colors: true }
40 | });
41 | server.listen(port, host, function(err) {
42 | if (err) {
43 | throw new Error(err);
44 | }
45 | console.log('Listening on http://' + host + ':' + port);
46 | });
--------------------------------------------------------------------------------
/_example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zipcode-tw-react-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "lib/index.js",
6 | "jsnext:main": "es/index.js",
7 | "dependencies": {
8 | "prop-types": "^15.5.8",
9 | "react": "^16.6.0",
10 | "react-bootstrap": "^0.32.4",
11 | "react-dom": "^16.6.0",
12 | "sweetalert2": "^7.28.2",
13 | "zipcode-tw-react": "^1.1.3"
14 | },
15 | "devDependencies": {
16 | "@babel/cli": "^7.0.0-beta.36",
17 | "babel-loader": "^8.0.2",
18 | "@babel/core": "^7.0.0-rc.1",
19 | "@babel/plugin-proposal-class-properties": "^7.0.0-rc.1",
20 | "@babel/plugin-proposal-json-strings": "^7.0.0-rc.1",
21 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0-rc.1",
22 | "@babel/plugin-syntax-dynamic-import": "^7.0.0-rc.1",
23 | "@babel/plugin-syntax-import-meta": "^7.0.0-rc.1",
24 | "@babel/polyfill": "^7.0.0-rc.1",
25 | "@babel/preset-env": "^7.0.0-rc.1",
26 | "@babel/preset-flow": "^7.0.0-rc.1",
27 | "@babel/preset-react": "^7.0.0-rc.1",
28 | "babel-core": "^7.0.0-bridge.0",
29 | "fs-extra": "^5.0.0",
30 | "webpack": "^3.10.0",
31 | "webpack-body-parser": "^1.11.110",
32 | "webpack-encoding-plugin": "^0.2.0"
33 | },
34 | "scripts": {
35 | "test": "echo \"Error: no test specified\" && exit 1",
36 | "build": "node ./scripts/build.js"
37 | },
38 | "author": "Chris.Tsai",
39 | "license": "MIT",
40 | "homepage": "https://github.com/Chris-Tsai/zipcode-tw-react#readme"
41 | }
42 |
--------------------------------------------------------------------------------
/_example/demo/scripts/start.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 | const WebpackDevServer = require("webpack-dev-server");
3 | const config = require("../webpack.config");
4 | const fs = require("fs");
5 | const path = require("path");
6 |
7 | const port = process.env.PORT || 3001;
8 | const host = process.env.HOST || 'localhost';
9 |
10 | const cfg = config();
11 |
12 | fs.unlink(path.resolve(cfg.output.path, cfg.output.filename), function () {
13 | });
14 |
15 | cfg.entry.app.unshift(
16 | "webpack-dev-server/client?http://" + host + ":" + port + "/",
17 | "webpack/hot/dev-server"
18 | );
19 |
20 | cfg.plugins.unshift(
21 | new webpack.HotModuleReplacementPlugin()
22 | );
23 |
24 | // console.log(JSON.stringify(cfg));
25 |
26 | const compiler = webpack(cfg);
27 |
28 | // webpack-dev-server options: https://webpack.github.io/docs/webpack-dev-server.html#api
29 | const server = new WebpackDevServer(compiler, {
30 | hot: true,
31 | before: function (app) {
32 | // Here you can access the Express app object and add your own custom middleware to it.
33 | // For example, to define custom handlers for some paths:
34 | // app.get('/some/path', function(req, res) {
35 | // res.json({ custom: 'response' });
36 | // });
37 | },
38 | quiet: true,
39 | publicPath: '/build/',
40 | stats: {colors: true}
41 | });
42 | server.listen(port, host, function (err) {
43 | if (err) {
44 | throw new Error(err);
45 | }
46 | console.log('Listening on http://' + host + ':' + port);
47 | });
--------------------------------------------------------------------------------
/_example/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zipcode-tw-react-demo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "lib/index.js",
6 | "jsnext:main": "es/index.js",
7 | "dependencies": {
8 | "prop-types": "^15.5.8",
9 | "react": "^16.6.0",
10 | "react-bootstrap": "^0.32.4",
11 | "react-dom": "^16.6.0",
12 | "sweetalert2": "^7.28.2",
13 | "zipcode-tw-react": "^1.1.3"
14 | },
15 | "devDependencies": {
16 | "@babel/cli": "^7.0.0-beta.36",
17 | "babel-loader": "^8.0.2",
18 | "@babel/core": "^7.0.0-rc.1",
19 | "@babel/plugin-proposal-class-properties": "^7.0.0-rc.1",
20 | "@babel/plugin-proposal-json-strings": "^7.0.0-rc.1",
21 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0-rc.1",
22 | "@babel/plugin-syntax-dynamic-import": "^7.0.0-rc.1",
23 | "@babel/plugin-syntax-import-meta": "^7.0.0-rc.1",
24 | "@babel/polyfill": "^7.0.0-rc.1",
25 | "@babel/preset-env": "^7.0.0-rc.1",
26 | "@babel/preset-flow": "^7.0.0-rc.1",
27 | "@babel/preset-react": "^7.0.0-rc.1",
28 | "babel-core": "^7.0.0-bridge.0",
29 | "fs-extra": "^5.0.0",
30 | "webpack": "^3.10.0",
31 | "webpack-body-parser": "^1.11.110",
32 | "webpack-encoding-plugin": "^0.2.0"
33 | },
34 | "scripts": {
35 | "test": "echo \"Error: no test specified\" && exit 1",
36 | "build": "node ./scripts/build.js",
37 | "start": "node ./scripts/start.js"
38 | },
39 | "author": "Chris.Tsai",
40 | "license": "MIT",
41 | "homepage": "https://github.com/Chris-Tsai/zipcode-tw-react#readme"
42 | }
43 |
--------------------------------------------------------------------------------
/scripts/build.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 | const config = require("../webpack.config");
3 | const path = require('path');
4 | const fs = require('fs-extra');
5 |
6 | console.log('Creating an optimized build...');
7 |
8 | const cfg = config();
9 | cfg.output.path = path.resolve(__dirname, '../dist');
10 |
11 | // delete build folder
12 | const deleteFolderRecursive = function(path) {
13 | if( fs.existsSync(path) ) {
14 | fs.readdirSync(path).forEach(function(file,index){
15 | const curPath = path + "/" + file;
16 | if(fs.lstatSync(curPath).isDirectory()) { // recurse
17 | deleteFolderRecursive(curPath);
18 | } else { // delete file
19 | fs.unlinkSync(curPath);
20 | }
21 | });
22 | // fs.rmdirSync(path);
23 | }
24 | };
25 |
26 | deleteFolderRecursive(cfg.output.path);
27 | console.log('Successfully deleted dist folder');
28 |
29 | webpack(cfg).run(function (err, stats) {
30 | if (err) {
31 | throw new Error(err);
32 | }
33 | const jsonStats = stats.toJson();
34 | if (jsonStats.errors.length > 0) {
35 | throw new Error(jsonStats.errors);
36 | }
37 | console.log('Successfully compiled: ' + path.resolve(cfg.output.path, cfg.output.filename));
38 | });
39 |
40 | // begin compile uglify file
41 | const minCfg = config();
42 | minCfg.output.path = path.resolve(__dirname, '../dist');
43 | minCfg.output.filename = 'zipcode-tw-react.min.js';
44 |
45 | minCfg.plugins.unshift(
46 | new webpack.optimize.UglifyJsPlugin(),
47 | new webpack.optimize.OccurrenceOrderPlugin(),
48 | new webpack.DefinePlugin({
49 | "process.env": {
50 | NODE_ENV: JSON.stringify("production")
51 | }
52 | })
53 | );
54 |
55 | webpack(minCfg).run(function (err, stats) {
56 | if (err) {
57 | throw new Error(err);
58 | }
59 | const jsonStats = stats.toJson();
60 | if (jsonStats.errors.length > 0) {
61 | throw new Error(jsonStats.errors);
62 | }
63 | console.log('Successfully compiled uglify: ' + path.resolve(minCfg.output.path, minCfg.output.filename));
64 | });
--------------------------------------------------------------------------------
/_example/scripts/build.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 | const config = require("../webpack.config");
3 | const path = require('path');
4 | const fs = require('fs-extra');
5 |
6 | console.log('Creating an optimized build...');
7 |
8 | const cfg = config();
9 | cfg.output.path = path.resolve(__dirname, '../dist');
10 |
11 | // delete build folder
12 | const deleteFolderRecursive = function(path) {
13 | if( fs.existsSync(path) ) {
14 | fs.readdirSync(path).forEach(function(file,index){
15 | const curPath = path + "/" + file;
16 | if(fs.lstatSync(curPath).isDirectory()) { // recurse
17 | deleteFolderRecursive(curPath);
18 | } else { // delete file
19 | fs.unlinkSync(curPath);
20 | }
21 | });
22 | // fs.rmdirSync(path);
23 | }
24 | };
25 |
26 | deleteFolderRecursive(cfg.output.path);
27 | console.log('Successfully deleted dist folder');
28 |
29 | webpack(cfg).run(function (err, stats) {
30 | if (err) {
31 | throw new Error(err);
32 | }
33 | const jsonStats = stats.toJson();
34 | if (jsonStats.errors.length > 0) {
35 | throw new Error(jsonStats.errors);
36 | }
37 | console.log('Successfully compiled: ' + path.resolve(cfg.output.path, cfg.output.filename));
38 | });
39 |
40 | // begin compile uglify file
41 | const minCfg = config();
42 | minCfg.output.path = path.resolve(__dirname, '../dist');
43 | minCfg.output.filename = 'zipcode-tw-react-example.min.js';
44 |
45 | minCfg.plugins.unshift(
46 | new webpack.optimize.UglifyJsPlugin(),
47 | new webpack.optimize.OccurrenceOrderPlugin(),
48 | new webpack.DefinePlugin({
49 | "process.env": {
50 | NODE_ENV: JSON.stringify("production")
51 | }
52 | })
53 | );
54 |
55 | webpack(minCfg).run(function (err, stats) {
56 | if (err) {
57 | throw new Error(err);
58 | }
59 | const jsonStats = stats.toJson();
60 | if (jsonStats.errors.length > 0) {
61 | throw new Error(jsonStats.errors);
62 | }
63 | console.log('Successfully compiled uglify: ' + path.resolve(minCfg.output.path, minCfg.output.filename));
64 | });
--------------------------------------------------------------------------------
/_example/demo/scripts/build.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 | const config = require("../webpack.config");
3 | const path = require('path');
4 | const fs = require('fs-extra');
5 |
6 | console.log('Creating an optimized build...');
7 |
8 | const cfg = config();
9 | cfg.output.path = path.resolve(__dirname, '../dist');
10 |
11 | // delete build folder
12 | const deleteFolderRecursive = function (path) {
13 | if (fs.existsSync(path)) {
14 | fs.readdirSync(path).forEach(function (file, index) {
15 | const curPath = path + "/" + file;
16 | if (fs.lstatSync(curPath).isDirectory()) { // recurse
17 | deleteFolderRecursive(curPath);
18 | } else { // delete file
19 | fs.unlinkSync(curPath);
20 | }
21 | });
22 | // fs.rmdirSync(path);
23 | }
24 | };
25 |
26 | deleteFolderRecursive(cfg.output.path);
27 | console.log('Successfully deleted dist folder');
28 |
29 | webpack(cfg).run(function (err, stats) {
30 | if (err) {
31 | throw new Error(err);
32 | }
33 | const jsonStats = stats.toJson();
34 | if (jsonStats.errors.length > 0) {
35 | throw new Error(jsonStats.errors);
36 | }
37 | console.log('Successfully compiled: ' + path.resolve(cfg.output.path,
38 | cfg.output.filename));
39 | });
40 |
41 | // begin compile uglify file
42 | const minCfg = config();
43 | minCfg.output.path = path.resolve(__dirname, '../dist');
44 | minCfg.output.filename = 'zipcode-tw-react.min.js';
45 |
46 | minCfg.plugins.unshift(
47 | new webpack.optimize.UglifyJsPlugin(),
48 | new webpack.optimize.OccurrenceOrderPlugin(),
49 | new webpack.DefinePlugin({
50 | "process.env": {
51 | NODE_ENV: JSON.stringify("production")
52 | }
53 | })
54 | );
55 |
56 | webpack(minCfg).run(function (err, stats) {
57 | if (err) {
58 | throw new Error(err);
59 | }
60 | const jsonStats = stats.toJson();
61 | if (jsonStats.errors.length > 0) {
62 | throw new Error(jsonStats.errors);
63 | }
64 | console.log(
65 | 'Successfully compiled uglify: ' + path.resolve(minCfg.output.path,
66 | minCfg.output.filename));
67 | });
--------------------------------------------------------------------------------
/es/zipcode/District.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | /**
5 | * 行政區
6 | *
7 | * @author Chris Tsai
8 | */
9 | export default class District extends React.Component {
10 |
11 | constructor(props) {
12 | super(props);
13 | }
14 |
15 | handleChange = (e) =>{
16 | let value = e.target.value;
17 | let {onChange} = this.props;
18 | if(typeof (onChange) == 'function'){
19 | onChange(value);
20 | }
21 | }
22 |
23 | render() {
24 | const {dataOptions, fieldName, districtClass, districtStyle, value, displayType,
25 | } = this.props;
26 |
27 | const districts = !!dataOptions && dataOptions.map((op) =>
28 | );
29 | return (
30 | <>
31 | {!!displayType && displayType === 'text' ?
32 |
40 | :
41 | <>
42 | {value}
47 |
48 | >
49 | }
50 | >
51 | );
52 | }
53 | }
54 |
55 | District.propTypes = {
56 |
57 | /**
58 | * 顯示樣式
59 | */
60 | displayType: PropTypes.oneOf(['text', 'display']),
61 | /**
62 | * 欄位名稱
63 | */
64 | fieldName: PropTypes.string.isRequired,
65 |
66 | /**
67 | * 欄位值
68 | */
69 | value: PropTypes.string,
70 |
71 | /**
72 | * onChange callback function
73 | */
74 | onChange: PropTypes.func,
75 |
76 | /**
77 | * dataOptions
78 | */
79 | dataOptions: PropTypes.arrayOf(PropTypes.string),
80 |
81 | /**
82 | * class
83 | */
84 | districtClass: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
85 |
86 | /**
87 | * style
88 | */
89 | districtStyle: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
90 | };
--------------------------------------------------------------------------------
/es/zipcode/County.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | /**
5 | * 縣市
6 | *
7 | * @author Chris Tsai
8 | */
9 | export default class County extends React.Component {
10 |
11 | constructor(props) {
12 | super(props);
13 | }
14 |
15 | handleChange = (e) =>{
16 | let value = e.target.value;
17 | let {onChange} = this.props;
18 | if(typeof (onChange) == 'function'){
19 | onChange(value);
20 | }
21 | }
22 |
23 | render() {
24 | const {
25 | dataOptions, fieldName, countyClass, countyStyle, value, displayType
26 | } = this.props;
27 |
28 | const counties = !!dataOptions && dataOptions.map((op) =>
29 | );
30 | return (
31 | <>
32 | {!!displayType && displayType === 'text' ?
33 |
41 | :
42 | <>
43 | {value}
48 |
49 | >
50 | }
51 | >
52 | );
53 | }
54 | }
55 |
56 | County.propTypes = {
57 | /**
58 | * 顯示樣式
59 | */
60 | displayType: PropTypes.oneOf(['text', 'display']),
61 | /**
62 | * 欄位名稱
63 | */
64 | fieldName: PropTypes.string.isRequired,
65 |
66 | /**
67 | * 欄位值
68 | */
69 | value: PropTypes.string,
70 |
71 | /**
72 | * onChange callback function
73 | */
74 | onChange: PropTypes.func,
75 |
76 | /**
77 | * dataOptions
78 | */
79 | dataOptions: PropTypes.arrayOf(PropTypes.string),
80 |
81 | /**
82 | * class
83 | */
84 | countyClass: PropTypes.oneOfType(
85 | [PropTypes.string, PropTypes.array, PropTypes.object]),
86 |
87 | /**
88 | * style
89 | */
90 | countyStyle: PropTypes.oneOfType(
91 | [PropTypes.string, PropTypes.array, PropTypes.object]),
92 | };
--------------------------------------------------------------------------------
/es/zipcode/ZipCode.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | /**
5 | * 郵遞區號
6 | *
7 | * @author Chris Tsai
8 | */
9 | export default class ZipCode extends React.Component {
10 |
11 | constructor(props) {
12 | super(props);
13 | }
14 |
15 | handleChange = (e) =>{
16 | let value = e.target.value;
17 | let {onChange} = this.props;
18 | if((value == '' || /^\d+$/.test(value)) && value.length <= 3
19 | && typeof (onChange) == 'function'){
20 | onChange(value);
21 | }
22 | }
23 |
24 | handleBlur = (e) =>{
25 | let value = e.target.value;
26 | let {onBlur} = this.props;
27 | if(typeof (onBlur) == 'function'){
28 | onBlur(value);
29 | }
30 | }
31 |
32 | render() {
33 | const {fieldName, zipClass, zipStyle, value, displayType, placeholder
34 | } = this.props;
35 |
36 | const nowStyle = typeof(zipStyle) == 'undefined' ? {width:'40px'} : zipStyle;
37 | return (
38 | <>
39 | {!!displayType && displayType === 'text' ?
40 |
50 | :
51 | <>
52 | {value}
57 |
58 | >
59 | }
60 | >
61 | );
62 | }
63 | }
64 |
65 | ZipCode.propTypes = {
66 | /**
67 | * 顯示樣式
68 | */
69 | displayType: PropTypes.oneOf(['text', 'display']),
70 | /**
71 | * 欄位名稱
72 | */
73 | fieldName: PropTypes.string.isRequired,
74 |
75 | /**
76 | * 欄位描述
77 | */
78 | placeholder: PropTypes.string,
79 |
80 | /**
81 | * 欄位值
82 | */
83 | value: PropTypes.string,
84 |
85 | /**
86 | * onChange callback function
87 | */
88 | onChange: PropTypes.func,
89 |
90 | /**
91 | * onBlur callback function
92 | */
93 | onBlur: PropTypes.func,
94 |
95 | /**
96 | * class
97 | */
98 | zipClass: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
99 |
100 | /**
101 | * style
102 | */
103 | zipStyle: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
104 | };
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import ZipCodeTW from "./es/zipcode/ZipCodeTW";
4 |
5 | class ZipCodeTWTest extends React.Component {
6 |
7 | constructor() {
8 | super();
9 | this.state = {
10 | county: '',
11 | district: '',
12 | zipCode: '',
13 | }
14 | }
15 |
16 | // 變更地址資訊
17 | handleZipCodeChange = (e) =>{
18 | const {countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue} = e;
19 | this.setState({
20 | [zipFieldName]: zipValue,
21 | [countyFieldName]: countyValue,
22 | [districtFieldName]: districtValue,
23 | });
24 | }
25 |
26 | render() {
27 | let countySort = {
28 | "基隆市": 2,
29 | "台北市": 1,
30 | "新北市": 3,
31 | "桃園市": 4,
32 | "新竹市": 5,
33 | "新竹縣": 6,
34 | "苗栗縣": 7,
35 | "台中市": 8,
36 | "彰化縣": 9,
37 | "南投縣": 10,
38 | "雲林縣": 11,
39 | "嘉義市": 12,
40 | "嘉義縣": 13,
41 | "台南市": 14,
42 | "高雄市": 15,
43 | "屏東縣": 16,
44 | "台東縣": 17,
45 | "花蓮縣": 18,
46 | "宜蘭縣": 19,
47 | "澎湖縣": 20,
48 | "金門縣": 21,
49 | "連江縣": 22
50 | };
51 | return (
52 |
53 |
Example
54 |
59 |
81 |
82 |
83 | );
84 | }
85 | }
86 |
87 | window.app = {};
88 |
89 | app.create = (dom) => {
90 | ReactDOM.render(
91 | ,
92 | dom
93 | )
94 | };
--------------------------------------------------------------------------------
/es/_test_/District.test.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import District from "../zipcode/District";
3 | import Enzyme, {mount} from 'enzyme';
4 | import Adapter from 'enzyme-adapter-react-16';
5 | import mockStore from "../config_test/mockStore";
6 |
7 | Enzyme.configure({adapter: new Adapter()});
8 |
9 | function setup(fieldName, value, className, displayType, mockFn) {
10 | const store = mockStore({
11 | district: {
12 | fieldName: fieldName,
13 | value: value,
14 | districtClass: className,
15 | displayType: displayType,
16 | onChange: mockFn,
17 | dataOptions: ['北區','東區','西區','南區']
18 | }
19 | });
20 | const state = store.getState();
21 | const props = Object.assign({}, state.district);
22 | const wrapper = mount();
23 |
24 | return {
25 | props,
26 | wrapper
27 | }
28 | }
29 |
30 | describe('District test', () => {
31 | it('test displayType= text', () => {
32 | const onChangeMock = jest.fn();
33 | const fieldName = 'district';
34 | const value = '東區';
35 | const changeValue = '西區';
36 | const displayType = 'text';
37 | const className = 'form-control';
38 | const {wrapper} = setup(fieldName, value, className, displayType, onChangeMock);
39 | expect(wrapper.find('select').props().value).toEqual(value);
40 | expect(wrapper.find('select').props().name).toEqual(fieldName);
41 | expect(wrapper.find('select').hasClass('form-control')).toBe(true);
42 | expect(wrapper.find('span').exists()).toBe(false);
43 | wrapper.find('select').simulate('change', {target: {value: changeValue} });
44 | expect(onChangeMock).toBeCalledWith(changeValue);
45 | });
46 |
47 | it('test displayType= text with no function', () => {
48 | const onChangeMock = undefined;
49 | const fieldName = 'district';
50 | const value = '東區';
51 | const changeValue = '西區';
52 | const displayType = 'text';
53 | const className = 'form-control';
54 | const {wrapper} = setup(fieldName, value, className, displayType, onChangeMock);
55 | expect(wrapper.find('select').props().value).toEqual(value);
56 | expect(wrapper.find('select').props().name).toEqual(fieldName);
57 | expect(wrapper.find('select').hasClass('form-control')).toBe(true);
58 | expect(wrapper.find('span').exists()).toBe(false);
59 | wrapper.find('select').simulate('change', {target: {value: changeValue} });
60 | });
61 |
62 | it('test displayType= display', () => {
63 | const fieldName = 'district';
64 | const value = '東區';
65 | const displayType = 'display';
66 | const className = 'form-control';
67 | const {wrapper} = setup(fieldName, value, className, displayType);
68 | expect(wrapper.find('select').exists()).toBe(false);
69 | expect(wrapper.find('span').exists()).toBe(true);
70 | });
71 | });
--------------------------------------------------------------------------------
/es/_test_/County.test.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import County from "../zipcode/County";
3 | import Enzyme, {shallow, mount} from 'enzyme';
4 | import Adapter from 'enzyme-adapter-react-16';
5 | import mockStore from "../config_test/mockStore";
6 |
7 | Enzyme.configure({adapter: new Adapter()});
8 |
9 | function setup(fieldName, value, className, displayType, mockFn) {
10 | const store = mockStore({
11 | county: {
12 | fieldName: fieldName,
13 | value: value,
14 | countyClass: className,
15 | displayType: displayType,
16 | onChange: mockFn,
17 | dataOptions: ['台北市','新北市','桃園市','台中市']
18 | }
19 | });
20 | const state = store.getState();
21 | const props = Object.assign({}, state.county);
22 | const wrapper = mount();
23 |
24 | return {
25 | props,
26 | wrapper
27 | }
28 | }
29 |
30 | describe('County test', () => {
31 | it('test displayType= text', () => {
32 | const onChangeMock = jest.fn();
33 | const countyFieldName = 'zipName';
34 | const countyValue = '台北市';
35 | const changeCountyValue = '新北市';
36 | const displayType = 'text';
37 | const countyClass = 'form-control';
38 | const {wrapper} = setup(countyFieldName, countyValue, countyClass, displayType, onChangeMock);
39 | expect(wrapper.find('select').props().value).toEqual(countyValue);
40 | expect(wrapper.find('select').props().name).toEqual(countyFieldName);
41 | expect(wrapper.find('select').hasClass('form-control')).toBe(true);
42 | expect(wrapper.find('span').exists()).toBe(false);
43 | wrapper.find('select').simulate('change', {target: {value: changeCountyValue} });
44 | expect(onChangeMock).toBeCalledWith(changeCountyValue);
45 | });
46 |
47 | it('test displayType= text with no function', () => {
48 | const onChangeMock = undefined;
49 | const countyFieldName = 'zipName';
50 | const countyValue = '台北市';
51 | const changeCountyValue = '新北市';
52 | const displayType = 'text';
53 | const countyClass = 'form-control';
54 | const {wrapper} = setup(countyFieldName, countyValue, countyClass, displayType, onChangeMock);
55 | expect(wrapper.find('select').props().value).toEqual(countyValue);
56 | expect(wrapper.find('select').props().name).toEqual(countyFieldName);
57 | expect(wrapper.find('select').hasClass('form-control')).toBe(true);
58 | expect(wrapper.find('span').exists()).toBe(false);
59 | wrapper.find('select').simulate('change', {target: {value: changeCountyValue} });
60 | });
61 |
62 | it('test displayType= display', () => {
63 | const countyFieldName = 'zipName';
64 | const countyValue = '台北市';
65 | const displayType = 'display';
66 | const countyClass = 'form-control';
67 | const {wrapper} = setup(countyFieldName, countyValue, countyClass, displayType);
68 | expect(wrapper.find('select').exists()).toBe(false);
69 | expect(wrapper.find('span').exists()).toBe(true);
70 | });
71 | });
--------------------------------------------------------------------------------
/_example/README.md:
--------------------------------------------------------------------------------
1 | # zipcode-tw-react-example
2 |
3 | ## Installation
4 |
5 | In zipcode-tw-react
6 | ```bash
7 | $ cd _example
8 | $ npm install
9 | $ npm run build
10 | ```
11 |
12 | Open `_example/index.html` and you will see
13 |
14 | 
15 |
16 | ---
17 | ## Useage
18 |
19 | 程式參考: [Address.js](https://github.com/Chris-Tsai/zipcode-tw-react/blob/master/_example/js/Address.js "Address.js")
20 |
21 | #### ZipCodeTW with address
22 | ```javascript
23 |
24 |
34 |
35 |
41 |
42 | ```
43 |
44 | #### Use zipCodePositionLast: false
45 | ```javascript
46 |
47 |
58 |
59 | ```
60 |
61 |
62 | #### No use address
63 | ```javascript
64 |
65 |
70 |
71 | ```
72 |
73 | #### Use address
74 | ```javascript
75 |
76 |
82 |
83 | ```
84 | #### Use fullAddress
85 | ```javascript
86 |
87 |
93 |
94 | ```
95 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zipcode-tw-react",
3 | "version": "1.2.0",
4 | "description": "",
5 | "main": "lib/index.js",
6 | "jsnext:main": "es/index.js",
7 | "dependencies": {
8 | "prop-types": "^15.5.8",
9 | "react": "^16.6.0",
10 | "react-dom": "^16.6.0"
11 | },
12 | "devDependencies": {
13 | "@babel/cli": "^7.0.0-beta.36",
14 | "babel-loader": "^8.0.2",
15 | "@babel/core": "^7.0.0-rc.1",
16 | "@babel/plugin-proposal-class-properties": "^7.0.0-rc.1",
17 | "@babel/plugin-proposal-json-strings": "^7.0.0-rc.1",
18 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0-rc.1",
19 | "@babel/plugin-syntax-dynamic-import": "^7.0.0-rc.1",
20 | "@babel/plugin-syntax-import-meta": "^7.0.0-rc.1",
21 | "@babel/polyfill": "^7.0.0-rc.1",
22 | "@babel/preset-env": "^7.0.0-rc.1",
23 | "@babel/preset-flow": "^7.0.0-rc.1",
24 | "@babel/preset-react": "^7.0.0-rc.1",
25 | "babel-core": "^7.0.0-bridge.0",
26 | "fs-extra": "^5.0.0",
27 | "webpack": "^3.10.0",
28 | "webpack-body-parser": "^1.11.110",
29 | "webpack-dev-server": "^2.10.0",
30 | "webpack-encoding-plugin": "^0.2.0",
31 | "eslint": "^5.8.0",
32 | "eslint-plugin-react": "^7.11.1",
33 | "babel-eslint": "^10.0.1",
34 | "jest": "^23.6.0",
35 | "babel-jest": "^23.6.0",
36 | "react-test-renderer": "^16.6.0",
37 | "css-loader": "^0.28.11",
38 | "style-loader": "^0.19.1",
39 | "check-node-version": "^3.2.0",
40 | "enzyme": "^3.7.0",
41 | "enzyme-adapter-react-16": "^1.6.0",
42 | "redux-mock-store": "^1.5.3",
43 | "redux": "^3.6.0",
44 | "coveralls": "^3.0.2"
45 | },
46 | "scripts": {
47 | "lint": "check-node-version --package && eslint ./es/zipcode/*.js",
48 | "test": "check-node-version --package && jest --coverage --coverageReporters=text-lcov | coveralls",
49 | "build": "check-node-version --package && node ./scripts/build.js",
50 | "start": "check-node-version --package && node ./scripts/start.js",
51 | "compile": "./node_modules/.bin/babel es -d lib --ignore es/_test_/**,es/config_test/**",
52 | "release": "./node_modules/.bin/babel es -d lib --ignore es/_test_/**,es/config_test/** && npm publish --access=public"
53 | },
54 | "jest": {
55 | "collectCoverageFrom": [
56 | "es/**/*.js",
57 | "!**/__tests__/**",
58 | "!es/config_test/**",
59 | "!es/data/**",
60 | "!es/index.js"
61 | ],
62 | "moduleNameMapper": {
63 | "\\.(css|less)$": "identity-obj-proxy"
64 | }
65 | },
66 | "engines": {
67 | "node": ">=6.2.0",
68 | "npm": ">=5.4.2"
69 | },
70 | "repository": {
71 | "type": "git",
72 | "url": "git+https://github.com/Chris-Tsai/zipcode-tw-react.git"
73 | },
74 | "keywords": [
75 | "react",
76 | "zipcode",
77 | "taiwan",
78 | "tw",
79 | "郵遞區號",
80 | "台灣"
81 | ],
82 | "author": "Chris.Tsai",
83 | "license": "MIT",
84 | "bugs": {
85 | "url": "https://github.com/Chris-Tsai/zipcode-tw-react/issues"
86 | },
87 | "homepage": "https://github.com/Chris-Tsai/zipcode-tw-react#readme"
88 | }
89 |
--------------------------------------------------------------------------------
/es/_test_/ZipCode.test.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import ZipCode from "../zipcode/ZipCode";
3 | import Enzyme, {mount} from 'enzyme';
4 | import Adapter from 'enzyme-adapter-react-16';
5 | import mockStore from "../config_test/mockStore";
6 |
7 | Enzyme.configure({adapter: new Adapter()});
8 |
9 | function setup(fieldName, value, className, displayType, mockFn, mockBlur) {
10 | const store = mockStore({
11 | zipCode: {
12 | fieldName: fieldName,
13 | value: value,
14 | zipClass: className,
15 | displayType: displayType,
16 | onChange: mockFn,
17 | onBlur: mockBlur
18 | }
19 | });
20 | const state = store.getState();
21 | const props = Object.assign({}, state.zipCode);
22 | const wrapper = mount();
23 |
24 | return {
25 | props,
26 | wrapper
27 | }
28 | }
29 |
30 | describe('ZipCode test', () => {
31 | it('test displayType= text', () => {
32 | const onChangeMock = jest.fn();
33 | const onBlurMock = jest.fn();
34 | const fieldName = 'zipCode';
35 | const value = '100';
36 | const changeValue = '111';
37 | const displayType = 'text';
38 | const className = 'form-control';
39 | const {wrapper} = setup(fieldName, value, className, displayType, onChangeMock, onBlurMock);
40 | expect(wrapper.find('input').props().value).toEqual(value);
41 | expect(wrapper.find('input').props().name).toEqual(fieldName);
42 | expect(wrapper.find('input').hasClass('form-control')).toBe(true);
43 | expect(wrapper.find('span').exists()).toBe(false);
44 | wrapper.find('input').simulate('change', {target: {value: changeValue} });
45 | expect(onChangeMock).toBeCalledWith(changeValue);
46 |
47 | wrapper.find('input').simulate('blur', {target: {value: changeValue} });
48 | expect(onBlurMock).toBeCalledWith(changeValue);
49 | });
50 |
51 | it('test displayType= text with no function', () => {
52 | const onChangeMock = undefined;
53 | const onBlurMock = undefined;
54 | const fieldName = 'zipCode';
55 | const value = '100';
56 | const changeValue = '111';
57 | const displayType = 'text';
58 | const className = 'form-control';
59 | const {wrapper} = setup(fieldName, value, className, displayType, onChangeMock, onBlurMock);
60 | expect(wrapper.find('input').props().value).toEqual(value);
61 | expect(wrapper.find('input').props().name).toEqual(fieldName);
62 | expect(wrapper.find('input').hasClass('form-control')).toBe(true);
63 | expect(wrapper.find('span').exists()).toBe(false);
64 | wrapper.find('input').simulate('change', {target: {value: changeValue} });
65 |
66 | wrapper.find('input').simulate('blur', {target: {value: changeValue} });
67 | });
68 |
69 | it('test displayType= display', () => {
70 | const fieldName = 'zipCode';
71 | const value = '100';
72 | const displayType = 'display';
73 | const className = 'form-control';
74 | const {wrapper} = setup(fieldName, value, className, displayType);
75 | expect(wrapper.find('input[type=\'text\']').exists()).toBe(false);
76 | expect(wrapper.find('span').exists()).toBe(true);
77 | });
78 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # zipcode-tw-react
2 | 提供台灣縣市、行政區下拉選單以及郵遞區號輸入欄位組合的React Component
3 | 藉由RawData快速進行郵遞區號切換,並提供地址合併顯示。
4 |
5 | [](https://travis-ci.org/Chris-Tsai/zipcode-tw-react.svg?branch=master)
6 | [](https://coveralls.io/github/Chris-Tsai/zipcode-tw-react?branch=master)
7 | [](https://github.com/Chris-Tsai/zipcode-tw-react/blob/master/LICENSE)
8 |
9 |
10 | ## Feature
11 | - 挑選台灣縣市、行政區下拉選單,快速帶出郵遞區號。
12 | - 提供可自定義台灣縣市下拉選單排序(countySort)。
13 | - 輸入郵遞區號,快速帶出台灣縣市、行政區。
14 | - 提供完整地址(fullAddress)或路段地址(address)欄位傳入,合併顯示郵遞區號及地址。
15 | - 可自定義下拉選單、輸入欄位、地址顯示欄位的CSS, Style,達到畫面的一致性。
16 |
17 |
18 | ## Demo
19 | Try it online: https://chris-tsai.github.io/
20 |
21 | 
22 |
23 | ## Installation
24 |
25 | ```bash
26 | npm install zipcode-tw-react --save
27 | ```
28 | or use package.json
29 |
30 | ```bash
31 | "dependencies": {
32 | ...
33 | + "zipcode-tw-react": "^1.2.0",
34 | },
35 | ```
36 |
37 | ## Usage
38 |
39 | ```javascript
40 | import {ZipCodeTW} from "zipcode-tw-react";
41 |
42 |
45 | ```
46 | Example : [zipcode-tw-react-example](https://github.com/Chris-Tsai/zipcode-tw-react/tree/master/_example)
47 |
48 | ## Props
49 |
50 | ###### Field
51 |
52 | Name | Type | Default | Description
53 | :--- | :--- | :--- | :---
54 | displayType| one of: 'text', 'display' | 'text' | displayType= display
1. 以span顯示且包含readOnly & disabled屬性
2. 提供fullAddress、address參數合併顯示郵遞區號及地址
55 | countySort| object | {"基隆市": 1, "台北市":2, "新北市":3,
"桃園市":4, "新竹市":5, "新竹縣":6,
"苗栗縣":7, "台中市":8, "彰化縣":9,
"南投縣":10,"雲林縣":11, "嘉義市":12,
"嘉義縣":13, "台南市":14, "高雄市":15,
"屏東縣":16, "台東縣":17, "花蓮縣":18,
"宜蘭縣":19, "澎湖縣":20,"金門縣":21,
"連江縣":22}|
56 | zipCodePositionLast| bool | true| Decide zipCode input text position,
when displayType= display, position is fixed
57 | countyFieldName | string |'county' |
58 | countyValue | string | |
59 | districtFieldName | string |'district' |
60 | districtValue | string | |
61 | zipCodeFieldName | string |'zipCode' |
62 | zipCodeValue | string | |
63 | countyClass | string |'form-control' |
64 | countyStyle | object | {} |
65 | districtClass | string |'form-control' |
66 | districtStyle | object | displayType= 'text'
預設為 {marginLeft:'5px', minWidth:'107px', paddingRight:'0px'} |
67 | zipClass | string | 'form-control'|
68 | zipStyle | object | displayType= 'text'
預設為 {marginLeft:'5px', width: '50px'}|
69 | zipCodePlaceholder | string | |
70 | fullAddress | string | | 完整地址(優化顯示)
71 | address | string | | 路段地址資訊(優化顯示)
72 | addressClass | string | 'form-control'|
73 | addressStyle | object | {} |
74 |
75 | ###### Method
76 |
77 | Name | Return | Description
78 | :--- | :--- | :---
79 | handleChangeCounty | { countyFieldName, countyValue,
districtFieldName, districtValue,
zipFieldName, zipValue }
80 | handleChangeDistrict | { countyFieldName, countyValue,
districtFieldName, districtValue,
zipFieldName, zipValue }
81 | handleChangeZipCode | { zipFieldName, zipValue }
82 | handleBlurZipCode | { countyFieldName, countyValue,
districtFieldName, districtValue,
zipFieldName, zipValue }
83 | handleZipCodeNotExists | { countyFieldName, countyValue,
districtFieldName, districtValue,
zipFieldName, zipValue, origZipCode }
84 |
85 |
89 |
--------------------------------------------------------------------------------
/_example/js/Address.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import swal from "sweetalert2";
3 | import {ZipCodeTW} from "zipcode-tw-react"
4 |
5 | class Address extends React.Component {
6 |
7 | constructor(props) {
8 | super(props);
9 |
10 | this.state = {
11 | displayType: 'text',
12 | county: '台北市',
13 | district: '中山區',
14 | zipCode: '104',
15 | address: '敬業三路20號'
16 | }
17 | }
18 |
19 | handleChange = (e) =>{
20 | this.setState({[e.target.name]: e.target.value});
21 | }
22 |
23 | // 變更地址資訊
24 | handleZipCodeChange = (e) =>{
25 | const {countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue} = e;
26 | this.setState({
27 | [zipFieldName]: zipValue,
28 | [countyFieldName]: countyValue,
29 | [districtFieldName]: districtValue,
30 | });
31 | }
32 |
33 | // 處理郵遞區號不存在
34 | handleZipCodeNotExists = (e) =>{
35 | const {countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue, origZipValue} = e;
36 | this.setState({
37 | [zipFieldName]: zipValue,
38 | [countyFieldName]: countyValue,
39 | [districtFieldName]: districtValue
40 | });
41 |
42 | swal('郵遞區號不存在: ' + origZipValue, '', 'error');
43 | }
44 |
45 | render() {
46 | let addressShow = this.state.displayType === 'display' ? 'none' : 'inline';
47 | let fullAddress = this.state.county+this.state.district+this.state.address;
48 | return (
49 | <>
50 |
51 |
52 |
53 |
63 |
64 |
70 |
71 |
72 |
73 |
74 |
75 |
86 |
87 |
88 |
89 |
90 |
DisplayType: 'display'
91 |
92 |
93 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
123 |
124 |
125 | >
126 | );
127 | }
128 | }
129 |
130 | export default Address;
--------------------------------------------------------------------------------
/es/data/RawData.js:
--------------------------------------------------------------------------------
1 | export default {
2 | '': {'':''},
3 | 基隆市: {
4 | 仁愛區: '200',
5 | 信義區: '201',
6 | 中正區: '202',
7 | 中山區: '203',
8 | 安樂區: '204',
9 | 暖暖區: '205',
10 | 七堵區: '206',
11 | },
12 | 台北市: {
13 | 中正區: '100',
14 | 大同區: '103',
15 | 中山區: '104',
16 | 松山區: '105',
17 | 大安區: '106',
18 | 萬華區: '108',
19 | 信義區: '110',
20 | 士林區: '111',
21 | 北投區: '112',
22 | 內湖區: '114',
23 | 南港區: '115',
24 | 文山區: '116',
25 | },
26 | 新北市: {
27 | 萬里區: '207',
28 | 金山區: '208',
29 | 板橋區: '220',
30 | 汐止區: '221',
31 | 深坑區: '222',
32 | 石碇區: '223',
33 | 瑞芳區: '224',
34 | 平溪區: '226',
35 | 雙溪區: '227',
36 | 貢寮區: '228',
37 | 新店區: '231',
38 | 坪林區: '232',
39 | 烏來區: '233',
40 | 永和區: '234',
41 | 中和區: '235',
42 | 土城區: '236',
43 | 三峽區: '237',
44 | 樹林區: '238',
45 | 鶯歌區: '239',
46 | 三重區: '241',
47 | 新莊區: '242',
48 | 泰山區: '243',
49 | 林口區: '244',
50 | 蘆洲區: '247',
51 | 五股區: '248',
52 | 八里區: '249',
53 | 淡水區: '251',
54 | 三芝區: '252',
55 | 石門區: '253',
56 | },
57 | 宜蘭縣: {
58 | 宜蘭市: '260',
59 | 頭城鎮: '261',
60 | 礁溪鄉: '262',
61 | 壯圍鄉: '263',
62 | 員山鄉: '264',
63 | 羅東鎮: '265',
64 | 三星鄉: '266',
65 | 大同鄉: '267',
66 | 五結鄉: '268',
67 | 冬山鄉: '269',
68 | 蘇澳鎮: '270',
69 | 南澳鄉: '272',
70 | 釣魚台列嶼: '290',
71 | },
72 | 新竹市: {
73 | 東區: '300',
74 | 北區: '300',
75 | 香山區: '300',
76 | },
77 | 新竹縣: {
78 | 竹北市: '302',
79 | 湖口鄉: '303',
80 | 新豐鄉: '304',
81 | 新埔鎮: '305',
82 | 關西鎮: '306',
83 | 芎林鄉: '307',
84 | 寶山鄉: '308',
85 | 竹東鎮: '310',
86 | 五峰鄉: '311',
87 | 橫山鄉: '312',
88 | 尖石鄉: '313',
89 | 北埔鄉: '314',
90 | 峨嵋鄉: '315',
91 | },
92 | 桃園市: {
93 | 中壢區: '320',
94 | 平鎮區: '324',
95 | 龍潭區: '325',
96 | 楊梅區: '326',
97 | 新屋區: '327',
98 | 觀音區: '328',
99 | 桃園區: '330',
100 | 龜山區: '333',
101 | 八德區: '334',
102 | 大溪區: '335',
103 | 復興區: '336',
104 | 大園區: '337',
105 | 蘆竹區: '338',
106 | },
107 | 苗栗縣: {
108 | 竹南鎮: '350',
109 | 頭份市: '351',
110 | 三灣鄉: '352',
111 | 南庄鄉: '353',
112 | 獅潭鄉: '354',
113 | 後龍鎮: '356',
114 | 通霄鎮: '357',
115 | 苑裡鎮: '358',
116 | 苗栗市: '360',
117 | 造橋鄉: '361',
118 | 頭屋鄉: '362',
119 | 公館鄉: '363',
120 | 大湖鄉: '364',
121 | 泰安鄉: '365',
122 | 銅鑼鄉: '366',
123 | 三義鄉: '367',
124 | 西湖鄉: '368',
125 | 卓蘭鎮: '369',
126 | },
127 | 台中市: {
128 | 中區: '400',
129 | 東區: '401',
130 | 南區: '402',
131 | 西區: '403',
132 | 北區: '404',
133 | 北屯區: '406',
134 | 西屯區: '407',
135 | 南屯區: '408',
136 | 太平區: '411',
137 | 大里區: '412',
138 | 霧峰區: '413',
139 | 烏日區: '414',
140 | 豐原區: '420',
141 | 后里區: '421',
142 | 石岡區: '422',
143 | 東勢區: '423',
144 | 和平區: '424',
145 | 新社區: '426',
146 | 潭子區: '427',
147 | 大雅區: '428',
148 | 神岡區: '429',
149 | 大肚區: '432',
150 | 沙鹿區: '433',
151 | 龍井區: '434',
152 | 梧棲區: '435',
153 | 清水區: '436',
154 | 大甲區: '437',
155 | 外埔區: '438',
156 | 大安區: '439',
157 | },
158 | 彰化縣: {
159 | 彰化市: '500',
160 | 芬園鄉: '502',
161 | 花壇鄉: '503',
162 | 秀水鄉: '504',
163 | 鹿港鎮: '505',
164 | 福興鄉: '506',
165 | 線西鄉: '507',
166 | 和美鎮: '508',
167 | 伸港鄉: '509',
168 | 員林市: '510',
169 | 社頭鄉: '511',
170 | 永靖鄉: '512',
171 | 埔心鄉: '513',
172 | 溪湖鎮: '514',
173 | 大村鄉: '515',
174 | 埔鹽鄉: '516',
175 | 田中鎮: '520',
176 | 北斗鎮: '521',
177 | 田尾鄉: '522',
178 | 埤頭鄉: '523',
179 | 溪州鄉: '524',
180 | 竹塘鄉: '525',
181 | 二林鎮: '526',
182 | 大城鄉: '527',
183 | 芳苑鄉: '528',
184 | 二水鄉: '530',
185 | },
186 | 南投縣: {
187 | 南投市: '540',
188 | 中寮鄉: '541',
189 | 草屯鎮: '542',
190 | 國姓鄉: '544',
191 | 埔里鎮: '545',
192 | 仁愛鄉: '546',
193 | 名間鄉: '551',
194 | 集集鎮: '552',
195 | 水里鄉: '553',
196 | 魚池鄉: '555',
197 | 信義鄉: '556',
198 | 竹山鎮: '557',
199 | 鹿谷鄉: '558',
200 | },
201 | 嘉義市: {
202 | 東區: '600',
203 | 西區: '600',
204 | },
205 | 嘉義縣: {
206 | 番路鄉: '602',
207 | 梅山鄉: '603',
208 | 竹崎鄉: '604',
209 | 阿里山: '605',
210 | 中埔鄉: '606',
211 | 大埔鄉: '607',
212 | 水上鄉: '608',
213 | 鹿草鄉: '611',
214 | 太保市: '612',
215 | 朴子市: '613',
216 | 東石鄉: '614',
217 | 六腳鄉: '615',
218 | 新港鄉: '616',
219 | 民雄鄉: '621',
220 | 大林鎮: '622',
221 | 溪口鄉: '623',
222 | 義竹鄉: '624',
223 | 布袋鎮: '625',
224 | },
225 | 雲林縣: {
226 | 斗南鎮: '630',
227 | 大埤鄉: '631',
228 | 虎尾鎮: '632',
229 | 土庫鎮: '633',
230 | 褒忠鄉: '634',
231 | 東勢鄉: '635',
232 | 臺西鄉: '636',
233 | 崙背鄉: '637',
234 | 麥寮鄉: '638',
235 | 斗六市: '640',
236 | 林內鄉: '643',
237 | 古坑鄉: '646',
238 | 莿桐鄉: '647',
239 | 西螺鎮: '648',
240 | 二崙鄉: '649',
241 | 北港鎮: '651',
242 | 水林鄉: '652',
243 | 口湖鄉: '653',
244 | 四湖鄉: '654',
245 | 元長鄉: '655',
246 | },
247 | 台南市: {
248 | 中西區: '700',
249 | 東區: '701',
250 | 南區: '702',
251 | 北區: '704',
252 | 安平區: '708',
253 | 安南區: '709',
254 | 永康區: '710',
255 | 歸仁區: '711',
256 | 新化區: '712',
257 | 左鎮區: '713',
258 | 玉井區: '714',
259 | 楠西區: '715',
260 | 南化區: '716',
261 | 仁德區: '717',
262 | 關廟區: '718',
263 | 龍崎區: '719',
264 | 官田區: '720',
265 | 麻豆區: '721',
266 | 佳里區: '722',
267 | 西港區: '723',
268 | 七股區: '724',
269 | 將軍區: '725',
270 | 學甲區: '726',
271 | 北門區: '727',
272 | 新營區: '730',
273 | 後壁區: '731',
274 | 白河區: '732',
275 | 東山區: '733',
276 | 六甲區: '734',
277 | 下營區: '735',
278 | 柳營區: '736',
279 | 鹽水區: '737',
280 | 善化區: '741',
281 | 大內區: '742',
282 | 山上區: '743',
283 | 新市區: '744',
284 | 安定區: '745',
285 | },
286 | 高雄市: {
287 | 新興區: '800',
288 | 前金區: '801',
289 | 苓雅區: '802',
290 | 鹽埕區: '803',
291 | 鼓山區: '804',
292 | 旗津區: '805',
293 | 前鎮區: '806',
294 | 三民區: '807',
295 | 楠梓區: '811',
296 | 小港區: '812',
297 | 左營區: '813',
298 | 仁武區: '814',
299 | 大社區: '815',
300 | 東沙群島: '817',
301 | 南沙群島: '819',
302 | 岡山區: '820',
303 | 路竹區: '821',
304 | 阿蓮區: '822',
305 | 田寮區: '823',
306 | 燕巢區: '824',
307 | 橋頭區: '825',
308 | 梓官區: '826',
309 | 彌陀區: '827',
310 | 永安區: '828',
311 | 湖內區: '829',
312 | 鳳山區: '830',
313 | 大寮區: '831',
314 | 林園區: '832',
315 | 鳥松區: '833',
316 | 大樹區: '840',
317 | 旗山區: '842',
318 | 美濃區: '843',
319 | 六龜區: '844',
320 | 內門區: '845',
321 | 杉林區: '846',
322 | 甲仙區: '847',
323 | 桃源區: '848',
324 | 那瑪夏區: '849',
325 | 茂林區: '851',
326 | 茄萣區: '852',
327 | },
328 | 屏東縣: {
329 | 屏東市: '900',
330 | 三地門鄉: '901',
331 | 霧臺鄉: '902',
332 | 瑪家鄉: '903',
333 | 九如鄉: '904',
334 | 里港鄉: '905',
335 | 高樹鄉: '906',
336 | 鹽埔鄉: '907',
337 | 長治鄉: '908',
338 | 麟洛鄉: '909',
339 | 竹田鄉: '911',
340 | 內埔鄉: '912',
341 | 萬丹鄉: '913',
342 | 潮州鎮: '920',
343 | 泰武鄉: '921',
344 | 來義鄉: '922',
345 | 萬巒鄉: '923',
346 | 崁頂鄉: '924',
347 | 新埤鄉: '925',
348 | 南州鄉: '926',
349 | 林邊鄉: '927',
350 | 東港鎮: '928',
351 | 琉球鄉: '929',
352 | 佳冬鄉: '931',
353 | 新園鄉: '932',
354 | 枋寮鄉: '940',
355 | 枋山鄉: '941',
356 | 春日鄉: '942',
357 | 獅子鄉: '943',
358 | 車城鄉: '944',
359 | 牡丹鄉: '945',
360 | 恆春鎮: '946',
361 | 滿州鄉: '947',
362 | },
363 | 台東縣: {
364 | 臺東市: '950',
365 | 綠島鄉: '951',
366 | 蘭嶼鄉: '952',
367 | 延平鄉: '953',
368 | 卑南鄉: '954',
369 | 鹿野鄉: '955',
370 | 關山鎮: '956',
371 | 海端鄉: '957',
372 | 池上鄉: '958',
373 | 東河鄉: '959',
374 | 成功鎮: '961',
375 | 長濱鄉: '962',
376 | 太麻里鄉: '963',
377 | 金峰鄉: '964',
378 | 大武鄉: '965',
379 | 達仁鄉: '966',
380 | },
381 | 花蓮縣: {
382 | 花蓮市: '970',
383 | 新城鄉: '971',
384 | 秀林鄉: '972',
385 | 吉安鄉: '973',
386 | 壽豐鄉: '974',
387 | 鳳林鎮: '975',
388 | 光復鄉: '976',
389 | 豐濱鄉: '977',
390 | 瑞穗鄉: '978',
391 | 萬榮鄉: '979',
392 | 玉里鎮: '981',
393 | 卓溪鄉: '982',
394 | 富里鄉: '983',
395 | },
396 | 金門縣: {
397 | 金沙鎮: '890',
398 | 金湖鎮: '891',
399 | 金寧鄉: '892',
400 | 金城鎮: '893',
401 | 烈嶼鄉: '894',
402 | 烏坵鄉: '896',
403 | },
404 | 連江縣: {
405 | 南竿鄉: '209',
406 | 北竿鄉: '210',
407 | 莒光鄉: '211',
408 | 東引鄉: '212',
409 | },
410 | 澎湖縣: {
411 | 馬公市: '880',
412 | 西嶼鄉: '881',
413 | 望安鄉: '882',
414 | 七美鄉: '883',
415 | 白沙鄉: '884',
416 | 湖西鄉: '885',
417 | },
418 | };
--------------------------------------------------------------------------------
/es/zipcode/ZipCodeTW.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import PropTypes from "prop-types";
3 | import District from "./District";
4 | import County from "./County";
5 | import ZipCode from "./ZipCode";
6 | import RawData from '../data/RawData';
7 | import RawDataSort from '../data/RawDataSort';
8 |
9 | /**
10 | * 組合元件
11 | *
12 | * @author Chris Tsai
13 | */
14 | export default class ZipCodeTW extends React.Component {
15 |
16 | constructor(props) {
17 | super(props);
18 | this.state = {
19 | countyFieldName: 'county',
20 | districtFieldName: 'district',
21 | zipCodeFieldName: 'zipCode',
22 | county: 'county',
23 | counties: [],
24 | district: 'district',
25 | districts: [],
26 | zipCode: 'zipCode',
27 | zipCodePlaceholder: '',
28 | };
29 | }
30 |
31 | componentDidUpdate(prevProps) {
32 | const {
33 | countyValue,
34 | districtValue,
35 | zipCodeValue
36 | } = this.props;
37 | if(prevProps.countyValue !== countyValue
38 | || prevProps.districtValue !== districtValue
39 | || prevProps.zipCodeValue !== zipCodeValue) {
40 | this.initData();
41 | }
42 | }
43 |
44 | componentDidMount() {
45 | this.initData();
46 | }
47 |
48 | initData = () =>{
49 | const counties = Object.keys(RawData);
50 | const {
51 | countyValue,
52 | districtValue,
53 | zipCodeValue,
54 | countyFieldName,
55 | districtFieldName,
56 | zipCodeFieldName,
57 | countySort,
58 | } = this.props;
59 | if(typeof countySort != 'undefined'){
60 | counties.sort(function (a, b) {
61 | return countySort[a] - countySort[b];
62 | });
63 | }else{
64 | counties.sort(function (a, b) {
65 | return RawDataSort[a] - RawDataSort[b];
66 | });
67 | }
68 |
69 | const county = (countyValue === '') ? counties[0] : countyValue;
70 | let district;
71 | let zipCode = typeof(zipCodeValue) == 'undefined' ? '' : zipCodeValue;
72 | let countyRaw = RawData[county];
73 | const districts = typeof(countyRaw) == 'undefined' ? [] : Object.keys(countyRaw).map((d) => d, []);
74 |
75 | if(typeof(districts) != 'undefined' && districts.length > 0){
76 | if (districtValue === '') {
77 | district = districts[0];
78 | } else if (districts.indexOf(districtValue) > -1) {
79 | district = districtValue;
80 | } else {
81 | district = districts[0];
82 | }
83 | zipCode = RawData[county][district];
84 | }
85 |
86 | const nowCountyFieldName = typeof (countyFieldName) != 'undefined' && countyFieldName !== '' ? countyFieldName: 'county';
87 | const nowDistrictFieldNam = typeof (districtFieldName) != 'undefined' && districtFieldName !== '' ? districtFieldName: 'district';
88 | const nowZipCodeFieldName = typeof (zipCodeFieldName) != 'undefined' && zipCodeFieldName !== '' ? zipCodeFieldName: 'zipCode';
89 |
90 | this.setState({
91 | county, district, zipCode,
92 | counties, districts,
93 | countyFieldName: nowCountyFieldName,
94 | districtFieldName: nowDistrictFieldNam,
95 | zipCodeFieldName: nowZipCodeFieldName
96 | });
97 | }
98 |
99 | handleChangeCounty = (county) => {
100 | const districts = Object.keys(RawData[county]).map((d) => d, []);
101 | let district = districts[0];
102 | let zipCode = RawData[county][districts[0]];
103 | let {handleChangeCounty} = this.props;
104 | let {countyFieldName, districtFieldName, zipCodeFieldName} = this.state;
105 | this.setState({
106 | county: county,
107 | districts: districts,
108 | district: district,
109 | zipCode: zipCode,
110 | }, () => {
111 | if(typeof (handleChangeCounty) == 'function'){
112 | handleChangeCounty({
113 | 'countyFieldName':countyFieldName, 'countyValue': county,
114 | 'districtFieldName':districtFieldName, 'districtValue': district,
115 | 'zipFieldName':zipCodeFieldName,'zipValue':zipCode
116 | });
117 | }
118 | });
119 | }
120 |
121 | handleChangeDistrict = (district) =>{
122 | let zipCode = RawData[this.state.county][district];
123 | let {handleChangeDistrict} = this.props;
124 | let {countyFieldName, districtFieldName, zipCodeFieldName} = this.state;
125 | this.setState({
126 | district: district,
127 | zipCode: zipCode,
128 | }, () => {
129 | if(typeof (handleChangeDistrict) == 'function'){
130 | handleChangeDistrict({
131 | 'countyFieldName': countyFieldName, 'countyValue': this.state.county,
132 | 'districtFieldName':districtFieldName, 'districtValue': district,
133 | 'zipFieldName':zipCodeFieldName,'zipValue':zipCode
134 | });
135 | }
136 | });
137 | }
138 |
139 | handleChangeZipCode = (zipCode) =>{
140 | let {handleChangeZipCode} = this.props;
141 | let {zipCodeFieldName} = this.state;
142 | this.setState({
143 | zipCode: zipCode,
144 | }, () =>{
145 | if(typeof (handleChangeZipCode) == 'function'){
146 | handleChangeZipCode({
147 | 'zipFieldName':zipCodeFieldName,'zipValue':zipCode
148 | });
149 | }
150 | });
151 | }
152 |
153 | handleBlurZipCode = (zipCode) =>{
154 | const { countyN, districtN } = this.findCountyAndDistrictByZipCode(zipCode);
155 | let {handleZipCodeNotExists, handleBlurZipCode} = this.props;
156 | let {countyFieldName, districtFieldName, zipCodeFieldName} = this.state;
157 | if(typeof(countyN) != 'undefined' && typeof(districtN) != 'undefined'){
158 | const districts = Object.keys(RawData[countyN]).map((d) => d, []);
159 | this.setState({
160 | county: countyN, district: districtN, districts: districts
161 | }, () =>{
162 | if(typeof (handleBlurZipCode) == 'function'){
163 | handleBlurZipCode({
164 | 'countyFieldName':countyFieldName, 'countyValue': countyN,
165 | 'districtFieldName':districtFieldName, 'districtValue': districtN,
166 | 'zipFieldName':zipCodeFieldName,'zipValue':zipCode
167 | });
168 | }
169 | });
170 | }else{
171 | this.setState({
172 | county: '', district: '', districts: [], zipCode: ''
173 | }, () =>{
174 | if(typeof (handleZipCodeNotExists) == 'function'){
175 | handleZipCodeNotExists({
176 | 'countyFieldName':countyFieldName, 'countyValue': '',
177 | 'districtFieldName':districtFieldName, 'districtValue': '',
178 | 'zipFieldName':zipCodeFieldName,'zipValue':'', 'origZipValue': zipCode
179 | });
180 | }
181 | });
182 | }
183 | }
184 |
185 | findCountyAndDistrictByZipCode = (zipCode) =>{
186 | let rtn = {}
187 | Object.keys(RawData).forEach((countyN) => {
188 | Object.keys(RawData[countyN]).forEach((districtN) => {
189 | if (RawData[countyN][districtN] === zipCode.toString()) {
190 | rtn = {
191 | countyN,
192 | districtN,
193 | };
194 | }
195 | });
196 | });
197 | return rtn;
198 | }
199 |
200 | render() {
201 | const {zipStyle, countyStyle, districtStyle, zipClass, countyClass, districtClass, displayType, zipCodePositionLast, countySort} = this.props;
202 | const {fullAddress, address, addressClass, addressStyle} = this.props;
203 | const displayTypeFlag = (displayType === 'display') ? true : false;
204 | const nowZipCodePositionLast = typeof (zipCodePositionLast) != 'undefined' ? zipCodePositionLast : true;
205 | const nowCountyStyle = typeof (countyStyle) != 'undefined' ? countyStyle: nowZipCodePositionLast ? {} : {marginLeft:'5px'};
206 | const nowDistrictStyle =
207 | typeof (districtStyle) != 'undefined' ? districtStyle: displayTypeFlag ? {} : {marginLeft:'5px', minWidth:'107px', paddingRight:'0px'};
208 | const nowZipStyle =
209 | typeof (zipStyle) != 'undefined' ? zipStyle: (!displayTypeFlag && !nowZipCodePositionLast) ? {width: '50px'} : {marginLeft:'5px', width: '50px'};
210 | const nowCountyClass =
211 | typeof (countyClass) != 'undefined' ? countyClass: 'form-control';
212 | const nowDistrictClass =
213 | typeof (districtClass) != 'undefined' ? districtClass: 'form-control';
214 | const nowZipClass =
215 | typeof (zipClass) != 'undefined' ? zipClass: 'form-control';
216 | const nowAddressClass =
217 | typeof (addressClass) != 'undefined' ? addressClass: 'form-control';
218 |
219 | return (
220 | <>
221 | {displayTypeFlag || (!displayTypeFlag && !nowZipCodePositionLast) ?
222 | : ''
231 | }
232 |
233 | {typeof (fullAddress) != 'undefined' && fullAddress !== '' && displayTypeFlag ?
234 | {fullAddress} : <>
238 |
246 |
254 | {!displayTypeFlag && nowZipCodePositionLast ?
255 | : ''
264 | }
265 | {typeof (address) != 'undefined' && address !== '' && displayTypeFlag ?
266 | {address} : ''
269 | }
270 | >
271 | }
272 | >
273 | );
274 | }
275 | }
276 |
277 | ZipCodeTW.propTypes = {
278 | displayType: PropTypes.oneOf(['text', 'display']).isRequired,
279 | fullAddress: PropTypes.string,
280 | zipCodePositionLast: PropTypes.bool,
281 | address: PropTypes.string,
282 | countyFieldName: PropTypes.string,
283 | countyValue: PropTypes.string,
284 | districtFieldName: PropTypes.string,
285 | districtValue: PropTypes.string,
286 | zipCodeFieldName: PropTypes.string,
287 | zipCodeValue: PropTypes.string,
288 | zipCodePlaceholder: PropTypes.string,
289 | handleChangeCounty: PropTypes.func,
290 | handleChangeDistrict: PropTypes.func,
291 | handleChangeZipCode: PropTypes.func,
292 | handleBlurZipCode: PropTypes.func,
293 | handleZipCodeNotExists: PropTypes.func,
294 | countyClass: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
295 | countyStyle: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
296 | districtClass: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
297 | districtStyle: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
298 | zipClass: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
299 | zipStyle: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
300 | addressClass: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
301 | addressStyle: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
302 | countySort: PropTypes.object,
303 | };
--------------------------------------------------------------------------------
/es/_test_/ZipCodeTW.test.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import ZipCodeTW from "../zipcode/ZipCodeTW";
3 | import Enzyme, {mount} from 'enzyme';
4 | import Adapter from 'enzyme-adapter-react-16';
5 | import mockStore from "../config_test/mockStore";
6 |
7 | Enzyme.configure({adapter: new Adapter()});
8 |
9 | function setup(countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue, zipCodePositionLast,
10 | displayType, mockFn, mockFnD, mockFnZ, notExistsZ, mockBlur, useClass,
11 | useStyle, address, fullAddress) {
12 | const store = mockStore({
13 | zipCodeTW: {
14 | countyFieldName: countyFieldName,
15 | countyValue: countyValue,
16 | districtFieldName: districtFieldName,
17 | districtValue: districtValue,
18 | zipCodeFieldName: zipFieldName,
19 | zipCodeValue: zipValue,
20 | zipCodePositionLast: zipCodePositionLast,
21 | displayType: displayType,
22 | handleChangeCounty: mockFn,
23 | handleChangeDistrict: mockFnD,
24 | handleChangeZipCode: mockFnZ,
25 | handleZipCodeNotExists: notExistsZ,
26 | handleBlurZipCode: mockBlur,
27 | countyClass: useClass,
28 | districtClass: useClass,
29 | zipClass: useClass,
30 | countyStyle: useStyle,
31 | districtStyle: useStyle,
32 | zipStyle: useStyle,
33 | addressClass: useClass,
34 | addressStyle: useStyle,
35 | address: address,
36 | fullAddress: fullAddress
37 |
38 | }
39 | });
40 | const state = store.getState();
41 | const props = Object.assign({}, state.zipCodeTW);
42 | const wrapper = mount();
43 |
44 | return {
45 | props,
46 | wrapper
47 | }
48 | }
49 |
50 | describe('ZipCodeTW test', () => {
51 | it('test displayType= text', () => {
52 | const onChangeMock = jest.fn();
53 | const onChangeMockD = jest.fn();
54 | const onChangeMockZ = jest.fn();
55 | const notExistsZ = jest.fn();
56 | const onBlurMock = jest.fn();
57 | const countyFieldName = 'zipName';
58 | const countyValue = '台北市';
59 | const districtFieldName = 'district';
60 | const districtValue = '中正區';
61 | const zipFieldName = 'zipCode';
62 | const zipValue = '100';
63 | const changeValue = '新北市';
64 | const changeDistrictValue = '萬里區';
65 | const changeZipValue = '207';
66 | const displayType = 'text';
67 | const zipCodePositionLast = false;
68 | const {wrapper} = setup(countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue,
69 | zipCodePositionLast, displayType, onChangeMock, onChangeMockD, onChangeMockZ, notExistsZ, onBlurMock);
70 | expect(wrapper.find('select[name=\''+countyFieldName+'\']').props().value).toEqual(countyValue);
71 | expect(wrapper.find('select[name=\''+countyFieldName+'\']').props().name).toEqual(countyFieldName);
72 | expect(wrapper.find('select[name=\''+countyFieldName+'\']').hasClass('form-control')).toBe(true);
73 |
74 | expect(wrapper.find('select[name=\''+districtFieldName+'\']').props().value).toEqual(districtValue);
75 | expect(wrapper.find('select[name=\''+districtFieldName+'\']').props().name).toEqual(districtFieldName);
76 | expect(wrapper.find('select[name=\''+districtFieldName+'\']').hasClass('form-control')).toBe(true);
77 |
78 | expect(wrapper.find('input[name=\''+zipFieldName+'\']').props().value).toEqual(zipValue);
79 | expect(wrapper.find('input[name=\''+zipFieldName+'\']').props().name).toEqual(zipFieldName);
80 | expect(wrapper.find('input[name=\''+zipFieldName+'\']').hasClass('form-control')).toBe(true);
81 | wrapper.find('select[name=\''+countyFieldName+'\']').simulate('change', {target: {value: changeValue} });
82 | expect(onChangeMock).toBeCalledWith({"countyFieldName": "zipName", "countyValue": "新北市", "districtFieldName": "district", "districtValue": "萬里區", "zipFieldName": "zipCode", "zipValue": "207"
83 | });
84 |
85 | wrapper.find('select[name=\''+districtFieldName+'\']').simulate('change', {target: {value: changeDistrictValue} });
86 | expect(onChangeMockD).toBeCalledWith({"countyFieldName": "zipName", "countyValue": "新北市", "districtFieldName": "district", "districtValue": "萬里區", "zipFieldName": "zipCode", "zipValue": "207"
87 | });
88 |
89 | wrapper.find('input[name=\''+zipFieldName+'\']').simulate('change', {target: {value: changeZipValue} });
90 | expect(onChangeMockZ).toBeCalledWith({"zipFieldName": "zipCode", "zipValue": "207"
91 | });
92 |
93 | wrapper.find('input[name=\''+zipFieldName+'\']').simulate('blur', {target: {value: changeZipValue} });
94 | expect(onBlurMock).toBeCalledWith({"countyFieldName": "zipName", "countyValue": "新北市", "districtFieldName": "district", "districtValue": "萬里區", "zipFieldName": "zipCode", "zipValue": "207"
95 | });
96 |
97 | wrapper.find('input[name=\''+zipFieldName+'\']').simulate('blur', {target: {value: '999'} });
98 | expect(notExistsZ).toBeCalledWith({"countyFieldName": "zipName", "countyValue": "", "districtFieldName": "district", "districtValue": "", "zipFieldName": "zipCode", "zipValue": "", "origZipValue":"999"
99 | });
100 | });
101 |
102 | it('test displayType= text (undefined)', () => {
103 | const onChangeMock = jest.fn();
104 | const onChangeMockD = jest.fn();
105 | const onChangeMockZ = jest.fn();
106 | const notExistsZ = jest.fn();
107 | const onBlurMock = jest.fn();
108 | const countyFieldName = '';
109 | const countyValue = '';
110 | const districtFieldName = '';
111 | const districtValue = undefined;
112 | const zipFieldName = '';
113 | const zipValue = undefined;
114 | const changeValue = '新北市';
115 | const changeDistrictValue = '萬里區';
116 | const changeZipValue = '207';
117 | const displayType = 'text';
118 | const useClass = 'form-control';
119 | const useStyle = {width:'100px'};
120 | const zipCodePositionLast = false;
121 | const {wrapper} = setup(countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue,
122 | zipCodePositionLast, displayType, onChangeMock, onChangeMockD, onChangeMockZ, notExistsZ, onBlurMock, useClass, useStyle);
123 | expect(wrapper.find('select[name=\'county\']').props().value).toEqual(countyValue);
124 | expect(wrapper.find('select[name=\'county\']').hasClass('form-control')).toBe(true);
125 |
126 | expect(wrapper.find('select[name=\'district\']').props().value).toEqual(districtValue);
127 | expect(wrapper.find('select[name=\'district\']').hasClass('form-control')).toBe(true);
128 |
129 | expect(wrapper.find('input[name=\'zipCode\']').props().value).toEqual(zipValue);
130 | expect(wrapper.find('input[name=\'zipCode\']').hasClass('form-control')).toBe(true);
131 |
132 | wrapper.find('select[name=\'county\']').simulate('change', {target: {value: changeValue} });
133 | expect(onChangeMock).toBeCalledWith({"countyFieldName": "county", "countyValue": "新北市", "districtFieldName": "district", "districtValue": "萬里區", "zipFieldName": "zipCode", "zipValue": "207"
134 | });
135 |
136 | wrapper.find('select[name=\'district\']').simulate('change', {target: {value: changeDistrictValue} });
137 | expect(onChangeMockD).toBeCalledWith({"countyFieldName": "county", "countyValue": "新北市", "districtFieldName": "district", "districtValue": "萬里區", "zipFieldName": "zipCode", "zipValue": "207"
138 | });
139 |
140 | wrapper.find('input[name=\'zipCode\']').simulate('change', {target: {value: changeZipValue} });
141 | expect(onChangeMockZ).toBeCalledWith({"zipFieldName": "zipCode", "zipValue": "207"
142 | });
143 |
144 | wrapper.find('input[name=\'zipCode\']').simulate('blur', {target: {value: changeZipValue} });
145 | expect(onBlurMock).toBeCalledWith({"countyFieldName": "county", "countyValue": "新北市", "districtFieldName": "district", "districtValue": "萬里區", "zipFieldName": "zipCode", "zipValue": "207"
146 | });
147 |
148 | wrapper.find('input[name=\'zipCode\']').simulate('blur', {target: {value: '999'} });
149 | expect(notExistsZ).toBeCalledWith({"countyFieldName": "county", "countyValue": "", "districtFieldName": "district", "districtValue": "", "zipFieldName": "zipCode", "zipValue": "", "origZipValue":"999"
150 | });
151 |
152 | });
153 |
154 | it('test displayType= text not equal', () => {
155 | const onChangeMock = jest.fn();
156 | const onChangeMockD = jest.fn();
157 | const onChangeMockZ = jest.fn();
158 | const notExistsZ = jest.fn();
159 | const onBlurMock = jest.fn();
160 | const countyFieldName = '';
161 | const countyValue = '台北市';
162 | const districtFieldName = '';
163 | const districtValue = '';
164 | const zipFieldName = '';
165 | const zipValue = undefined;
166 | const displayType = 'text';
167 | const useClass = 'form-control';
168 | const useStyle = {width:'100px'};
169 | const zipCodePositionLast = false;
170 | const {wrapper} = setup(countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue,
171 | zipCodePositionLast, displayType, onChangeMock, onChangeMockD, onChangeMockZ, notExistsZ, onBlurMock, useClass, useStyle);
172 | expect(wrapper.find('select[name=\'county\']').props().value).toEqual(countyValue);
173 | expect(wrapper.find('select[name=\'county\']').hasClass('form-control')).toBe(true);
174 |
175 | expect(wrapper.find('select[name=\'district\']').props().value).toEqual(districtValue);
176 | expect(wrapper.find('select[name=\'district\']').hasClass('form-control')).toBe(true);
177 |
178 | expect(wrapper.find('input[name=\'zipCode\']').props().value).toEqual(zipValue);
179 | expect(wrapper.find('input[name=\'zipCode\']').hasClass('form-control')).toBe(true);
180 |
181 | });
182 |
183 | it('test props', () => {
184 | const onChangeMock = undefined;
185 | const onChangeMockD = undefined;
186 | const onChangeMockZ = undefined;
187 | const notExistsZ = undefined;
188 | const onBlurMock = undefined;
189 | const countyFieldName = '';
190 | const countyValue = '台北市';
191 | const districtFieldName = '';
192 | const districtValue = '';
193 | const zipFieldName = '';
194 | const zipValue = undefined;
195 | const changeValue = '新北市';
196 | const changeDistrictValue = '萬里區';
197 | const changeZipValue = '207';
198 | const displayType = 'text';
199 | const useClass = 'form-control';
200 | const useStyle = {width:'100px'};
201 | const zipCodePositionLast = false;
202 | const {wrapper} = setup(countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue,
203 | zipCodePositionLast, displayType, onChangeMock, onChangeMockD, onChangeMockZ, notExistsZ, onBlurMock, useClass, useStyle);
204 | const prevProp = Object.assign(wrapper.props(), {zipCodeValue: '999'});
205 | wrapper.instance().componentDidUpdate(prevProp);
206 |
207 | wrapper.find('select[name=\'county\']').simulate('change', {target: {value: changeValue} });
208 |
209 | wrapper.find('select[name=\'district\']').simulate('change', {target: {value: changeDistrictValue} });
210 |
211 | wrapper.find('input[name=\'zipCode\']').simulate('change', {target: {value: changeZipValue} });
212 |
213 | wrapper.find('input[name=\'zipCode\']').simulate('blur', {target: {value: changeZipValue} });
214 |
215 | wrapper.find('input[name=\'zipCode\']').simulate('blur', {target: {value: '999'} });
216 | });
217 |
218 | it('test displayType= text not equal', () => {
219 | const onChangeMock = jest.fn();
220 | const onChangeMockD = jest.fn();
221 | const onChangeMockZ = jest.fn();
222 | const notExistsZ = jest.fn();
223 | const onBlurMock = jest.fn();
224 | const countyFieldName = '';
225 | const countyValue = '台北縣';
226 | const districtFieldName = '';
227 | const districtValue = '';
228 | const zipFieldName = '';
229 | const zipValue = undefined;
230 | const displayType = 'text';
231 | const useClass = 'form-control';
232 | const useStyle = {width:'100px'};
233 | const zipCodePositionLast = false;
234 | const {wrapper} = setup(countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue,
235 | zipCodePositionLast, displayType, onChangeMock, onChangeMockD, onChangeMockZ, notExistsZ, onBlurMock, useClass, useStyle);
236 | expect(wrapper.find('select[name=\'county\']').props().value).toEqual(countyValue);
237 | expect(wrapper.find('select[name=\'county\']').hasClass('form-control')).toBe(true);
238 |
239 | expect(wrapper.find('select[name=\'district\']').props().value).toEqual(districtValue);
240 | expect(wrapper.find('select[name=\'district\']').hasClass('form-control')).toBe(true);
241 |
242 | expect(wrapper.find('input[name=\'zipCode\']').props().value).toEqual(zipValue);
243 | expect(wrapper.find('input[name=\'zipCode\']').hasClass('form-control')).toBe(true);
244 |
245 | });
246 |
247 | it('test displayType= display', () => {
248 | const countyFieldName = 'zipName';
249 | const countyValue = '台北市';
250 | const districtFieldName = 'district';
251 | const districtValue = '中正區';
252 | const zipFieldName = 'zipCode';
253 | const zipValue = '100';
254 | const displayType = 'display';
255 | const zipCodePositionLast = undefined;
256 | const {wrapper} = setup(countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue, zipCodePositionLast, displayType);
257 | expect(wrapper.find('select[name=\''+countyFieldName+'\']').exists()).toBe(false);
258 | });
259 |
260 | it('test displayType= display with address', () => {
261 | const countyFieldName = 'zipName';
262 | const countyValue = '台北市';
263 | const districtFieldName = 'district';
264 | const districtValue = '中正區';
265 | const zipFieldName = 'zipCode';
266 | const zipValue = '100';
267 | const displayType = 'display';
268 | const zipCodePositionLast = undefined;
269 | const {wrapper} = setup(countyFieldName, countyValue, districtFieldName,
270 | districtValue, zipFieldName, zipValue,
271 | zipCodePositionLast, displayType, undefined, undefined, undefined,
272 | undefined, undefined, undefined, undefined, '中正路1號');
273 | expect(
274 | wrapper.find('select[name=\'' + countyFieldName + '\']').exists()).toBe(
275 | false);
276 | });
277 |
278 | it('test displayType= display with fullAddress', () => {
279 | const countyFieldName = 'zipName';
280 | const countyValue = '台北市';
281 | const districtFieldName = 'district';
282 | const districtValue = '中正區';
283 | const zipFieldName = 'zipCode';
284 | const zipValue = '100';
285 | const displayType = 'display';
286 | const zipCodePositionLast = undefined;
287 | const {wrapper} = setup(countyFieldName, countyValue, districtFieldName,
288 | districtValue, zipFieldName, zipValue,
289 | zipCodePositionLast, displayType, undefined, undefined, undefined,
290 | undefined, undefined, undefined, undefined, undefined, '台北市中正區中正路1號');
291 | expect(
292 | wrapper.find('select[name=\'' + countyFieldName + '\']').exists()).toBe(
293 | false);
294 | });
295 | });
--------------------------------------------------------------------------------
/_example/demo/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import {Panel, Table} from "react-bootstrap";
4 | import {ZipCodeTW} from "zipcode-tw-react"
5 | import swal from 'sweetalert2';
6 | import './css/demo.css';
7 |
8 | class ZipCodeTWTest extends React.Component {
9 |
10 | constructor() {
11 | super();
12 | this.state = {
13 | displayType: 'text',
14 | countyName1: 'countyValue1',
15 | countyValue1: '台北市',
16 | districtName1: 'districtValue1',
17 | districtValue1: '中山區',
18 | zipCodeName1: 'zipCodeValue1',
19 | zipCodeValue1: '104',
20 | address: '敬業三路20號',
21 | countyClass: 'form-control',
22 | countyStyle: undefined,
23 | districtClass: 'form-control',
24 | districtStyle: undefined,
25 | zipCodeClass: 'form-control',
26 | zipCodeStyle: undefined,
27 | countyStyleStr: '',
28 | districtStyleStr: 'marginLeft:\'5px\', minWidth:\'107px\', paddingRight:\'0px\'',
29 | zipCodeStyleStr: 'marginLeft:\'5px\', width: \'50px\'',
30 | handleCountyChange: {},
31 | handleDistrictChange: {},
32 | handleZipCodeChange: {},
33 | handleZipCodeBlur: {},
34 | handleZipCodeNotExists: {},
35 | addressClass: 'form-control',
36 | addressStyle: undefined,
37 | addressStyleStr: '',
38 | };
39 | }
40 |
41 | handleChange = (e) => {
42 | this.setState({[e.target.name]: e.target.value});
43 | }
44 |
45 | handleChangeObj = (e) => {
46 | let name = e.target.name;
47 | try {
48 | this.setState({
49 | [name.substring(0, name.indexOf('Str'))]: eval(
50 | '({' + e.target.value + '})'), [e.target.name]: e.target.value
51 | });
52 | } catch (ex) {
53 | this.setState({[e.target.name]: e.target.value});
54 | }
55 | }
56 |
57 | handleClick = (e) => {
58 | let show = this.state.show;
59 | let displayType = this.state.displayType;
60 | this.setState({
61 | show: !show,
62 | displayType: displayType === 'display' ? 'text' : 'display'
63 | });
64 | }
65 |
66 | handleCountyChange = (e) => {
67 | const {countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue} = e;
68 | this.setState({
69 | [zipFieldName]: zipValue,
70 | [countyFieldName]: countyValue,
71 | [districtFieldName]: districtValue,
72 | handleCountyChange: e,
73 | handleDistrictChange: {},
74 | handleZipCodeChange: {},
75 | handleZipCodeBlur: {},
76 | handleZipCodeNotExists: {}
77 | });
78 | }
79 |
80 | handleDistrictChange = (e) => {
81 | const {districtFieldName, districtValue, zipFieldName, zipValue} = e;
82 | this.setState({
83 | [zipFieldName]: zipValue,
84 | [districtFieldName]: districtValue,
85 | handleDistrictChange: e,
86 | handleCountyChange: {},
87 | handleZipCodeChange: {},
88 | handleZipCodeBlur: {},
89 | handleZipCodeNotExists: {}
90 | });
91 | }
92 |
93 | handleZipCodeChange = (e) => {
94 | this.setState({
95 | [e.zipFieldName]: e.zipValue,
96 | handleZipCodeChange: e,
97 | handleDistrictChange: {},
98 | handleCountyChange: {},
99 | handleZipCodeBlur: {},
100 | handleZipCodeNotExists: {}
101 | });
102 | }
103 |
104 | handleZipCodeBlur = (e) => {
105 | const {countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue} = e;
106 | this.setState({
107 | [zipFieldName]: zipValue,
108 | [countyFieldName]: countyValue,
109 | [districtFieldName]: districtValue,
110 | handleZipCodeBlur: e,
111 | handleDistrictChange: {},
112 | handleCountyChange: {},
113 | handleZipCodeChange: {},
114 | handleZipCodeNotExists: {}
115 | });
116 | }
117 |
118 | handleZipCodeNotExists = (e) => {
119 | const {countyFieldName, countyValue, districtFieldName, districtValue, zipFieldName, zipValue, origZipValue} = e;
120 | this.setState({
121 | [zipFieldName]: zipValue,
122 | [countyFieldName]: countyValue,
123 | [districtFieldName]: districtValue,
124 | handleZipCodeNotExists: e,
125 | handleZipCodeBlur: {},
126 | handleDistrictChange: {},
127 | handleCountyChange: {},
128 | handleZipCodeChange: {}
129 | });
130 |
131 | swal('郵遞區號不存在: ' + origZipValue, '', 'error');
132 | }
133 |
134 | render() {
135 | let countyRtn = JSON.stringify(this.state.handleCountyChange);
136 | let districtRtn = JSON.stringify(this.state.handleDistrictChange);
137 | let zipCodeRtn = JSON.stringify(this.state.handleZipCodeChange);
138 | let zipBlurRtn = JSON.stringify(this.state.handleZipCodeBlur);
139 | let zipNotExistsRtn = JSON.stringify(this.state.handleZipCodeNotExists);
140 | let fullAddress = this.state.countyValue1 + this.state.districtValue1
141 | + this.state.address;
142 | let addressShow = this.state.displayType === 'display' ? 'none' : 'inline';
143 | return (
144 |
145 |
158 |
159 | ZipCodeTW with Bootstrap CSS
160 |
161 | DisplayType: 'text'
162 |
163 |
182 |
183 |
193 |
194 |
195 |
196 |
197 |
198 |
218 |
219 |
220 |
221 |
DisplayType: 'display'
222 |
223 |
224 |
232 |
233 |
234 |
235 |
236 |
237 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
265 |
266 |
267 |
268 |
269 |
334 |
335 |
336 |
337 |
340 |
341 |
342 |
{districtRtn}
343 |
344 |
345 |
348 |
349 |
352 |
353 |
354 |
{zipNotExistsRtn}
355 |
356 |
357 |
358 |
359 | );
360 | }
361 | }
362 |
363 | window.app = {};
364 |
365 | app.create = (dom) => {
366 | ReactDOM.render(
367 | ,
368 | dom
369 | )
370 | };
--------------------------------------------------------------------------------