diff --git a/src/short-urls/ShortUrlForm.tsx b/src/short-urls/ShortUrlForm.tsx index 1d51e91f..51714bbc 100644 --- a/src/short-urls/ShortUrlForm.tsx +++ b/src/short-urls/ShortUrlForm.tsx @@ -3,25 +3,27 @@ import { InputType } from 'reactstrap/lib/Input'; import { Button, FormGroup, Input, Row } from 'reactstrap'; import { isEmpty, pipe, replace, trim } from 'ramda'; import m from 'moment'; +import classNames from 'classnames'; import DateInput, { DateInputProps } from '../utils/DateInput'; import { supportsListingDomains, supportsSettingShortCodeLength, supportsShortUrlTitle, + supportsValidateUrl, } from '../utils/helpers/features'; import { SimpleCard } from '../utils/SimpleCard'; import { handleEventPreventingDefault, hasValue } from '../utils/utils'; import Checkbox from '../utils/Checkbox'; import { SelectedServer } from '../servers/data'; import { TagsSelectorProps } from '../tags/helpers/TagsSelector'; -import { Versions } from '../utils/helpers/version'; import { DomainSelectorProps } from '../domains/DomainSelector'; import { formatIsoDate } from '../utils/helpers/date'; import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon'; import { ShortUrlData } from './data'; import './ShortUrlForm.scss'; -type Mode = 'create' | 'create-basic' | 'edit'; +export type Mode = 'create' | 'create-basic' | 'edit'; + type DateFields = 'validSince' | 'validUntil'; type NonDateFields = 'longUrl' | 'customSlug' | 'shortCodeLength' | 'domain' | 'maxVisits' | 'title'; @@ -37,7 +39,6 @@ const normalizeTag = pipe(trim, replace(/ /g, '-')); export const ShortUrlForm = ( TagsSelector: FC, - ForServerVersion: FC, DomainSelector: FC, ): FC => ({ mode, saving, onSave, initialState, selectedServer }) => { // eslint-disable-line complexity const [ shortUrlData, setShortUrlData ] = useState(initialState); @@ -101,6 +102,13 @@ export const ShortUrlForm = ( const showDomainSelector = supportsListingDomains(selectedServer); const disableShortCodeLength = !supportsSettingShortCodeLength(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 (
@@ -112,42 +120,44 @@ export const ShortUrlForm = ( -
- - {supportsTitle && renderOptionalInput('title', 'Title')} - {!isEdit && ( - <> - -
- {renderOptionalInput('customSlug', 'Custom slug', 'text', { - disabled: hasValue(shortUrlData.shortCodeLength), - })} -
-
- {renderOptionalInput('shortCodeLength', 'Short code length', 'number', { - min: 4, - disabled: disableShortCodeLength || hasValue(shortUrlData.customSlug), - ...disableShortCodeLength && { - title: 'Shlink 2.1.0 or higher is required to be able to provide the short code length', - }, - })} -
-
- {!showDomainSelector && renderOptionalInput('domain', 'Domain', 'text')} - {showDomainSelector && ( - - setShortUrlData({ ...shortUrlData, domain })} - /> - - )} - - )} -
-
+ {showCustomizeCard && ( +
+ + {supportsTitle && renderOptionalInput('title', 'Title')} + {!isEdit && ( + <> + +
+ {renderOptionalInput('customSlug', 'Custom slug', 'text', { + disabled: hasValue(shortUrlData.shortCodeLength), + })} +
+
+ {renderOptionalInput('shortCodeLength', 'Short code length', 'number', { + min: 4, + disabled: disableShortCodeLength || hasValue(shortUrlData.customSlug), + ...disableShortCodeLength && { + title: 'Shlink 2.1.0 or higher is required to be able to provide the short code length', + }, + })} +
+
+ {!showDomainSelector && renderOptionalInput('domain', 'Domain', 'text')} + {showDomainSelector && ( + + setShortUrlData({ ...shortUrlData, domain })} + /> + + )} + + )} +
+
+ )} -
+
{renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })} {renderDateInput('validSince', 'Enabled since...', { maxDate: shortUrlData.validUntil ? m(shortUrlData.validUntil) : undefined })} @@ -156,38 +166,40 @@ export const ShortUrlForm = (
- - {!isEdit && ( -

- Make sure the long URL is valid, or ensure an existing short URL is returned if it matches all - provided data. -

- )} - -

- setShortUrlData({ ...shortUrlData, validateUrl })} - > - Validate URL - -

-
- {!isEdit && ( -

- setShortUrlData({ ...shortUrlData, findIfExists })} - > - Use existing URL if found - - -

- )} -
+ {showExtraValidationsCard && ( + + {!isEdit && ( +

+ Make sure the long URL is valid, or ensure an existing short URL is returned if it matches all + provided data. +

+ )} + {showValidateUrl && ( +

+ setShortUrlData({ ...shortUrlData, validateUrl })} + > + Validate URL + +

+ )} + {!isEdit && ( +

+ setShortUrlData({ ...shortUrlData, findIfExists })} + > + Use existing URL if found + + +

+ )} +
+ )} )} diff --git a/src/short-urls/services/provideServices.ts b/src/short-urls/services/provideServices.ts index 9caa6d36..49dee640 100644 --- a/src/short-urls/services/provideServices.ts +++ b/src/short-urls/services/provideServices.ts @@ -34,7 +34,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.serviceFactory('ShortUrlsRow', ShortUrlsRow, 'ShortUrlsRowMenu', 'ColorGenerator', 'useStateFlagTimeout'); bottle.serviceFactory('ShortUrlsRowMenu', ShortUrlsRowMenu, 'DeleteShortUrlModal', 'QrCodeModal'); 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.decorator( diff --git a/src/utils/helpers/features.ts b/src/utils/helpers/features.ts index 44279433..2caecc75 100644 --- a/src/utils/helpers/features.ts +++ b/src/utils/helpers/features.ts @@ -12,6 +12,8 @@ export const supportsListingDomains = serverMatchesVersions({ minVersion: '2.4.0 export const supportsQrCodeSvgFormat = supportsListingDomains; +export const supportsValidateUrl = supportsListingDomains; + export const supportsQrCodeSizeInQuery = serverMatchesVersions({ minVersion: '2.5.0' }); export const supportsShortUrlTitle = serverMatchesVersions({ minVersion: '2.6.0' }); diff --git a/test/short-urls/ShortUrlForm.test.tsx b/test/short-urls/ShortUrlForm.test.tsx index c5f13ece..b5b9dc6d 100644 --- a/test/short-urls/ShortUrlForm.test.tsx +++ b/test/short-urls/ShortUrlForm.test.tsx @@ -3,32 +3,37 @@ import moment from 'moment'; import { identity } from 'ramda'; import { Mock } from 'ts-mockery'; 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 { ShortUrlData } from '../../src/short-urls/data'; +import { ReachableServer, SelectedServer } from '../../src/servers/data'; +import { SimpleCard } from '../../src/utils/SimpleCard'; describe('', () => { let wrapper: ShallowWrapper; const TagsSelector = () => null; const createShortUrl = jest.fn(); - - beforeEach(() => { - const ShortUrlForm = createShortUrlForm(TagsSelector, () => null, () => null); + const createWrapper = (selectedServer: SelectedServer = null, mode: Mode = 'create') => { + const ShortUrlForm = createShortUrlForm(TagsSelector, () => null); wrapper = shallow( ({ validateUrl: true, findIfExists: false })} onSave={createShortUrl} />, ); - }); + + return wrapper; + }; + afterEach(() => wrapper.unmount()); afterEach(jest.clearAllMocks); it('saves short URL with data set in form controls', () => { + const wrapper = createWrapper(); const validSince = moment('2017-01-01'); const validUntil = moment('2017-01-06'); @@ -56,4 +61,25 @@ describe('', () => { validateUrl: true, }); }); + + it.each([ + [ null, 'create' as Mode, 4 ], + [ null, 'create-basic' as Mode, 0 ], + [ Mock.of({ version: '2.6.0' }), 'create' as Mode, 4 ], + [ Mock.of({ version: '2.5.0' }), 'create' as Mode, 4 ], + [ Mock.of({ version: '2.4.0' }), 'create' as Mode, 4 ], + [ Mock.of({ version: '2.3.0' }), 'create' as Mode, 4 ], + [ Mock.of({ version: '2.6.0' }), 'edit' as Mode, 4 ], + [ Mock.of({ version: '2.5.0' }), 'edit' as Mode, 3 ], + [ Mock.of({ version: '2.4.0' }), 'edit' as Mode, 3 ], + [ Mock.of({ 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); + }, + ); });