From 366b6d47bb53dfc0add2c5d9e081c0126ec1b1f6 Mon Sep 17 00:00:00 2001 From: SanttuA Date: Thu, 15 Feb 2024 10:32:39 +0200 Subject: [PATCH] Improved my premises page's date input (#300) Changes: - styled input to have field bottom border to have more text input feel - changed input icon to be calendar - added today shortcut button in day select popover - added aria-label to the input field --- app/i18n/messages/en.json | 1 + app/i18n/messages/fi.json | 1 + app/i18n/messages/sv.json | 1 + .../_admin-resources-page.scss | 49 +++++++++++-------- .../availability-view/_availability-view.scss | 4 +- app/shared/date-picker/DatePicker.js | 22 ++++++--- app/shared/date-picker/DatePicker.spec.js | 38 +++++++++++++- app/shared/date-picker/_date-picker.scss | 49 ++++++++++++++----- 8 files changed, 122 insertions(+), 43 deletions(-) diff --git a/app/i18n/messages/en.json b/app/i18n/messages/en.json index d159e9357..cd31d0075 100644 --- a/app/i18n/messages/en.json +++ b/app/i18n/messages/en.json @@ -107,6 +107,7 @@ "common.starFieldsAreRequired": "The fields marked with an asterisk (*) are mandatory.", "common.taxesTotal": "Total taxes", "common.taxlessTotal": "Tax-free total", + "common.today": "Today", "common.total": "total", "common.unit.time.hour": "h", "common.unit.time.day": "day", diff --git a/app/i18n/messages/fi.json b/app/i18n/messages/fi.json index b87c37649..fc0a6194e 100644 --- a/app/i18n/messages/fi.json +++ b/app/i18n/messages/fi.json @@ -107,6 +107,7 @@ "common.starFieldsAreRequired": "Tähdellä (*) merkityt tiedot ovat pakollisia.", "common.taxesTotal": "Verot yhteensä", "common.taxlessTotal": "Veroton yhteensä", + "common.today": "Tänään", "common.total": "yhteensä", "common.unit.time.hour": "h", "common.unit.time.day": "päivä", diff --git a/app/i18n/messages/sv.json b/app/i18n/messages/sv.json index d79fed976..d7edb8e92 100644 --- a/app/i18n/messages/sv.json +++ b/app/i18n/messages/sv.json @@ -107,6 +107,7 @@ "common.starFieldsAreRequired": "Uppgifterna markerade med en asterisk (*) är obligatoriska.", "common.taxesTotal": "Totala skatter", "common.taxlessTotal": "Skattefritt totalt", + "common.today": "Idag", "common.total": "totalt", "common.unit.time.hour": "h", "common.unit.time.day": "dag", diff --git a/app/pages/admin-resources/_admin-resources-page.scss b/app/pages/admin-resources/_admin-resources-page.scss index 55ac3f7e1..fa9b934ef 100644 --- a/app/pages/admin-resources/_admin-resources-page.scss +++ b/app/pages/admin-resources/_admin-resources-page.scss @@ -1,14 +1,22 @@ .admin-resources-page { min-height: 600px; - .loadedContent{ + .loadedContent { .availability-view .date-selector { font-size: 1em; - .date-picker { - > input { - min-width: 140px; - } + height: 40px; + + .date-picker { + >input { + min-width: 142px; + border-bottom: solid white 2px; + padding-top: 0; + padding-bottom: 0; + margin-bottom: 2px; + height: 30px; + } + .DayPicker-WeekDaysRow, .DayPicker-Day { font-size: 1em; @@ -24,14 +32,13 @@ text-decoration: none; } } - - } + .date-picker .DayPicker-WeekDaysRow { font-size: 1em; } - @include high-contrast{ + @include high-contrast { .loadedContent { .resource-type-filter-container { @@ -45,20 +52,25 @@ color: black; border: 1px solid black; } - .sidebar > .group-info { + + .sidebar>.group-info { background-color: transparent; border: 1px solid black; } + .date-selector { background-color: transparent; color: black; border: 1px solid black; + a { color: black; } + .date-picker input { color: black; } + .date-picker::after { border-top-color: black; } @@ -68,28 +80,25 @@ } @media(max-width: $screen-sm-max) { - .availability-view .date-selector .current-value .date-picker{ - > div { - margin-left: -150%; - } - > input { + .availability-view .date-selector .current-value .date-picker { + >div { + margin-left: -150%; + } + + >input { min-width: 120px; } } - } @media(max-width: $screen-xs-max) { - .availability-view .date-selector { + .availability-view .date-selector { justify-content: center; .previous, .next { display: none; } - - - } } -} +} \ No newline at end of file diff --git a/app/shared/availability-view/_availability-view.scss b/app/shared/availability-view/_availability-view.scss index cc0f1245c..5c2572b1e 100644 --- a/app/shared/availability-view/_availability-view.scss +++ b/app/shared/availability-view/_availability-view.scss @@ -50,7 +50,7 @@ .date-selector { display: flex; align-items: center; - height: $av-header-height; + height: 40px; margin-bottom: $padding-base-vertical; background-color: $brand-primary; color: white; @@ -417,4 +417,4 @@ @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { padding: 2px 5px; } -} +} \ No newline at end of file diff --git a/app/shared/date-picker/DatePicker.js b/app/shared/date-picker/DatePicker.js index 62d1d7425..5b92644c0 100644 --- a/app/shared/date-picker/DatePicker.js +++ b/app/shared/date-picker/DatePicker.js @@ -9,14 +9,15 @@ import MomentLocaleUtils, { import { connect } from 'react-redux'; import { createStructuredSelector } from 'reselect'; +import { injectT } from 'i18n'; import AppConstants from 'constants/AppConstants'; import { currentLanguageSelector } from 'state/selectors/translationSelectors'; const defaultDateFormat = 'YYYY-MM-DD'; const localizedDateFormat = 'D.M.YYYY'; -export function UnconnectedDatePicker({ - dateFormat, onChange, currentLocale, value, rest +function UnconnectedDatePicker({ + dateFormat, onChange, currentLocale, value, rest, t }) { const pickerDateFormat = dateFormat || localizedDateFormat; @@ -29,16 +30,22 @@ export function UnconnectedDatePicker({ dayPickerProps={{ showOutsideDays: true, localeUtils: MomentLocaleUtils, - locale: currentLocale + locale: currentLocale, + todayButton: t('common.today'), }} format={pickerDateFormat} formatDate={formatDate} + inputProps={{ + 'aria-label': t('DatePickerControl.buttonLabel') + }} keepFocus={false} onDayChange={date => onChange(formatDate(date, defaultDateFormat))} parseDate={parseDate} value={new Date(value)} {...rest} - /> + > + + ); } @@ -47,7 +54,8 @@ UnconnectedDatePicker.propTypes = { onChange: PropTypes.func.isRequired, value: PropTypes.string.isRequired, currentLocale: PropTypes.string, - rest: PropTypes.object + rest: PropTypes.object, + t: PropTypes.func.isRequired, }; UnconnectedDatePicker.defaultProps = { @@ -58,4 +66,6 @@ const languageSelector = createStructuredSelector({ currentLocale: currentLanguageSelector }); -export default connect(languageSelector)(UnconnectedDatePicker); +UnconnectedDatePicker = injectT(UnconnectedDatePicker); // eslint-disable-line +export { UnconnectedDatePicker }; +export default connect(languageSelector)(injectT(UnconnectedDatePicker)); diff --git a/app/shared/date-picker/DatePicker.spec.js b/app/shared/date-picker/DatePicker.spec.js index 654610b4a..fead2667f 100644 --- a/app/shared/date-picker/DatePicker.spec.js +++ b/app/shared/date-picker/DatePicker.spec.js @@ -1,16 +1,17 @@ -import { shallow } from 'enzyme'; import React from 'react'; import DayPickerInput from 'react-day-picker/DayPickerInput'; import simple from 'simple-mock'; +import { shallowWithIntl } from 'utils/testUtils'; import { UnconnectedDatePicker as DatePicker } from './DatePicker'; function getWrapper(props) { const defaults = { onChange: () => null, value: '2016-12-12', + t: () => {}, }; - return shallow(); + return shallowWithIntl(); } describe('shared/date-picker/DatePicker', () => { @@ -23,6 +24,39 @@ describe('shared/date-picker/DatePicker', () => { expect(getDateFieldWrapper()).toHaveLength(1); }); + test('has correct props', () => { + const input = getDateFieldWrapper(); + expect(input.prop('classNames')).toEqual({ + container: 'date-picker', + overlay: 'date-picker-overlay', + }); + + expect(input.prop('dayPickerProps')).toEqual({ + showOutsideDays: true, + localeUtils: expect.any(Object), + locale: 'fi', + todayButton: 'common.today' + }); + + expect(input.prop('inputProps')).toEqual({ + 'aria-label': 'DatePickerControl.buttonLabel', + }); + + expect(input.prop('format')).toBe('D.M.YYYY'); + expect(input.prop('formatDate')).toStrictEqual(expect.any(Function)); + expect(input.prop('keepFocus')).toBe(false); + expect(input.prop('onDayChange')).toStrictEqual(expect.any(Function)); + expect(input.prop('parseDate')).toStrictEqual(expect.any(Function)); + expect(input.prop('value')).toStrictEqual(new Date('2016-12-12')); + }); + + test('has glyphicon span', () => { + const input = getDateFieldWrapper(); + const span = input.find('span'); + expect(span.hasClass('glyphicon')).toBe(true); + expect(span.hasClass('glyphicon-calendar')).toBe(true); + }); + test('changing date calls onDayChange with date in correct format', () => { const onChange = simple.mock(); const dateField = getDateFieldWrapper({ onChange }); diff --git a/app/shared/date-picker/_date-picker.scss b/app/shared/date-picker/_date-picker.scss index e4a984951..b54d4a398 100644 --- a/app/shared/date-picker/_date-picker.scss +++ b/app/shared/date-picker/_date-picker.scss @@ -4,16 +4,17 @@ @include add-focus; &::after { - $arrow-size: 6px; - display: inline-block; - content: ''; position: absolute; - top: calc(50% - #{$arrow-size / 2}); - right: 5px; - border-left: $arrow-size solid transparent; - border-right: $arrow-size solid transparent; - border-top: $arrow-size solid $white; + top: 5.5px; + right: 4px; pointer-events: none; + content: ''; + display: inline-block; + width: 26px; + height: 26px; + background: url('/app/assets/icons/calendar.svg'); + background-size: 26px 26px; + filter: invert(100%) sepia(100%) saturate(1%) hue-rotate(285deg) brightness(105%) contrast(101%); } input { @@ -25,9 +26,10 @@ background-color: white; position: absolute; z-index: 100; - border: solid 2px $brand-primary;; + border: solid 2px $brand-primary; padding: 20px; - @media (max-width: $screen-xs-max){ + + @media (max-width: $screen-xs-max) { width: 330px; padding-top: 12px; padding-left: 8px; @@ -41,13 +43,18 @@ max-width: 400px; margin: 0 auto; width: 100%; - @media (max-width: $screen-xs-max){ + + @media (max-width: $screen-xs-max) { width: 300px; } &:focus { outline: none; } + + .DayPicker-wrapper { + padding-bottom: 0; + } } .DayPicker-NavBar { @@ -85,7 +92,8 @@ margin: 0; border-collapse: separate; width: 100%; - @media (max-width: $screen-xs-max){ + + @media (max-width: $screen-xs-max) { border-collapse: collapse; } } @@ -98,27 +106,33 @@ background-color: $light-gray; border-radius: 0; font-size: 1.6rem; - @media (max-width: $screen-xs-max){ + + @media (max-width: $screen-xs-max) { font-size: 1.2rem; } &:focus { outline: none; } + &--available:not(.DayPicker-Day--disabled) { background-color: $light-gray; + &.DayPicker-Day--selected { background-color: $light-gray; } } + &--outside { color: $medium-gray; } + &--disabled { cursor: pointer; background-color: $medium-gray; color: $dark-gray; } + &--selected, &--selected:not(.DayPicker-Day--disabled):not(.DayPicker-Day--outside) { color: $white; @@ -138,6 +152,7 @@ .DayPicker-Weekdays { text-transform: uppercase; font-weight: normal; + abbr { border: none; cursor: default; @@ -152,4 +167,12 @@ .DayPicker-WeekNumber { font-size: 14px; } + + .DayPicker-Footer { + text-align: center; + + .DayPicker-TodayButton { + color: #0072c6; + } + } }