mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2026-03-13 11:03:50 +00:00
Merge pull request #751 from acelaya-forks/feature/fix-max-length
Feature/fix max length
This commit is contained in:
@@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
* [#729](https://github.com/shlinkio/shlink-web-client/issues/729) Fixed wrong stats displayed in tags after renaming.
|
* [#729](https://github.com/shlinkio/shlink-web-client/issues/729) Fixed wrong stats displayed in tags after renaming.
|
||||||
* [#737](https://github.com/shlinkio/shlink-web-client/issues/737) Fixed incorrect contrast in warning messages when using dark theme.
|
* [#737](https://github.com/shlinkio/shlink-web-client/issues/737) Fixed incorrect contrast in warning messages when using dark theme.
|
||||||
* [#726](https://github.com/shlinkio/shlink-web-client/issues/726) Fixed delete server and delete short URL modals getting removed from the DOM before finishing close transition.
|
* [#726](https://github.com/shlinkio/shlink-web-client/issues/726) Fixed delete server and delete short URL modals getting removed from the DOM before finishing close transition.
|
||||||
|
* [#749](https://github.com/shlinkio/shlink-web-client/issues/749) Fixed broken short URLs table when some short URL has a too long custom slug.
|
||||||
|
|
||||||
|
|
||||||
## [3.7.3] - 2022-09-13
|
## [3.7.3] - 2022-09-13
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
@import './utils/base';
|
@import './utils/base';
|
||||||
@import 'node_modules/bootstrap/scss/bootstrap.scss';
|
@import 'node_modules/bootstrap/scss/bootstrap.scss';
|
||||||
@import './common/react-tag-autocomplete.scss';
|
@import './common/react-tag-autocomplete.scss';
|
||||||
@import 'utils/theme/theme';
|
@import './utils/theme/theme';
|
||||||
|
@import './utils/mixins/text-ellipsis';
|
||||||
@import './utils/table/ResponsiveTable';
|
@import './utils/table/ResponsiveTable';
|
||||||
@import './utils/StickyCardPaginator';
|
@import './utils/StickyCardPaginator';
|
||||||
|
|
||||||
@@ -222,9 +223,7 @@ hr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text-ellipsis {
|
.text-ellipsis {
|
||||||
text-overflow: ellipsis;
|
@include text-ellipsis();
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ interface DeleteShortUrlModalConnectProps extends ShortUrlModalProps {
|
|||||||
resetDeleteShortUrl: () => void;
|
resetDeleteShortUrl: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DELETION_PATTERN = 'delete';
|
||||||
|
|
||||||
export const DeleteShortUrlModal = ({
|
export const DeleteShortUrlModal = ({
|
||||||
shortUrl,
|
shortUrl,
|
||||||
toggle,
|
toggle,
|
||||||
@@ -41,12 +43,12 @@ export const DeleteShortUrlModal = ({
|
|||||||
<ModalBody>
|
<ModalBody>
|
||||||
<p><b className="text-danger">Caution!</b> You are about to delete a short URL.</p>
|
<p><b className="text-danger">Caution!</b> You are about to delete a short URL.</p>
|
||||||
<p>This action cannot be undone. Once you have deleted it, all the visits stats will be lost.</p>
|
<p>This action cannot be undone. Once you have deleted it, all the visits stats will be lost.</p>
|
||||||
<p>Write <b>{shortUrl.shortCode}</b> to confirm deletion.</p>
|
<p>Write <b>{DELETION_PATTERN}</b> to confirm deletion.</p>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
placeholder={`Insert the short code (${shortUrl.shortCode})`}
|
placeholder={`Insert ${DELETION_PATTERN}`}
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={(e) => setInputValue(e.target.value)}
|
onChange={(e) => setInputValue(e.target.value)}
|
||||||
/>
|
/>
|
||||||
@@ -62,7 +64,7 @@ export const DeleteShortUrlModal = ({
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-danger"
|
className="btn btn-danger"
|
||||||
disabled={inputValue !== shortUrl.shortCode || loading}
|
disabled={inputValue !== DELETION_PATTERN || loading}
|
||||||
>
|
>
|
||||||
{loading ? 'Deleting...' : 'Delete'}
|
{loading ? 'Deleting...' : 'Delete'}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@import '../../utils/base';
|
@import '../../utils/base';
|
||||||
|
@import '../../utils/mixins/text-ellipsis';
|
||||||
@import '../../utils/mixins/vertical-align';
|
@import '../../utils/mixins/vertical-align';
|
||||||
|
|
||||||
.short-urls-row__cell.short-urls-row__cell {
|
.short-urls-row__cell.short-urls-row__cell {
|
||||||
@@ -13,6 +14,26 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.short-urls-row__cell--indivisible {
|
||||||
|
@media (min-width: $lgMin) {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.short-urls-row__short-url-wrapper {
|
||||||
|
@media (max-width: $mdMax) {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: $lgMin) {
|
||||||
|
@include text-ellipsis();
|
||||||
|
|
||||||
|
vertical-align: bottom;
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 18rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.short-urls-row__copy-hint {
|
.short-urls-row__copy-hint {
|
||||||
@include vertical-align(translateX(10px));
|
@include vertical-align(translateX(10px));
|
||||||
|
|
||||||
|
|||||||
@@ -43,11 +43,8 @@ export const ShortUrlsRow = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isFirstRun.current) {
|
!isFirstRun.current && setActive();
|
||||||
isFirstRun.current = false;
|
isFirstRun.current = false;
|
||||||
} else {
|
|
||||||
setActive();
|
|
||||||
}
|
|
||||||
}, [shortUrl.visitsCount]);
|
}, [shortUrl.visitsCount]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -56,15 +53,20 @@ export const ShortUrlsRow = (
|
|||||||
<Time date={shortUrl.dateCreated} />
|
<Time date={shortUrl.dateCreated} />
|
||||||
</td>
|
</td>
|
||||||
<td className="responsive-table__cell short-urls-row__cell" data-th="Short URL">
|
<td className="responsive-table__cell short-urls-row__cell" data-th="Short URL">
|
||||||
<span className="indivisible short-urls-row__cell--relative">
|
<span className="short-urls-row__cell--relative short-urls-row__cell--indivisible">
|
||||||
<ExternalLink href={shortUrl.shortUrl} />
|
<span className="short-urls-row__short-url-wrapper">
|
||||||
|
<ExternalLink href={shortUrl.shortUrl} />
|
||||||
|
</span>
|
||||||
<CopyToClipboardIcon text={shortUrl.shortUrl} onCopy={setCopiedToClipboard} />
|
<CopyToClipboardIcon text={shortUrl.shortUrl} onCopy={setCopiedToClipboard} />
|
||||||
<span className="badge bg-warning text-black short-urls-row__copy-hint" hidden={!copiedToClipboard}>
|
<span className="badge bg-warning text-black short-urls-row__copy-hint" hidden={!copiedToClipboard}>
|
||||||
Copied short URL!
|
Copied short URL!
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="responsive-table__cell short-urls-row__cell short-urls-row__cell--break" data-th={`${shortUrl.title ? 'Title' : 'Long URL'}`}>
|
<td
|
||||||
|
className="responsive-table__cell short-urls-row__cell short-urls-row__cell--break"
|
||||||
|
data-th={`${shortUrl.title ? 'Title' : 'Long URL'}`}
|
||||||
|
>
|
||||||
<ExternalLink href={shortUrl.longUrl}>{shortUrl.title ?? shortUrl.longUrl}</ExternalLink>
|
<ExternalLink href={shortUrl.longUrl}>{shortUrl.title ?? shortUrl.longUrl}</ExternalLink>
|
||||||
</td>
|
</td>
|
||||||
{shortUrl.title && (
|
{shortUrl.title && (
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const useTimeoutToggle = (
|
|||||||
return [flag, callback];
|
return [flag, callback];
|
||||||
};
|
};
|
||||||
|
|
||||||
type ToggleResult = [ boolean, () => void, () => void, () => void ];
|
type ToggleResult = [boolean, () => void, () => void, () => void];
|
||||||
|
|
||||||
export const useToggle = (initialValue = false): ToggleResult => {
|
export const useToggle = (initialValue = false): ToggleResult => {
|
||||||
const [flag, setFlag] = useState<boolean>(initialValue);
|
const [flag, setFlag] = useState<boolean>(initialValue);
|
||||||
|
|||||||
5
src/utils/mixins/text-ellipsis.scss
Normal file
5
src/utils/mixins/text-ellipsis.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@mixin text-ellipsis() {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
@@ -62,30 +62,28 @@ describe('<DeleteShortUrlModal />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('enables submit button when proper short code is provided', async () => {
|
it('enables submit button when proper short code is provided', async () => {
|
||||||
const shortCode = 'abc123';
|
|
||||||
const { user } = setUp({
|
const { user } = setUp({
|
||||||
loading: false,
|
loading: false,
|
||||||
error: false,
|
error: false,
|
||||||
shortCode,
|
shortCode: 'abc123',
|
||||||
});
|
});
|
||||||
const getDeleteBtn = () => screen.getByRole('button', { name: 'Delete' });
|
const getDeleteBtn = () => screen.getByRole('button', { name: 'Delete' });
|
||||||
|
|
||||||
expect(getDeleteBtn()).toHaveAttribute('disabled');
|
expect(getDeleteBtn()).toHaveAttribute('disabled');
|
||||||
await user.type(screen.getByPlaceholderText(/^Insert the short code/), shortCode);
|
await user.type(screen.getByPlaceholderText('Insert delete'), 'delete');
|
||||||
expect(getDeleteBtn()).not.toHaveAttribute('disabled');
|
expect(getDeleteBtn()).not.toHaveAttribute('disabled');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tries to delete short URL when form is submit', async () => {
|
it('tries to delete short URL when form is submit', async () => {
|
||||||
const shortCode = 'abc123';
|
|
||||||
const { user } = setUp({
|
const { user } = setUp({
|
||||||
loading: false,
|
loading: false,
|
||||||
error: false,
|
error: false,
|
||||||
deleted: true,
|
deleted: true,
|
||||||
shortCode,
|
shortCode: 'abc123',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(deleteShortUrl).not.toHaveBeenCalled();
|
expect(deleteShortUrl).not.toHaveBeenCalled();
|
||||||
await user.type(screen.getByPlaceholderText(/^Insert the short code/), shortCode);
|
await user.type(screen.getByPlaceholderText('Insert delete'), 'delete');
|
||||||
await user.click(screen.getByRole('button', { name: 'Delete' }));
|
await user.click(screen.getByRole('button', { name: 'Delete' }));
|
||||||
expect(deleteShortUrl).toHaveBeenCalledTimes(1);
|
expect(deleteShortUrl).toHaveBeenCalledTimes(1);
|
||||||
await waitFor(() => expect(shortUrlDeleted).toHaveBeenCalledTimes(1));
|
await waitFor(() => expect(shortUrlDeleted).toHaveBeenCalledTimes(1));
|
||||||
|
|||||||
Reference in New Issue
Block a user