mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +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).
|
||||
|
||||
## [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
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { FC, useEffect, useState } from 'react';
|
||||
import { InputType } from 'reactstrap/lib/Input';
|
||||
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 { parseISO } from 'date-fns';
|
||||
import DateInput, { DateInputProps } from '../utils/DateInput';
|
||||
import { supportsCrawlableVisits, supportsShortUrlTitle } from '../utils/helpers/features';
|
||||
import { SimpleCard } from '../utils/SimpleCard';
|
||||
import { handleEventPreventingDefault, hasValue } from '../utils/utils';
|
||||
import { handleEventPreventingDefault, hasValue, OptionalString } from '../utils/utils';
|
||||
import Checkbox from '../utils/Checkbox';
|
||||
import { SelectedServer } from '../servers/data';
|
||||
import { TagsSelectorProps } from '../tags/helpers/TagsSelector';
|
||||
|
@ -40,14 +40,25 @@ export const ShortUrlForm = (
|
|||
): FC<ShortUrlFormProps> => ({ mode, saving, onSave, initialState, selectedServer }) => {
|
||||
const [ shortUrlData, setShortUrlData ] = useState(initialState);
|
||||
const isEdit = mode === 'edit';
|
||||
const hadTitleOriginally = hasValue(initialState.title);
|
||||
const changeTags = (tags: string[]) => setShortUrlData({ ...shortUrlData, tags: tags.map(normalizeTag) });
|
||||
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({
|
||||
...shortUrlData,
|
||||
validSince: formatIsoDate(shortUrlData.validSince) ?? null,
|
||||
validUntil: formatIsoDate(shortUrlData.validUntil) ?? null,
|
||||
maxVisits: !hasValue(shortUrlData.maxVisits) ? null : Number(shortUrlData.maxVisits),
|
||||
title: !hasValue(shortUrlData.title) ? undefined : shortUrlData.title,
|
||||
title: resolveNewTitle(),
|
||||
}).then(() => !isEdit && reset()).catch(() => {}));
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Nullable, OptionalString } from '../../utils/utils';
|
|||
export interface EditShortUrlData {
|
||||
longUrl?: string;
|
||||
tags?: string[];
|
||||
title?: string;
|
||||
title?: string | null;
|
||||
validSince?: Date | string | null;
|
||||
validUntil?: Date | string | null;
|
||||
maxVisits?: number | null;
|
||||
|
|
|
@ -9,13 +9,14 @@ import { ShortUrlData } from '../../src/short-urls/data';
|
|||
import { ReachableServer, SelectedServer } from '../../src/servers/data';
|
||||
import { SimpleCard } from '../../src/utils/SimpleCard';
|
||||
import { parseDate } from '../../src/utils/helpers/date';
|
||||
import { OptionalString } from '../../src/utils/utils';
|
||||
|
||||
describe('<ShortUrlForm />', () => {
|
||||
let wrapper: ShallowWrapper;
|
||||
const TagsSelector = () => null;
|
||||
const DomainSelector = () => null;
|
||||
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);
|
||||
|
||||
wrapper = shallow(
|
||||
|
@ -23,7 +24,7 @@ describe('<ShortUrlForm />', () => {
|
|||
selectedServer={selectedServer}
|
||||
mode={mode}
|
||||
saving={false}
|
||||
initialState={Mock.of<ShortUrlData>({ validateUrl: true, findIfExists: false })}
|
||||
initialState={Mock.of<ShortUrlData>({ validateUrl: true, findIfExists: false, title })}
|
||||
onSave={createShortUrl}
|
||||
/>,
|
||||
);
|
||||
|
@ -80,4 +81,26 @@ describe('<ShortUrlForm />', () => {
|
|||
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