Core Datepicker
@nrk/core-datepicker
enhances all child input
, select
table
and button
elements with keyboard accessible functionality
for selecting both dates and times. The interface and granularity of date refinement can easily be altered through markup.
Examples (Plain JS)
Toggled calendar
Toggled datepicker (using core-toggle) with calendar to update value of input
Note: We add event listeners to both, datepicker.change
as well as datepicker.click.day
, events to only close the core-toggle
on the latter of the two.
<input type="text" placeholder="No date selected" id="toggled-datepicker-output">
<button type="button">Show calendar</button>
<core-toggle id="calendar-toggle" data-popup hidden class="my-popup">
<core-datepicker
id="toggled-datepicker"
days="Mon,Tue,Wed,Thu,Fri,Sat,Sun"
months="January,Febuary,March,April,May,June,July,August,September,October,November,December"
>
<table></table>
</core-datepicker>
</core-toggle>
<script>
document.addEventListener('datepicker.change', function (event) {
if (event.target.id !== 'toggled-datepicker') return
document.getElementById('toggled-datepicker-output').value = event.target.date ? event.target.date.toLocaleString() : null
})
document.addEventListener('datepicker.click.day', function (event) {
if (event.target.id !== 'toggled-datepicker') return
document.getElementById('calendar-toggle').setAttribute('hidden', true)
document.getElementById('toggled-datepicker-output').value = event.target.date ? event.target.date.toLocaleString() : null
})
</script>
Adjacent calendar
Datepicker with inline calendar
Note: Table and buttons are outside of core-datepicker element, using data-for
.
<core-datepicker
id="adjacent-datepicker"
></core-datepicker>
<button type="button" data-for="adjacent-datepicker" value="-1 month">Previous month</button>
<input id="adjacent-datepicker-output" placeholder="No date selected" readonly/>
<button type="button" data-for="adjacent-datepicker" value="now">Today</button>
<button type="button" data-for="adjacent-datepicker" value="+1 month">Next month</button>
<table data-for="adjacent-datepicker"></table>
<script>
document.addEventListener('datepicker.change', function (event) {
if (event.target.id !== 'adjacent-datepicker') return
document.getElementById('adjacent-datepicker-output').value = event.target.date ? event.target.date.toLocaleString() : null
})
</script>
All the things
Extravagantly featured implementation to showcase most of what you can do out of the box
<button type="button" class="my-toggle">Choose date</button>
<core-toggle data-popup hidden class="my-popup">
<core-datepicker
id="my-datepicker"
days="Mon,Tue,Wed,Thu,Fri,Sat,Sun"
months="January,Febuary,March,April,May,June,July,August,September,October,November,December"
>
<input type="timestamp">
<fieldset>
<legend>Navigation</legend>
<button type="button" value="now">Today</button>
<button type="button" value="now - 1 day">Yesterday</button>
<button type="button" value="now + 1 day">Tomorrow</button>
<button type="button" value="- 1 week">Previous week</button>
<button type="button" value="+ 1 week">Next week</button>
<button type="button" value="now tuesday - 1 week">Tuesday last week</button>
<button type="button" value="now + 10 years">Add ten years</button>
<button type="button" value="yy00-01-01 - 100 years">Last century</button>
</fieldset>
<label>
Year
<select>
<option value="2016-m-d">2016</option>
<option value="2017-m-d">2017</option>
<option value="2018-m-d">2018</option>
<option value="2019-m-d">2019</option>
</select>
</label>
<label>Month<select></select></label>
<fieldset>
<legend>Month</legend>
<label><input type="radio" name="my-months" value="y-1-d">January</label>
<label><input type="radio" name="my-months" value="y-2-d">February</label>
<label><input type="radio" name="my-months" value="y-3-d">March</label>
<label><input type="radio" name="my-months" value="y-4-d">April</label>
<label><input type="radio" name="my-months" value="y-5-d">May</label>
<label><input type="radio" name="my-months" value="y-6-d">June</label>
<label><input type="radio" name="my-months" value="y-7-d">July</label>
<label><input type="radio" name="my-months" value="y-8-d">August</label>
<label><input type="radio" name="my-months" value="y-9-d">September</label>
<label><input type="radio" name="my-months" value="y-10-d">October</label>
<label><input type="radio" name="my-months" value="y-11-d">November</label>
<label><input type="radio" name="my-months" value="y-12-d">December</label>
</fieldset>
<label><span>Year</span><input type="year"></label>
<label><span>Month</span><input type="month"></label>
<fieldset>
<legend>Clock</legend>
<label>Hour<input type="hour"></label>
<label>Minute<input type="minute"></label>
<label>
<span>Hour</span>
<select>
<option>--</option>
<option value="11:m">11</option>
<option value="12:m">12</option>
<option value="13:m">13</option>
</select>
</label>
</fieldset>
<table></table>
</core-datepicker>
</core-toggle>
<button type="button" data-for="my-datepicker" value="now">Now</button>
<button type="button" data-for="my-datepicker" value="now + 1 week">Next week</button>
<button type="button" data-for="my-datepicker" value="+ 1 week">Add one week</button>
<select data-for="my-datepicker">
<option>Hour</option>
<option value="11:m">11</option>
<option value="12:m">12</option>
<option value="13:m">13</option>
</select>
<table data-for="my-datepicker"></table>
<input type="text" id="my-datepicker-output" placeholder="No date selected">
<script>
document.getElementById('my-datepicker').disabled = (date) => {
var oneWeekFromNow = (new Date()).setDate(new Date().getDate() + 7)
return date > oneWeekFromNow
}
document.addEventListener('datepicker.change', function (event) {
if (event.target.id !== 'my-datepicker') return
document.getElementById('my-datepicker-output').value = event.target.date.toLocaleString()
})
</script>
Examples (React)
Toggled calendar
Toggled datepicker (using core-toggle) with calendar to update value of input
<div id="react-basic-datepicker"></div>
<script>
const BasicPicker = () => {
const [hiddenVal, setHiddenVal] = React.useState(true)
const [dateVal, setDateVal] = React.useState(null)
const handleToggle = (event) => { setHiddenVal(event.target.hidden) }
const handleDateChange = (event) => { setDateVal(event.target.date) }
const handleDateClick = (event) => {
setDateVal(event.target.date)
setHiddenVal(true)
}
return (
<>
<input type="text" readOnly value={dateVal ? dateVal.toLocaleDateString() : ''} placeholder="No date selected"/>
<button type="button">Choose date</button>
<CoreToggle
className="my-popup"
hidden={hiddenVal}
onToggle={handleToggle}
data-popup
>
<CoreDatepicker
date={dateVal}
onDatepickerChange={handleDateChange}
onDatepickerClickDay={handleDateClick}
>
<label>Year<input type="year" /></label>
<label>Month<select></select></label>
<table></table>
</CoreDatepicker>
</CoreToggle>
</>
)
}
ReactDOM.render(<BasicPicker />, document.getElementById('react-basic-datepicker'))
</script>
React class
<div id="jsx-datepicker"></div>
<script type="text/javascript">
class MyDate extends React.Component {
constructor (props) {
super(props)
this.today = Date.now() - Date.now() % 864e3
this.state = { date: null }
this.onNow = this.onNow.bind(this)
this.onChange = this.onChange.bind(this)
this.resetDate = this.resetDate.bind(this)
this.myRef = React.createRef();
}
onNow () { this.setState({ date: new Date() }) }
onChange (event) { this.setState({ date: event.target.date }) }
resetDate () {
this.setState({ date: null })
}
getForwardRef (node) { return node }
render () {
return <>
<button type="button">Choose date</button>
<CoreToggle hidden data-popup className="my-popup">
<CoreDatepicker
date={this.state.date}
disabled={(date) => date <= this.today}
onDatepickerChange={this.onChange}
forwardRef={this.myRef}
>
<label>Year <input type="year" /></label>
<label>Month <select></select></label>
<table></table>
</CoreDatepicker>
</CoreToggle>
<button type="button" onClick={this.onNow}>Today</button>
<button type="button" onClick={this.resetDate}>Reset</button>
<input type="text" readOnly value={this.state.date ? this.state.date.toLocaleDateString() : ''} placeholder="No date selected" />
</>
}
}
ReactDOM.render(<MyDate />, document.getElementById('jsx-datepicker'))
</script>
Installation
Using NPM provides own element namespace and extensibility.
Recommended:
npm install @nrk/core-datepicker
Using static registers the custom element with default name automatically:
<script src="https://static.nrk.no/core-components/major/10/core-datepicker/core-datepicker.min.js"></script>
Remember to polyfill custom elements if needed.
Usage
All date values - both HTML markup and JavaScript - accepts accepts dates as numbers, or as natural language in the format of @nrk/simple-date-parse.
HTML / JavaScript
<core-datepicker
date="{String}" <!-- Optional. Uses simple-date-parse to set date from parseable value or natural language -->
months="{String}"
days="{String}">
<input type="radio|checkbox|year|month|day|hour|minute|second|timestamp"/>
<select></select>
<select>
<option value="2016-m-d">Set year to 2016</option>
<option value="19yy-1-1">Back 100 years and set to January 1st.</option>
<option value="1985-12-19">December 19, 1985</option>
</select>
<table></table>
<fieldset>
<legend>Navigasjon</legend>
<button type="button" value="now">I dag</button>
<button type="button" value="now - 1 day|week|month|year">I går/forrige uke/måned/år</button>
<button type="button" value="now + 1 day|week|month|year">I morgen/neste uke/måned/år</button>
<button type="button" value="yy00-01-01">Start of current century</button>
</fieldset>
</core-datepicker>
import CoreDatepicker from '@nrk/core-datepicker'
window.customElements.define('core-datepicker', CoreProgress)
const myDatepicker = document.querySelector('core-datepicker')
myDatepicker.date
myDatepicker.timestamp
myDatepicker.year
myDatepicker.month
myDatepicker.day
myDatepicker.hour
myDatepicker.minute
myDatepicker.second
myDatepicker.date = 'now'
myDatepicker.months = ['Jan', 'Feb', ...]
myDatepicker.days = ['Man', 'Tir', ...]
myDatepicker.disabled = Function|Boolean
myDatepicker.parse('fri')
React / Preact
import CoreDatepicker from '@nrk/core-datepicker/jsx'
<CoreDatepicker
date={String}
months={String}
days={String}
ref={(comp) => {}}
forwardRef={(el) => {}}
onDatepickerChange={Function}
onDatepickerClickDay={Function}
>
<input type="radio|checkbox|year|month|day|hour|minute|second|timestamp"/>
<select></select>
<table></table>
</CoreDatepicker>
Events
datepicker.change
Fired when date is changed by user or programatically:
document.addEventListener('datepicker.change', (event) => {
event.target
event.detail
})
datepicker.click.day
Fired if the user clicks a day in the month days grid. The datepicker.click.day
runs before datepicker.change
:
document.addEventListener('datepicker.click.day', (event) => {
event.target
})
Properties
@nrk/core-datepicker
defaults to Norwegian Bokmål text without abbreviations (writing September
instead of Sept
). This can be configured by setting the days
and months
properties.
Note that abbreviations should always be at least 3 characters long to ensure a better experience for screen reader users (for instance writing Mon
, Tue
... instead of m
, t
...).
myDatepicker.days = ['man', 'tir', 'ons', 'tor', 'fre', 'lør', 'søn']
myDatepicker.months = ['jan', 'feb', ...]
myDatepicker.disabled = (date) => date > Date.now()
myDatepicker.disabled = false
Styling
CSS
.my-datepicker
.my-datepicker input:checked
.my-datepicker input:disabled
.my-datepicker button:disabled
.my-datepicker button[autofocus]
.my-datepicker button[aria-current="date"]
.my-datepicker button[data-adjacent="false"]
.my-datepicker button[data-adjacent="true"]