From e8635972c6fbb3ab3494bde9704527e6bd47ae7e Mon Sep 17 00:00:00 2001 From: Nikolay <46225163+vigdorov@users.noreply.github.com> Date: Mon, 28 Dec 2020 00:26:20 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BE=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20url=20=D1=81=D1=81=D1=8B=D0=BB=D0=BE?= =?UTF-8?q?=D0=BA=20=D0=B2=D0=BD=D1=83=D1=82=D1=80=D0=B8=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F,=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=84?= =?UTF-8?q?=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20triggerLink=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D0=BE=D0=B2?= =?UTF-8?q?=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 41 +++++++++++++---- package.json | 1 + src/app/components/top-menu/TopMenu.tsx | 5 +- src/core/utils/__test__/buildPath.test.ts | 56 +++++++++++++++++++++++ src/core/utils/buildPath.ts | 23 ++++++++++ src/core/utils/triggerLink.ts | 33 +++++++++++++ 6 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 src/core/utils/__test__/buildPath.test.ts create mode 100644 src/core/utils/buildPath.ts create mode 100644 src/core/utils/triggerLink.ts diff --git a/package-lock.json b/package-lock.json index e6c1419..2993e5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8290,16 +8290,11 @@ "dev": true }, "history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.0.0.tgz", + "integrity": "sha512-3NyRMKIiFSJmIPdq7FxkNMJkQ7ZEtVblOQ38VtKaA0zZMW1Eo6Q6W8oDKEflr1kNNTItSnk4JMCO1deeSgbLLg==", "requires": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" + "@babel/runtime": "^7.7.6" } }, "hmac-drbg": { @@ -15977,6 +15972,19 @@ "tiny-warning": "^1.0.0" }, "dependencies": { + "history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -15996,6 +16004,21 @@ "react-router": "5.2.0", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" + }, + "dependencies": { + "history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + } } }, "react-scripts": { diff --git a/package.json b/package.json index ee15850..aaaf122 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "axios": "^0.21.0", "date-fns": "^2.16.1", "fp-ts": "^2.8.5", + "history": "^5.0.0", "lodash": "^4.17.20", "react": "^17.0.1", "react-dom": "^17.0.1", diff --git a/src/app/components/top-menu/TopMenu.tsx b/src/app/components/top-menu/TopMenu.tsx index 63ccefd..e654647 100644 --- a/src/app/components/top-menu/TopMenu.tsx +++ b/src/app/components/top-menu/TopMenu.tsx @@ -10,7 +10,8 @@ import SearchIcon from '@material-ui/icons/Search'; import {Avatar} from '@material-ui/core'; import {useParams} from '_hooks/useParams'; import {PageType} from '_enums/common'; -import {PAGE_TITLE, ROUTES} from '_consts/common'; +import {PAGE_TITLE} from '_consts/common'; +import {buildPath} from '../../../core/utils/buildPath'; const NO_NAME_AVATAR = 'https://d.newsweek.com/en/full/425257/02-10-putin-economy.jpg'; @@ -33,7 +34,7 @@ const TopMenu: React.FC = () => { const history = useHistory(); const handleGoRoot = () => { - history.push(ROUTES.MAIN); + history.push(buildPath({pageType: PageType.Main})); }; const title = PAGE_TITLE[pageType]; diff --git a/src/core/utils/__test__/buildPath.test.ts b/src/core/utils/__test__/buildPath.test.ts new file mode 100644 index 0000000..0aae8db --- /dev/null +++ b/src/core/utils/__test__/buildPath.test.ts @@ -0,0 +1,56 @@ +import {PageType} from '../../enums/common'; +import {buildPath, BuildPathOptions} from '../buildPath'; + +describe('buildPath', () => { + it('Путь до страницы', () => { + const options: BuildPathOptions = { + pageType: PageType.Tags + }; + expect(buildPath(options)).toBe(`/${PageType.Tags}`); + }); + + it('Путь с route параметрами', () => { + const options: BuildPathOptions = { + pageType: PageType.Tags, + params: ['mode', '/id', 'fine'], + }; + expect(buildPath(options)).toBe(`/${PageType.Tags}/mode/id/fine`); + }); + + it('Путь c query', () => { + const options: BuildPathOptions = { + pageType: PageType.Tags, + query: { + foo: 'bar', + }, + }; + expect(buildPath(options)).toBe(`/${PageType.Tags}?foo=bar`); + }); + + it('Путь c query значениями undefined', () => { + const options: BuildPathOptions = { + pageType: PageType.Tags, + query: { + foo: undefined, + }, + }; + expect(buildPath(options)).toBe(`/${PageType.Tags}?foo=`); + }); + + it('Старые query должны быть затерты', () => { + const options: BuildPathOptions = { + pageType: PageType.Tags, + }; + history.pushState(null, '', '/name?like=true'); + expect(buildPath(options)).toBe(`/${PageType.Tags}`); + }); + + it('Старые query должны остаться в url', () => { + const options: BuildPathOptions = { + pageType: PageType.Tags, + withQuery: true, + }; + history.pushState(null, '', '/name?like=true'); + expect(buildPath(options)).toBe(`/${PageType.Tags}?like=true`); + }); +}); diff --git a/src/core/utils/buildPath.ts b/src/core/utils/buildPath.ts new file mode 100644 index 0000000..6ff2b57 --- /dev/null +++ b/src/core/utils/buildPath.ts @@ -0,0 +1,23 @@ +import {decode, ParsedUrlQueryInput, stringify} from 'querystring'; +import {PageType} from '../enums/common'; + +export type BuildPathOptions = { + pageType: PageType; + params?: Array; + query?: ParsedUrlQueryInput; + withQuery?: boolean, +}; + +const makePath = (params: Array) => params.map(param => { + return param.startsWith('/') ? param.slice(1) : param; +}).join('/'); + +export const buildPath = ({pageType, params, query, withQuery}: BuildPathOptions) => { + const path = makePath([pageType, ...(params ?? [])]); + const previousQuery = decode(location.search.slice(1)); + const stringifyQuery = stringify({ + ...(withQuery ? previousQuery : {}), + ...query, + }); + return [`/${path}`, stringifyQuery].filter(Boolean).join('?'); +}; diff --git a/src/core/utils/triggerLink.ts b/src/core/utils/triggerLink.ts new file mode 100644 index 0000000..ec80536 --- /dev/null +++ b/src/core/utils/triggerLink.ts @@ -0,0 +1,33 @@ +type Options = { + download?: boolean | string; + target?: '_self' | '_blank'; +}; + +const DEFAULT_OPTIONS = { + target: '_self', + download: false, +} as const; + +/** + * Использование этой функции требуется для открытия ссылок в новых + * вкладках из методов сервиса. Внутри компонентов его не используем. + */ +export const triggerLink = (link: string, options?: Options) => { + const finalOptions = { + ...DEFAULT_OPTIONS, + ...options + }; + + const a = document.createElement('a'); + a.href = link; + a.target = finalOptions.target; + + if (finalOptions.download === true) { + a.download = 'yes'; + } else if (typeof finalOptions.download === 'string') { + a.download = finalOptions.download; + } + + a.dispatchEvent(new MouseEvent('click')); + document.removeChild(a); +};