mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 17:40:23 +03:00
Merge pull request #505 from acelaya-forks/feature/resettable-title
Ensured short URL title can be resetted after creation
This commit is contained in:
commit
f19746cd58
4 changed files with 57 additions and 6 deletions
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
|
||||||
|
|
||||||
|
## [3.3.2] - 2021-10-17
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#503](https://github.com/shlinkio/shlink-web-client/issues/503) Fixed short URLs title not being resettable after creation.
|
||||||
|
|
||||||
|
|
||||||
## [3.3.1] - 2021-09-27
|
## [3.3.1] - 2021-09-27
|
||||||
### Added
|
### Added
|
||||||
* *Nothing*
|
* *Nothing*
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { InputType } from 'reactstrap/lib/Input';
|
import { InputType } from 'reactstrap/lib/Input';
|
||||||
import { Button, FormGroup, Input, Row } from 'reactstrap';
|
import { Button, FormGroup, Input, Row } from 'reactstrap';
|
||||||
import { isEmpty, pipe, replace, trim } from 'ramda';
|
import { cond, isEmpty, pipe, replace, trim, T } from 'ramda';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { parseISO } from 'date-fns';
|
import { parseISO } from 'date-fns';
|
||||||
import DateInput, { DateInputProps } from '../utils/DateInput';
|
import DateInput, { DateInputProps } from '../utils/DateInput';
|
||||||
import { supportsCrawlableVisits, supportsShortUrlTitle } from '../utils/helpers/features';
|
import { supportsCrawlableVisits, supportsShortUrlTitle } from '../utils/helpers/features';
|
||||||
import { SimpleCard } from '../utils/SimpleCard';
|
import { SimpleCard } from '../utils/SimpleCard';
|
||||||
import { handleEventPreventingDefault, hasValue } from '../utils/utils';
|
import { handleEventPreventingDefault, hasValue, OptionalString } from '../utils/utils';
|
||||||
import Checkbox from '../utils/Checkbox';
|
import Checkbox from '../utils/Checkbox';
|
||||||
import { SelectedServer } from '../servers/data';
|
import { SelectedServer } from '../servers/data';
|
||||||
import { TagsSelectorProps } from '../tags/helpers/TagsSelector';
|
import { TagsSelectorProps } from '../tags/helpers/TagsSelector';
|
||||||
|
@ -40,14 +40,25 @@ export const ShortUrlForm = (
|
||||||
): FC<ShortUrlFormProps> => ({ mode, saving, onSave, initialState, selectedServer }) => {
|
): FC<ShortUrlFormProps> => ({ mode, saving, onSave, initialState, selectedServer }) => {
|
||||||
const [ shortUrlData, setShortUrlData ] = useState(initialState);
|
const [ shortUrlData, setShortUrlData ] = useState(initialState);
|
||||||
const isEdit = mode === 'edit';
|
const isEdit = mode === 'edit';
|
||||||
|
const hadTitleOriginally = hasValue(initialState.title);
|
||||||
const changeTags = (tags: string[]) => setShortUrlData({ ...shortUrlData, tags: tags.map(normalizeTag) });
|
const changeTags = (tags: string[]) => setShortUrlData({ ...shortUrlData, tags: tags.map(normalizeTag) });
|
||||||
const reset = () => setShortUrlData(initialState);
|
const reset = () => setShortUrlData(initialState);
|
||||||
|
const resolveNewTitle = (): OptionalString => {
|
||||||
|
const hasNewTitle = hasValue(shortUrlData.title);
|
||||||
|
const matcher = cond<never, OptionalString>([
|
||||||
|
[ () => !hasNewTitle && !hadTitleOriginally, () => undefined ],
|
||||||
|
[ () => !hasNewTitle && hadTitleOriginally, () => null ],
|
||||||
|
[ T, () => shortUrlData.title ],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return matcher();
|
||||||
|
};
|
||||||
const submit = handleEventPreventingDefault(async () => onSave({
|
const submit = handleEventPreventingDefault(async () => onSave({
|
||||||
...shortUrlData,
|
...shortUrlData,
|
||||||
validSince: formatIsoDate(shortUrlData.validSince) ?? null,
|
validSince: formatIsoDate(shortUrlData.validSince) ?? null,
|
||||||
validUntil: formatIsoDate(shortUrlData.validUntil) ?? null,
|
validUntil: formatIsoDate(shortUrlData.validUntil) ?? null,
|
||||||
maxVisits: !hasValue(shortUrlData.maxVisits) ? null : Number(shortUrlData.maxVisits),
|
maxVisits: !hasValue(shortUrlData.maxVisits) ? null : Number(shortUrlData.maxVisits),
|
||||||
title: !hasValue(shortUrlData.title) ? undefined : shortUrlData.title,
|
title: resolveNewTitle(),
|
||||||
}).then(() => !isEdit && reset()).catch(() => {}));
|
}).then(() => !isEdit && reset()).catch(() => {}));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Nullable, OptionalString } from '../../utils/utils';
|
||||||
export interface EditShortUrlData {
|
export interface EditShortUrlData {
|
||||||
longUrl?: string;
|
longUrl?: string;
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
title?: string;
|
title?: string | null;
|
||||||
validSince?: Date | string | null;
|
validSince?: Date | string | null;
|
||||||
validUntil?: Date | string | null;
|
validUntil?: Date | string | null;
|
||||||
maxVisits?: number | null;
|
maxVisits?: number | null;
|
||||||
|
|
|
@ -9,13 +9,14 @@ import { ShortUrlData } from '../../src/short-urls/data';
|
||||||
import { ReachableServer, SelectedServer } from '../../src/servers/data';
|
import { ReachableServer, SelectedServer } from '../../src/servers/data';
|
||||||
import { SimpleCard } from '../../src/utils/SimpleCard';
|
import { SimpleCard } from '../../src/utils/SimpleCard';
|
||||||
import { parseDate } from '../../src/utils/helpers/date';
|
import { parseDate } from '../../src/utils/helpers/date';
|
||||||
|
import { OptionalString } from '../../src/utils/utils';
|
||||||
|
|
||||||
describe('<ShortUrlForm />', () => {
|
describe('<ShortUrlForm />', () => {
|
||||||
let wrapper: ShallowWrapper;
|
let wrapper: ShallowWrapper;
|
||||||
const TagsSelector = () => null;
|
const TagsSelector = () => null;
|
||||||
const DomainSelector = () => null;
|
const DomainSelector = () => null;
|
||||||
const createShortUrl = jest.fn(async () => Promise.resolve());
|
const createShortUrl = jest.fn(async () => Promise.resolve());
|
||||||
const createWrapper = (selectedServer: SelectedServer = null, mode: Mode = 'create') => {
|
const createWrapper = (selectedServer: SelectedServer = null, mode: Mode = 'create', title?: OptionalString) => {
|
||||||
const ShortUrlForm = createShortUrlForm(TagsSelector, DomainSelector);
|
const ShortUrlForm = createShortUrlForm(TagsSelector, DomainSelector);
|
||||||
|
|
||||||
wrapper = shallow(
|
wrapper = shallow(
|
||||||
|
@ -23,7 +24,7 @@ describe('<ShortUrlForm />', () => {
|
||||||
selectedServer={selectedServer}
|
selectedServer={selectedServer}
|
||||||
mode={mode}
|
mode={mode}
|
||||||
saving={false}
|
saving={false}
|
||||||
initialState={Mock.of<ShortUrlData>({ validateUrl: true, findIfExists: false })}
|
initialState={Mock.of<ShortUrlData>({ validateUrl: true, findIfExists: false, title })}
|
||||||
onSave={createShortUrl}
|
onSave={createShortUrl}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
@ -80,4 +81,26 @@ describe('<ShortUrlForm />', () => {
|
||||||
expect(cards).toHaveLength(expectedAmountOfCards);
|
expect(cards).toHaveLength(expectedAmountOfCards);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[ null, 'new title', 'new title' ],
|
||||||
|
[ undefined, 'new title', 'new title' ],
|
||||||
|
[ '', 'new title', 'new title' ],
|
||||||
|
[ null, '', undefined ],
|
||||||
|
[ null, null, undefined ],
|
||||||
|
[ '', '', undefined ],
|
||||||
|
[ undefined, undefined, undefined ],
|
||||||
|
[ 'old title', null, null ],
|
||||||
|
[ 'old title', undefined, null ],
|
||||||
|
[ 'old title', '', null ],
|
||||||
|
])('sends expected title based on original and new values', (originalTitle, newTitle, expectedSentTitle) => {
|
||||||
|
const wrapper = createWrapper(Mock.of<ReachableServer>({ version: '2.6.0' }), 'create', originalTitle);
|
||||||
|
|
||||||
|
wrapper.find('#title').simulate('change', { target: { value: newTitle } });
|
||||||
|
wrapper.find('form').simulate('submit', { preventDefault: identity });
|
||||||
|
|
||||||
|
expect(createShortUrl).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
title: expectedSentTitle,
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue