Fix up CanvasDateInput and use it in MutationAuditLog

Refs FOO-625
flag=none

The new InstUI DateInput is way less opinionated than the old
one, so it takes a lot more handholding to make it function.
Fortunately someone had written much of a Canvas driver for
that component and it is only going to need a little tweaking.

As a first test of this, I did the MutationAuditLog requester
using this new component.

Test plan:
* Go to Admin Tools -> Logging -> GraphQL Mutation Activity
* That form doesn't look exactly the same, but should have all
  the same functionality as it used to

Change-Id: Ic61e91623562e7990f861ef44019ba92f5e87fd8
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/247425
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Ahmad Amireh <ahmad@instructure.com>
QA-Review: Ahmad Amireh <ahmad@instructure.com>
Product-Review: Charley Kline <ckline@instructure.com>
This commit is contained in:
Charley Kline 2020-09-10 19:10:06 -05:00
parent ebbd319d8a
commit 07a43d4024
3 changed files with 69 additions and 39 deletions

View File

@ -18,19 +18,26 @@
import {ApolloProvider, Query, gql, createClient} from 'jsx/canvas-apollo'
import React, {useState} from 'react'
import I18n from 'i18n!mutationActivity'
import tz from 'timezone'
import {Button} from '@instructure/ui-buttons'
import {DateInput, TextInput} from '@instructure/ui-forms'
import {TextInput} from '@instructure/ui-text-input'
import CanvasDateInput from 'jsx/shared/components/CanvasDateInput'
import {Heading} from '@instructure/ui-elements'
import {Table} from '@instructure/ui-table'
import {Spinner} from '@instructure/ui-spinner'
import {View} from '@instructure/ui-layout'
import {Grid} from '@instructure/ui-grid'
function formatDate(date) {
return tz.format(date, 'date.formats.medium_with_weekday')
}
const AuditLogForm = ({onSubmit}) => {
const [assetString, setAssetString] = useState('')
const [startDate, setStartDate] = useState(null)
const [endDate, setEndDate] = useState(null)
const makeDateHandler = setter => (_e, isoDate, _raw, conversionFailed) => {
if (!conversionFailed) setter(isoDate)
const makeDateHandler = setter => value => {
if (value) setter(value)
}
const formDisabled = assetString.length === 0
@ -52,29 +59,38 @@ const AuditLogForm = ({onSubmit}) => {
}}
required
/>
<br />
<DateInput
label={I18n.t('Start Date')}
previousLabel={I18n.t('Previous Month')}
nextLabel={I18n.t('Next Month')}
onDateChange={makeDateHandler(setStartDate)}
dateValue={startDate}
/>
<br />
<div style={{marginTop: '1.5em'}} />
<DateInput
label={I18n.t('End Date')}
previousLabel={I18n.t('Previous Month')}
nextLabel={I18n.t('Next Month')}
onDateChange={makeDateHandler(setEndDate)}
dateValue={endDate}
/>
<br />
<Button variant="primary" type="submit" margin="small 0 0" disabled={formDisabled}>
{I18n.t('Find')}
</Button>
<Grid>
<Grid.Row>
<Grid.Col>
<CanvasDateInput
renderLabel={I18n.t('Start Date')}
onSelectedDateChange={makeDateHandler(setStartDate)}
formatDate={formatDate}
selectedDate={startDate}
placement="top center"
withRunningValue
/>
</Grid.Col>
<Grid.Col>
<CanvasDateInput
renderLabel={I18n.t('End Date')}
onSelectedDateChange={makeDateHandler(setEndDate)}
formatDate={formatDate}
selectedDate={endDate}
placement="top center"
withRunningValue
/>
</Grid.Col>
<Grid.Col vAlign="middle">
<Button variant="primary" type="submit" margin="small 0 0" disabled={formDisabled}>
{I18n.t('Find')}
</Button>
</Grid.Col>
</Grid.Row>
</Grid>
</form>
</View>
)

View File

@ -18,7 +18,7 @@
import I18n from 'i18n!app_shared_components_canvas_date_time'
import React, {useRef, useState} from 'react'
import {arrayOf, element, func, instanceOf, oneOfType, shape, string} from 'prop-types'
import {arrayOf, bool, element, func, instanceOf, oneOfType, shape, string} from 'prop-types'
import moment from 'moment-timezone'
import tz from 'timezone'
import {DateTime} from '@instructure/ui-i18n'
@ -37,14 +37,18 @@ CanvasDateInput.propTypes = {
timezone: string,
formatDate: func.isRequired,
onSelectedDateChange: func.isRequired,
interaction: string
interaction: string,
placement: string,
withRunningValue: bool
}
CanvasDateInput.defaultProps = {
timezone: ENV?.TIMEZONE || DateTime.browserTimeZone(),
renderLabel: I18n.t('Choose a date'),
messages: [],
interaction: 'enabled'
interaction: 'enabled',
placement: 'bottom center',
withRunningValue: false
}
export default function CanvasDateInput({
@ -54,7 +58,9 @@ export default function CanvasDateInput({
timezone,
formatDate,
onSelectedDateChange,
interaction
interaction,
placement,
withRunningValue
}) {
const todayMoment = moment().tz(timezone)
const selectedMoment = selectedDate && moment.tz(selectedDate, timezone)
@ -62,7 +68,7 @@ export default function CanvasDateInput({
const [inputValue, setInputValue] = useState('')
const [isShowingCalendar, setIsShowingCalendar] = useState(false)
const [renderedMoment, setRenderedMoment] = useState(selectedMoment || todayMoment)
const [errorMessages, setErrorMessages] = useState([])
const [internalMessages, setInternalMessages] = useState([])
const priorSelectedMoment = useRef(null)
function isDifferentMoment(firstMoment, secondMoment) {
@ -82,7 +88,7 @@ export default function CanvasDateInput({
function syncInput(newMoment) {
const newInputValue = newMoment ? formatDate(newMoment.toDate()) : ''
setInputValue(newInputValue)
setErrorMessages([])
setInternalMessages([])
setRenderedMoment(newMoment || todayMoment)
}
@ -119,12 +125,13 @@ export default function CanvasDateInput({
setInputValue(value)
const newDate = tz.parse(value)
if (newDate) {
const msgs = withRunningValue ? [{type: 'success', text: formatDate(newDate)}] : []
setRenderedMoment(moment.tz(newDate, timezone))
setErrorMessages([])
setInternalMessages(msgs)
} else if (value === '') {
setErrorMessages([])
setInternalMessages([])
} else {
setErrorMessages([{type: 'error', text: I18n.t("That's not a date!")}])
setInternalMessages([{type: 'error', text: I18n.t('Invalid Date')}])
}
}
@ -207,7 +214,8 @@ export default function CanvasDateInput({
value={inputValue}
onChange={handleChange}
isInline
messages={messages.concat(errorMessages)}
placement={placement}
messages={messages.concat(internalMessages)}
isShowingCalendar={isShowingCalendar}
onBlur={handleBlur}
onRequestShowCalendar={() => setIsShowingCalendar(true)}

View File

@ -226,34 +226,34 @@ describe('rendered month', () => {
describe('error messages', () => {
it('shows an error message if the input date is unparseable', () => {
const {getByText} = renderAndDirtyInput('asdf')
expect(getByText("That's not a date!")).toBeInTheDocument()
expect(getByText('Invalid Date')).toBeInTheDocument()
})
it('clears error messages when the selectedDate changes', () => {
const {props, rerender, queryByText} = renderAndDirtyInput('asdf')
props.selectedDate = new Date()
rerender(<CanvasDateInput {...props} />)
expect(queryByText("That's not a date!")).toBeNull()
expect(queryByText('Invalid Date')).toBeNull()
})
it('clears error messages when a day is clicked', () => {
const date = new Date()
const {getByText, queryByText} = renderAndDirtyInput('asdf', {selectedDate: date})
fireEvent.click(getByText('15'))
expect(queryByText("That's not a date!")).toBeNull()
expect(queryByText('Invalid Date')).toBeNull()
})
it('clears error messages even when selectedDay is clicked', () => {
const date = new Date()
const {getByText, queryByText} = renderAndDirtyInput('asdf', {selectedDate: date})
fireEvent.click(getByText('20'))
expect(queryByText("That's not a date!")).toBeNull()
expect(queryByText('Invalid Date')).toBeNull()
})
it('clears error messages when the input changes to an empty string', () => {
const {getInput, queryByText} = renderAndDirtyInput('asdf')
fireEvent.change(getInput(), {target: {value: ''}})
expect(queryByText("That's not a date!")).toBeNull()
expect(queryByText('Invalid Date')).toBeNull()
})
})
@ -264,6 +264,12 @@ describe('messages', () => {
})
expect(getByText('my what an interesting date')).toBeInTheDocument()
})
it('shows a running result when requested', () => {
const {getByText} = renderAndDirtyInput('sat', {withRunningValue: true})
// The Saturday after our "current date" is 5/23/2020
expect(getByText('2020-05-23T00:00:00.000Z')).toBeInTheDocument()
})
})
describe('locales', () => {