Skip to content

Commit cfe966a

Browse files
authored
Merge pull request #250 from acelaya-forks/feature/native-date-inputs
Feature/native date inputs
2 parents 3fc5730 + 18835b2 commit cfe966a

22 files changed

+201
-363
lines changed

.eslintrc

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
"project": "./tsconfig.json"
88
},
99
"rules": {
10+
"jsx-a11y/label-has-associated-control": ["error", {
11+
"controlComponents": ["Input", "DateInput"]
12+
}],
1013
"react/no-unused-prop-types": "off"
1114
},
1215
"overrides": [{

CHANGELOG.md

+17
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
44

55
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).
66

7+
## [Unreleased]
8+
### Added
9+
* *Nothing*
10+
11+
### Changed
12+
* Replace `react-datepicker` with native `input[type="date"]` and `input[type="datetime-local"]` elements.
13+
14+
### Deprecated
15+
* *Nothing*
16+
17+
### Removed
18+
* *Nothing*
19+
20+
### Fixed
21+
* *Nothing*
22+
23+
724
## [0.5.0] - 2024-01-29
825
### Added
926
* [#7](https://github.com/shlinkio/shlink-web-component/issues/7) Allow comparing visits for multiple short URLs, tags or domains.

package-lock.json

+7-77
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
"event-source-polyfill": "^1.0.31",
7070
"leaflet": "^1.9.4",
7171
"react-copy-to-clipboard": "^5.1.0",
72-
"react-datepicker": "^4.25.0",
7372
"react-external-link": "^2.2.0",
7473
"react-leaflet": "^4.2.1",
7574
"react-swipeable": "^7.0.1",
@@ -87,7 +86,6 @@
8786
"@types/leaflet": "^1.9.8",
8887
"@types/react": "^18.2.52",
8988
"@types/react-copy-to-clipboard": "^5.0.7",
90-
"@types/react-datepicker": "^4.19.5",
9189
"@types/react-dom": "^18.2.18",
9290
"@vitejs/plugin-react": "^4.2.1",
9391
"@vitest/coverage-v8": "^1.2.2",

src/index.scss

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
@import './tags/react-tag-autocomplete';
22
@import './utils/StickyCardPaginator.scss';
33
@import 'leaflet/dist/leaflet.css';
4-
@import 'react-datepicker/dist/react-datepicker.min.css';
54

65
.input-group-text.input-group-text {
76
border-color: var(--input-border-color);
@@ -12,3 +11,17 @@
1211
top: $headerHeight;
1312
z-index: 10;
1413
}
14+
15+
// This should probably go to shlink-frontend-kit
16+
$input-types: "text", "date", "datetime-local", "password", "number", "email", "search", "url";
17+
18+
@each $type in $input-types {
19+
.card input[type="#{$type}"]:not(:disabled),
20+
.dropdown input[type="#{$type}"]:not(:disabled) {
21+
background-color: var(--input-color) !important;
22+
}
23+
}
24+
25+
.dropdown label {
26+
color: var(--text-color) !important;
27+
}

src/short-urls/ShortUrlForm.tsx

+41-23
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ import type { DomainSelectorProps } from '../domains/DomainSelector';
1515
import type { TagsSelectorProps } from '../tags/helpers/TagsSelector';
1616
import type { TagsList } from '../tags/reducers/tagsList';
1717
import { IconInput } from '../utils/components/IconInput';
18-
import type { DateTimeInputProps } from '../utils/dates/DateTimeInput';
19-
import { DateTimeInput } from '../utils/dates/DateTimeInput';
2018
import { formatIsoDate } from '../utils/dates/helpers/date';
19+
import { LabelledDateInput } from '../utils/dates/LabelledDateInput';
2120
import { useFeature } from '../utils/features';
2221
import { handleEventPreventingDefault, hasValue } from '../utils/helpers';
2322
import { ShortUrlFormCheckboxGroup } from './helpers/ShortUrlFormCheckboxGroup';
@@ -26,7 +25,6 @@ import './ShortUrlForm.scss';
2625

2726
export type Mode = 'create' | 'create-basic' | 'edit';
2827

29-
type DateFields = 'validSince' | 'validUntil';
3028
type NonDateFields = 'longUrl' | 'customSlug' | 'shortCodeLength' | 'domain' | 'maxVisits' | 'title';
3129

3230
export interface ShortUrlFormProps<T extends ShlinkCreateShortUrlData | ShlinkEditShortUrlData> {
@@ -74,12 +72,9 @@ const ShortUrlForm: FCWithDeps<ShortUrlFormConnectProps, ShortUrlFormDeps> = (
7472
// value gets removed. Otherwise, set undefined so that it gets ignored.
7573
return hasValue(initialValue) ? null : undefined;
7674
};
77-
const submit = handleEventPreventingDefault(async () => onSave({
78-
...shortUrlData,
79-
validSince: formatIsoDate(shortUrlData.validSince) ?? null,
80-
validUntil: formatIsoDate(shortUrlData.validUntil) ?? null,
81-
maxVisits: !hasValue(shortUrlData.maxVisits) ? null : Number(shortUrlData.maxVisits),
82-
}).then((result: any) => !isEdit && !isErrorAction(result) && reset()).catch(() => {}));
75+
const submit = handleEventPreventingDefault(async () => onSave(shortUrlData)
76+
.then((result: any) => !isEdit && !isErrorAction(result) && reset())
77+
.catch(() => {}));
8378

8479
useEffect(() => {
8580
setShortUrlData(initialState);
@@ -123,16 +118,6 @@ const ShortUrlForm: FCWithDeps<ShortUrlFormConnectProps, ShortUrlFormDeps> = (
123118
}))}
124119
/>
125120
);
126-
const renderDateInput = (id: DateFields, placeholder: string, props: Partial<DateTimeInputProps> = {}) => (
127-
<DateTimeInput
128-
name={id}
129-
selected={shortUrlData[id] ? toDate(shortUrlData[id] as string | Date) : null}
130-
placeholderText={placeholder}
131-
isClearable
132-
onChange={(date) => setShortUrlData((prev) => ({ ...prev, [id]: date }))}
133-
{...props}
134-
/>
135-
);
136121
const basicComponents = (
137122
<>
138123
<FormGroup>
@@ -216,11 +201,44 @@ const ShortUrlForm: FCWithDeps<ShortUrlFormConnectProps, ShortUrlFormDeps> = (
216201

217202
<div className="col-sm-6 mb-3">
218203
<SimpleCard title="Limit access to the short URL">
219-
{renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })}
220-
<div className="mb-3">
221-
{renderDateInput('validSince', 'Enabled since...', { maxDate: shortUrlData.validUntil ? toDate(shortUrlData.validUntil) : undefined })}
204+
<div className="row mb-3">
205+
<div className="col-lg-6">
206+
<LabelledDateInput
207+
label="Enabled since"
208+
withTime
209+
maxDate={shortUrlData.validUntil ? toDate(shortUrlData.validUntil) : undefined}
210+
name="validSince"
211+
value={shortUrlData.validSince ? toDate(shortUrlData.validSince) : null}
212+
onChange={(date) => setShortUrlData((prev) => ({ ...prev, validSince: formatIsoDate(date) }))}
213+
/>
214+
</div>
215+
<div className="col-lg-6 mt-3 mt-lg-0">
216+
<LabelledDateInput
217+
label="Enabled until"
218+
withTime
219+
minDate={shortUrlData.validSince ? toDate(shortUrlData.validSince) : undefined}
220+
name="validUntil"
221+
value={shortUrlData.validUntil ? toDate(shortUrlData.validUntil) : null}
222+
onChange={(date) => setShortUrlData((prev) => ({ ...prev, validUntil: formatIsoDate(date) }))}
223+
/>
224+
</div>
225+
</div>
226+
227+
<div>
228+
<label htmlFor="maxVisits" className="mb-1">Maximum visits allowed:</label>
229+
<Input
230+
name="maxVisits"
231+
id="maxVisits"
232+
type="number"
233+
min={1}
234+
placeholder="25..."
235+
value={shortUrlData.maxVisits ?? ''}
236+
onChange={(e) => setShortUrlData((prev) => ({
237+
...prev,
238+
maxVisits: !hasValue(e.target.value) ? null : Number(e.target.value),
239+
}))}
240+
/>
222241
</div>
223-
{renderDateInput('validUntil', 'Enabled until...', { minDate: shortUrlData.validSince ? toDate(shortUrlData.validSince) : undefined })}
224242
</SimpleCard>
225243
</div>
226244
</Row>

src/short-urls/ShortUrlsFilteringBar.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ const ShortUrlsFilteringBar: FCWithDeps<ShortUrlsFilteringConnectProps, ShortUrl
104104
<Row className="flex-lg-row-reverse">
105105
<div className="col-lg-8 col-xl-6 mt-3">
106106
<div className="d-md-flex">
107-
<div className="flex-fill">
107+
<div className="flex-grow-1">
108108
<DateRangeSelector
109109
defaultText="All short URLs"
110110
dateRangeOrInterval={activeInterval ?? datesToDateRange(startDate, endDate)}

src/short-urls/helpers/ShortUrlsFilterDropdown.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const ShortUrlsFilterDropdown = (
1616
const onFilterClick = (key: keyof ShortUrlsFilter) => () => onChange({ ...selected, [key]: !selected?.[key] });
1717

1818
return (
19-
<DropdownBtn text="Filters" dropdownClassName={className} inline end minWidth={250}>
19+
<DropdownBtn text="Filters" dropdownClassName={className} end minWidth={250}>
2020
<DropdownItem header aria-hidden>Visits:</DropdownItem>
2121
<DropdownItem active={excludeBots} onClick={onFilterClick('excludeBots')}>Ignore visits from bots</DropdownItem>
2222

src/utils/components/IconInput.scss

-5
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@
1313
background-color: var(--primary-color) !important;
1414
}
1515

16-
.card .icon-input-container__input:not(:disabled),
17-
.dropdown .icon-input-container__input:not(:disabled) {
18-
background-color: var(--input-color) !important;
19-
}
20-
2116
.icon-input-container__icon {
2217
@include vertical-align();
2318

0 commit comments

Comments
 (0)