-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Site Settings: add date time format sub-page #11259
Changes from 25 commits
db973be
d543766
182ded9
28ba0cd
3b20156
ff8e9a6
58b4458
b66ec33
b2aa6ea
0548aa0
227a492
aa8bbb8
6018d26
eebf862
a388e06
3ed84f7
9842128
249647d
8c68099
4c439b2
289e576
6c30526
8eb467d
2c3a1de
a3b40b8
a15c407
a3cf018
7c1edeb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import React from 'react'; | ||
import { localize } from 'i18n-calypso'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import FormFieldset from 'components/forms/form-fieldset'; | ||
import FormInput from 'components/forms/form-text-input'; | ||
import FormLabel from 'components/forms/form-label'; | ||
import FormRadio from 'components/forms/form-radio'; | ||
import FormSettingExplanation from 'components/forms/form-setting-explanation'; | ||
import { phpToMomentDatetimeFormat } from 'lib/formatting'; | ||
import { defaultDateFormats } from './default-formats'; | ||
|
||
export const DateFormatOption = ( { | ||
dateFormat, | ||
disabled, | ||
isCustom, | ||
now, | ||
setCustomDateFormat, | ||
setDateFormat, | ||
translate, | ||
} ) => ( | ||
<FormFieldset> | ||
<FormLabel> | ||
{ translate( 'Date Format' ) } | ||
</FormLabel> | ||
{ defaultDateFormats.map( ( format, index ) => | ||
<FormLabel key={ index }> | ||
<FormRadio | ||
checked={ ! isCustom && format === dateFormat } | ||
disabled={ disabled } | ||
name="date_format" | ||
onChange={ setDateFormat } | ||
value={ format } | ||
/> | ||
<span>{ now.format( phpToMomentDatetimeFormat( format ) ) }</span> | ||
</FormLabel> | ||
) } | ||
<FormLabel className="date-time-format__custom-field"> | ||
<FormRadio | ||
checked={ isCustom } | ||
disabled={ disabled } | ||
name="date_format" | ||
onChange={ setCustomDateFormat } | ||
value={ dateFormat } | ||
/> | ||
<span> | ||
{ translate( 'Custom', { comment: 'Custom date/time format field' } ) } | ||
<FormInput | ||
disabled={ disabled } | ||
name="date_format_custom" | ||
onChange={ setCustomDateFormat } | ||
type="text" | ||
value={ dateFormat || '' } | ||
/> | ||
<FormSettingExplanation> | ||
{ isCustom && dateFormat | ||
? translate( 'Preview: %s', { | ||
args: now.format( phpToMomentDatetimeFormat( dateFormat ) ), | ||
comment: 'Date/time format preview', | ||
} ) | ||
: '' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused at the ternary here. Is the goal only to output a string when { isCustom && dateFormat && translate(
'Preview: %s',
{
args: …,
comment: …,
}
) } |
||
} | ||
</FormSettingExplanation> | ||
</span> | ||
</FormLabel> | ||
</FormFieldset> | ||
); | ||
|
||
export default localize( DateFormatOption ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import React, { Component } from 'react'; | ||
import { localize } from 'i18n-calypso'; | ||
import { capitalize, includes } from 'lodash'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import Button from 'components/button'; | ||
import Card from 'components/card'; | ||
import SectionHeader from 'components/section-header'; | ||
import DateFormatOption from './date-format-option'; | ||
import StartOfWeekOption from './start-of-week-option'; | ||
import TimeFormatOption from './time-format-option'; | ||
import { defaultDateFormats, defaultTimeFormats } from './default-formats'; | ||
import { getNow } from './utils'; | ||
import wrapSettingsForm from '../wrap-settings-form'; | ||
|
||
export class DateTimeFormatOptions extends Component { | ||
state = { | ||
customDateFormat: false, | ||
customTimeFormat: false, | ||
isLoadingSettings: true, | ||
}; | ||
|
||
componentWillReceiveProps( nextProps ) { | ||
const { | ||
fields: { | ||
date_format: dateFormat, | ||
time_format: timeFormat, | ||
}, | ||
} = nextProps; | ||
|
||
if ( ! this.state.isLoadingSettings || '' === dateFormat || '' === timeFormat ) { | ||
return; | ||
} | ||
|
||
this.setState( { | ||
customDateFormat: ! includes( defaultDateFormats, dateFormat ), | ||
customTimeFormat: ! includes( defaultTimeFormats, timeFormat ), | ||
isLoadingSettings: false, | ||
} ); | ||
} | ||
|
||
setFormat = ( name, defaultFormats ) => event => { | ||
const { value: format } = event.currentTarget; | ||
this.props.updateFields( { [ `${ name }_format` ]: format } ); | ||
this.setState( { | ||
[ `custom${ capitalize( name ) }Format` ]: ! includes( defaultFormats, format ), | ||
} ); | ||
}; | ||
|
||
setDateFormat = this.setFormat( 'date', defaultDateFormats ); | ||
|
||
setTimeFormat = this.setFormat( 'time', defaultTimeFormats ); | ||
|
||
setCustomFormat = name => event => { | ||
const { value: format } = event.currentTarget; | ||
this.props.updateFields( { [ `${ name }_format` ]: format } ); | ||
this.setState( { | ||
[ `custom${ capitalize( name ) }Format` ]: true, | ||
} ); | ||
}; | ||
|
||
setCustomDateFormat = this.setCustomFormat( 'date' ); | ||
|
||
setCustomTimeFormat = this.setCustomFormat( 'time' ); | ||
|
||
render() { | ||
const { | ||
fields: { | ||
date_format: dateFormat, | ||
start_of_week: startOfWeek, | ||
time_format: timeFormat, | ||
timezone_string: timezoneString, | ||
}, | ||
handleSelect, | ||
handleSubmitForm, | ||
isRequestingSettings, | ||
isSavingSettings, | ||
translate, | ||
} = this.props; | ||
|
||
const { | ||
customDateFormat, | ||
customTimeFormat, | ||
} = this.state; | ||
|
||
const now = getNow( timezoneString ); | ||
|
||
return ( | ||
<div> | ||
<SectionHeader> | ||
<Button | ||
compact={ true } | ||
onClick={ handleSubmitForm } | ||
primary={ true } | ||
type="submit" | ||
disabled={ isRequestingSettings || isSavingSettings }> | ||
{ isSavingSettings | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. small nitpick: this confused me here (also a big reason I prefer to keep the <Button
compact={ true }
…
>
{ isSavingSettings
? …
: …
}
</Button> |
||
? translate( 'Saving…' ) | ||
: translate( 'Save Settings' ) | ||
} | ||
</Button> | ||
</SectionHeader> | ||
<Card> | ||
<form> | ||
<DateFormatOption | ||
dateFormat={ dateFormat } | ||
disabled={ isRequestingSettings } | ||
isCustom={ customDateFormat } | ||
now={ now } | ||
setCustomDateFormat={ this.setCustomDateFormat } | ||
setDateFormat={ this.setDateFormat } | ||
/> | ||
<TimeFormatOption | ||
disabled={ isRequestingSettings } | ||
isCustom={ customTimeFormat } | ||
now={ now } | ||
setCustomTimeFormat={ this.setCustomTimeFormat } | ||
setTimeFormat={ this.setTimeFormat } | ||
timeFormat={ timeFormat } | ||
/> | ||
<StartOfWeekOption | ||
disabled={ isRequestingSettings } | ||
onChange={ handleSelect } | ||
startOfWeek={ startOfWeek } | ||
/> | ||
</form> | ||
</Card> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default wrapSettingsForm( settings => { | ||
const defaultSettings = { | ||
date_format: '', | ||
start_of_week: 0, | ||
time_format: '', | ||
timezone_string: '', | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if these are always constant then we might consider moving them into the module-global scope There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense. That whole part was basically brought over |
||
|
||
if ( ! settings ) { | ||
return defaultSettings; | ||
} | ||
|
||
const formSettings = { | ||
date_format: settings.date_format, | ||
start_of_week: settings.start_of_week, | ||
time_format: settings.time_format, | ||
timezone_string: settings.timezone_string, | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fun fact! we can use object assignment to provide defaults. I'm not sure if you want the default switch to be "either use all of the defaults or use none of them" but if you want to instead say "use any of the defaults if their corresponding value isn't set" then we can do this: const settings = {
...defaultSettings,
...customSettings,
}; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The thing here is that I don't need all the settings (there are currently 64 settings, some of them are arrays of objects), so I'd need at least a Then, the assumption is that, if So while I'd love to spread here, I'd say it would result in more code (and conditionals) for no real readability improvements. |
||
|
||
// handling `gmt_offset` and `timezone_string` values | ||
const gmt_offset = settings.gmt_offset; | ||
|
||
if ( | ||
! settings.timezone_string && | ||
typeof gmt_offset === 'string' && | ||
gmt_offset.length | ||
) { | ||
formSettings.timezone_string = 'UTC' + | ||
( /\-/.test( gmt_offset ) ? '' : '+' ) + | ||
gmt_offset; | ||
} | ||
|
||
return formSettings; | ||
} )( localize( DateTimeFormatOptions ) ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
export const defaultDateFormats = [ | ||
'F j, Y', | ||
'Y-m-d', | ||
'm/d/Y', | ||
'd/m/Y', | ||
]; | ||
|
||
export const defaultTimeFormats = [ | ||
'g:i a', | ||
'g:i A', | ||
'H:i', | ||
]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import React from 'react'; | ||
import { connect } from 'react-redux'; | ||
import { localize } from 'i18n-calypso'; | ||
import page from 'page'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import DateTimeFormatOptions from './date-time-format-options'; | ||
import DocumentHead from 'components/data/document-head'; | ||
import HeaderCake from 'components/header-cake'; | ||
import { getSelectedSite } from 'state/ui/selectors'; | ||
|
||
export const DateTimeFormat = ( { translate, site } ) => { | ||
const goBack = () => { | ||
page( '/settings/general/' + site.slug ); | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'd be on board with that 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe I'll start targeting this as soon as I close up some HTTP layer work… a simple middleware could handle many of the |
||
|
||
return ( | ||
<div className="main main-column date-time-format" role="main"> | ||
<DocumentHead title={ translate( 'Manage Date and Time Format' ) } /> | ||
<HeaderCake onClick={ goBack }> | ||
<h1> | ||
{ translate( 'Date and Time Format' ) } | ||
</h1> | ||
</HeaderCake> | ||
<DateTimeFormatOptions /> | ||
</div> | ||
); | ||
}; | ||
|
||
const mapStateToProps = state => ( { | ||
site: getSelectedSite( state ), | ||
} ); | ||
|
||
export default connect( mapStateToProps )( localize( DateTimeFormat ) ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import React from 'react'; | ||
import { localize } from 'i18n-calypso'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import FormFieldset from 'components/forms/form-fieldset'; | ||
import FormLabel from 'components/forms/form-label'; | ||
import FormSelect from 'components/forms/form-select'; | ||
|
||
export const StartOfWeekOption = ( { | ||
disabled, | ||
moment, | ||
onChange, | ||
startOfWeek, | ||
translate, | ||
} ) => | ||
<FormFieldset> | ||
<FormLabel> | ||
{ translate( 'Week starts on' ) } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi! I've found a possible matching string that has already been translated 88 times: Help me improve these suggestions: react with 👎 if the suggestion doesn't make any sense, or with 👍 if it's a particularly good one (even if not implemented). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi! I've found a possible matching string that has already been translated 88 times: Help me improve these suggestions: react with 👎 if the suggestion doesn't make any sense, or with 👍 if it's a particularly good one (even if not implemented). |
||
</FormLabel> | ||
<FormSelect | ||
disabled={ disabled } | ||
name="start_of_week" | ||
onChange={ onChange } | ||
value={ startOfWeek || 0 } | ||
> | ||
{ moment.weekdays().map( ( day, index ) => | ||
<option key={ index } value={ index }> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's no more difficult to just use |
||
{ day } | ||
</option> | ||
) } | ||
</FormSelect> | ||
</FormFieldset>; | ||
|
||
export default localize( StartOfWeekOption ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
.date-time-format__custom-field { | ||
.form-radio { | ||
margin-top: 12px; | ||
} | ||
|
||
.form-text-input { | ||
margin: 0 12px; | ||
width: 100px; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
danger, React key as loop index!