mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 17:40:23 +03:00
Updated ShortUrlForm to ensure it does not render empty cards
This commit is contained in:
parent
6628a4059e
commit
56aab349db
4 changed files with 118 additions and 78 deletions
|
@ -3,25 +3,27 @@ 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 { isEmpty, pipe, replace, trim } from 'ramda';
|
||||||
import m from 'moment';
|
import m from 'moment';
|
||||||
|
import classNames from 'classnames';
|
||||||
import DateInput, { DateInputProps } from '../utils/DateInput';
|
import DateInput, { DateInputProps } from '../utils/DateInput';
|
||||||
import {
|
import {
|
||||||
supportsListingDomains,
|
supportsListingDomains,
|
||||||
supportsSettingShortCodeLength,
|
supportsSettingShortCodeLength,
|
||||||
supportsShortUrlTitle,
|
supportsShortUrlTitle,
|
||||||
|
supportsValidateUrl,
|
||||||
} from '../utils/helpers/features';
|
} from '../utils/helpers/features';
|
||||||
import { SimpleCard } from '../utils/SimpleCard';
|
import { SimpleCard } from '../utils/SimpleCard';
|
||||||
import { handleEventPreventingDefault, hasValue } from '../utils/utils';
|
import { handleEventPreventingDefault, hasValue } 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';
|
||||||
import { Versions } from '../utils/helpers/version';
|
|
||||||
import { DomainSelectorProps } from '../domains/DomainSelector';
|
import { DomainSelectorProps } from '../domains/DomainSelector';
|
||||||
import { formatIsoDate } from '../utils/helpers/date';
|
import { formatIsoDate } from '../utils/helpers/date';
|
||||||
import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon';
|
import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon';
|
||||||
import { ShortUrlData } from './data';
|
import { ShortUrlData } from './data';
|
||||||
import './ShortUrlForm.scss';
|
import './ShortUrlForm.scss';
|
||||||
|
|
||||||
type Mode = 'create' | 'create-basic' | 'edit';
|
export type Mode = 'create' | 'create-basic' | 'edit';
|
||||||
|
|
||||||
type DateFields = 'validSince' | 'validUntil';
|
type DateFields = 'validSince' | 'validUntil';
|
||||||
type NonDateFields = 'longUrl' | 'customSlug' | 'shortCodeLength' | 'domain' | 'maxVisits' | 'title';
|
type NonDateFields = 'longUrl' | 'customSlug' | 'shortCodeLength' | 'domain' | 'maxVisits' | 'title';
|
||||||
|
|
||||||
|
@ -37,7 +39,6 @@ const normalizeTag = pipe(trim, replace(/ /g, '-'));
|
||||||
|
|
||||||
export const ShortUrlForm = (
|
export const ShortUrlForm = (
|
||||||
TagsSelector: FC<TagsSelectorProps>,
|
TagsSelector: FC<TagsSelectorProps>,
|
||||||
ForServerVersion: FC<Versions>,
|
|
||||||
DomainSelector: FC<DomainSelectorProps>,
|
DomainSelector: FC<DomainSelectorProps>,
|
||||||
): FC<ShortUrlFormProps> => ({ mode, saving, onSave, initialState, selectedServer }) => { // eslint-disable-line complexity
|
): FC<ShortUrlFormProps> => ({ mode, saving, onSave, initialState, selectedServer }) => { // eslint-disable-line complexity
|
||||||
const [ shortUrlData, setShortUrlData ] = useState(initialState);
|
const [ shortUrlData, setShortUrlData ] = useState(initialState);
|
||||||
|
@ -101,6 +102,13 @@ export const ShortUrlForm = (
|
||||||
const showDomainSelector = supportsListingDomains(selectedServer);
|
const showDomainSelector = supportsListingDomains(selectedServer);
|
||||||
const disableShortCodeLength = !supportsSettingShortCodeLength(selectedServer);
|
const disableShortCodeLength = !supportsSettingShortCodeLength(selectedServer);
|
||||||
const supportsTitle = supportsShortUrlTitle(selectedServer);
|
const supportsTitle = supportsShortUrlTitle(selectedServer);
|
||||||
|
const showCustomizeCard = supportsTitle || !isEdit;
|
||||||
|
const limitAccessCardClasses = classNames('mb-3', {
|
||||||
|
'col-sm-6': showCustomizeCard,
|
||||||
|
'col-sm-12': !showCustomizeCard,
|
||||||
|
});
|
||||||
|
const showValidateUrl = supportsValidateUrl(selectedServer);
|
||||||
|
const showExtraValidationsCard = showValidateUrl || !isEdit;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="short-url-form" onSubmit={submit}>
|
<form className="short-url-form" onSubmit={submit}>
|
||||||
|
@ -112,6 +120,7 @@ export const ShortUrlForm = (
|
||||||
</SimpleCard>
|
</SimpleCard>
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
|
{showCustomizeCard && (
|
||||||
<div className="col-sm-6 mb-3">
|
<div className="col-sm-6 mb-3">
|
||||||
<SimpleCard title="Customize the short URL">
|
<SimpleCard title="Customize the short URL">
|
||||||
{supportsTitle && renderOptionalInput('title', 'Title')}
|
{supportsTitle && renderOptionalInput('title', 'Title')}
|
||||||
|
@ -146,8 +155,9 @@ export const ShortUrlForm = (
|
||||||
)}
|
)}
|
||||||
</SimpleCard>
|
</SimpleCard>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="col-sm-6 mb-3">
|
<div className={limitAccessCardClasses}>
|
||||||
<SimpleCard title="Limit access to the short URL">
|
<SimpleCard title="Limit access to the short URL">
|
||||||
{renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })}
|
{renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })}
|
||||||
{renderDateInput('validSince', 'Enabled since...', { maxDate: shortUrlData.validUntil ? m(shortUrlData.validUntil) : undefined })}
|
{renderDateInput('validSince', 'Enabled since...', { maxDate: shortUrlData.validUntil ? m(shortUrlData.validUntil) : undefined })}
|
||||||
|
@ -156,6 +166,7 @@ export const ShortUrlForm = (
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
|
{showExtraValidationsCard && (
|
||||||
<SimpleCard title="Extra validations" className="mb-3">
|
<SimpleCard title="Extra validations" className="mb-3">
|
||||||
{!isEdit && (
|
{!isEdit && (
|
||||||
<p>
|
<p>
|
||||||
|
@ -163,7 +174,7 @@ export const ShortUrlForm = (
|
||||||
provided data.
|
provided data.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<ForServerVersion minVersion="2.4.0">
|
{showValidateUrl && (
|
||||||
<p>
|
<p>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
inline
|
inline
|
||||||
|
@ -173,7 +184,7 @@ export const ShortUrlForm = (
|
||||||
Validate URL
|
Validate URL
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</p>
|
</p>
|
||||||
</ForServerVersion>
|
)}
|
||||||
{!isEdit && (
|
{!isEdit && (
|
||||||
<p>
|
<p>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
@ -188,6 +199,7 @@ export const ShortUrlForm = (
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</SimpleCard>
|
</SimpleCard>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||||
bottle.serviceFactory('ShortUrlsRow', ShortUrlsRow, 'ShortUrlsRowMenu', 'ColorGenerator', 'useStateFlagTimeout');
|
bottle.serviceFactory('ShortUrlsRow', ShortUrlsRow, 'ShortUrlsRowMenu', 'ColorGenerator', 'useStateFlagTimeout');
|
||||||
bottle.serviceFactory('ShortUrlsRowMenu', ShortUrlsRowMenu, 'DeleteShortUrlModal', 'QrCodeModal');
|
bottle.serviceFactory('ShortUrlsRowMenu', ShortUrlsRowMenu, 'DeleteShortUrlModal', 'QrCodeModal');
|
||||||
bottle.serviceFactory('CreateShortUrlResult', CreateShortUrlResult, 'useStateFlagTimeout');
|
bottle.serviceFactory('CreateShortUrlResult', CreateShortUrlResult, 'useStateFlagTimeout');
|
||||||
bottle.serviceFactory('ShortUrlForm', ShortUrlForm, 'TagsSelector', 'ForServerVersion', 'DomainSelector');
|
bottle.serviceFactory('ShortUrlForm', ShortUrlForm, 'TagsSelector', 'DomainSelector');
|
||||||
|
|
||||||
bottle.serviceFactory('CreateShortUrl', CreateShortUrl, 'ShortUrlForm', 'CreateShortUrlResult');
|
bottle.serviceFactory('CreateShortUrl', CreateShortUrl, 'ShortUrlForm', 'CreateShortUrlResult');
|
||||||
bottle.decorator(
|
bottle.decorator(
|
||||||
|
|
|
@ -12,6 +12,8 @@ export const supportsListingDomains = serverMatchesVersions({ minVersion: '2.4.0
|
||||||
|
|
||||||
export const supportsQrCodeSvgFormat = supportsListingDomains;
|
export const supportsQrCodeSvgFormat = supportsListingDomains;
|
||||||
|
|
||||||
|
export const supportsValidateUrl = supportsListingDomains;
|
||||||
|
|
||||||
export const supportsQrCodeSizeInQuery = serverMatchesVersions({ minVersion: '2.5.0' });
|
export const supportsQrCodeSizeInQuery = serverMatchesVersions({ minVersion: '2.5.0' });
|
||||||
|
|
||||||
export const supportsShortUrlTitle = serverMatchesVersions({ minVersion: '2.6.0' });
|
export const supportsShortUrlTitle = serverMatchesVersions({ minVersion: '2.6.0' });
|
||||||
|
|
|
@ -3,32 +3,37 @@ import moment from 'moment';
|
||||||
import { identity } from 'ramda';
|
import { identity } from 'ramda';
|
||||||
import { Mock } from 'ts-mockery';
|
import { Mock } from 'ts-mockery';
|
||||||
import { Input } from 'reactstrap';
|
import { Input } from 'reactstrap';
|
||||||
import { ShortUrlForm as createShortUrlForm } from '../../src/short-urls/ShortUrlForm';
|
import { ShortUrlForm as createShortUrlForm, Mode } from '../../src/short-urls/ShortUrlForm';
|
||||||
import DateInput from '../../src/utils/DateInput';
|
import DateInput from '../../src/utils/DateInput';
|
||||||
import { ShortUrlData } from '../../src/short-urls/data';
|
import { ShortUrlData } from '../../src/short-urls/data';
|
||||||
|
import { ReachableServer, SelectedServer } from '../../src/servers/data';
|
||||||
|
import { SimpleCard } from '../../src/utils/SimpleCard';
|
||||||
|
|
||||||
describe('<ShortUrlForm />', () => {
|
describe('<ShortUrlForm />', () => {
|
||||||
let wrapper: ShallowWrapper;
|
let wrapper: ShallowWrapper;
|
||||||
const TagsSelector = () => null;
|
const TagsSelector = () => null;
|
||||||
const createShortUrl = jest.fn();
|
const createShortUrl = jest.fn();
|
||||||
|
const createWrapper = (selectedServer: SelectedServer = null, mode: Mode = 'create') => {
|
||||||
beforeEach(() => {
|
const ShortUrlForm = createShortUrlForm(TagsSelector, () => null);
|
||||||
const ShortUrlForm = createShortUrlForm(TagsSelector, () => null, () => null);
|
|
||||||
|
|
||||||
wrapper = shallow(
|
wrapper = shallow(
|
||||||
<ShortUrlForm
|
<ShortUrlForm
|
||||||
selectedServer={null}
|
selectedServer={selectedServer}
|
||||||
mode="create"
|
mode={mode}
|
||||||
saving={false}
|
saving={false}
|
||||||
initialState={Mock.of<ShortUrlData>({ validateUrl: true, findIfExists: false })}
|
initialState={Mock.of<ShortUrlData>({ validateUrl: true, findIfExists: false })}
|
||||||
onSave={createShortUrl}
|
onSave={createShortUrl}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
return wrapper;
|
||||||
|
};
|
||||||
|
|
||||||
afterEach(() => wrapper.unmount());
|
afterEach(() => wrapper.unmount());
|
||||||
afterEach(jest.clearAllMocks);
|
afterEach(jest.clearAllMocks);
|
||||||
|
|
||||||
it('saves short URL with data set in form controls', () => {
|
it('saves short URL with data set in form controls', () => {
|
||||||
|
const wrapper = createWrapper();
|
||||||
const validSince = moment('2017-01-01');
|
const validSince = moment('2017-01-01');
|
||||||
const validUntil = moment('2017-01-06');
|
const validUntil = moment('2017-01-06');
|
||||||
|
|
||||||
|
@ -56,4 +61,25 @@ describe('<ShortUrlForm />', () => {
|
||||||
validateUrl: true,
|
validateUrl: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[ null, 'create' as Mode, 4 ],
|
||||||
|
[ null, 'create-basic' as Mode, 0 ],
|
||||||
|
[ Mock.of<ReachableServer>({ version: '2.6.0' }), 'create' as Mode, 4 ],
|
||||||
|
[ Mock.of<ReachableServer>({ version: '2.5.0' }), 'create' as Mode, 4 ],
|
||||||
|
[ Mock.of<ReachableServer>({ version: '2.4.0' }), 'create' as Mode, 4 ],
|
||||||
|
[ Mock.of<ReachableServer>({ version: '2.3.0' }), 'create' as Mode, 4 ],
|
||||||
|
[ Mock.of<ReachableServer>({ version: '2.6.0' }), 'edit' as Mode, 4 ],
|
||||||
|
[ Mock.of<ReachableServer>({ version: '2.5.0' }), 'edit' as Mode, 3 ],
|
||||||
|
[ Mock.of<ReachableServer>({ version: '2.4.0' }), 'edit' as Mode, 3 ],
|
||||||
|
[ Mock.of<ReachableServer>({ version: '2.3.0' }), 'edit' as Mode, 2 ],
|
||||||
|
])(
|
||||||
|
'renders expected amount of cards based on server capabilities and mode',
|
||||||
|
(selectedServer, mode, expectedAmountOfCards) => {
|
||||||
|
const wrapper = createWrapper(selectedServer, mode);
|
||||||
|
const cards = wrapper.find(SimpleCard);
|
||||||
|
|
||||||
|
expect(cards).toHaveLength(expectedAmountOfCards);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue