Fix up some <Table> usage to comply with InstUI 7.0

Refs USERS-624
flag=none

A bunch of calls to the InstUI <Table> component are
still using "plain old HTML" <tr> <th> <td> etc inside
them, which is a no-no as of InstUI 7.0. This commit
addresses some of them (about half, I think).

Testing this is mostly a matter of observing the affected
displays in the UI and making sure they still look normal.
Note that some subtle changes to the UI may have occurred
due to the InstUI Canvas theme being used rather than the
original Canvas CSS, but everything in here looks just
fine to my eye.

Test plan:
* all tests pass
* The "act as" modal dialog still looks good
* Adding people to courses (duplicate section, missing
  people section, and the "people ready list") still
  all looks good
* Developer key table still looks good

Change-Id: I20d2c0c88d9050eb73be33e8c26c951a90bc6160
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/240161
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Ed Schiebel <eschiebel@instructure.com>
Product-Review: Charley Kline <ckline@instructure.com>
QA-Review: August Thornton <august@instructure.com>
This commit is contained in:
Charley Kline 2020-06-13 16:47:11 -05:00
parent f0b8f0ab2c
commit 01d434fab0
14 changed files with 512 additions and 709 deletions

View File

@ -25,7 +25,9 @@ import I18n from 'i18n!act_as'
import Modal from '../shared/components/InstuiModal'
import {ScreenReaderContent} from '@instructure/ui-a11y'
import {View} from '@instructure/ui-layout'
import {Text, Avatar, Spinner, Table} from '@instructure/ui-elements'
import {Text, Avatar} from '@instructure/ui-elements'
import {Spinner} from '@instructure/ui-spinner'
import {Table} from '@instructure/ui-table'
import {Button} from '@instructure/ui-buttons'
import ActAsMask from './ActAsMask'
@ -61,7 +63,7 @@ export default class ActAsModal extends React.Component {
this._button = null
}
componentWillMount() {
UNSAFE_componentWillMount() {
if (window.location.href === document.referrer) {
this.setState({isLoading: true})
window.location.href = '/'
@ -103,17 +105,17 @@ export default class ActAsModal extends React.Component {
renderInfoTable(caption, renderRows) {
return (
<Table caption={<ScreenReaderContent>{caption}</ScreenReaderContent>}>
<thead>
<tr>
<th>
<Table caption={caption}>
<Table.Head>
<Table.Row>
<Table.ColHeader id="actasmodal-category">
<ScreenReaderContent>{I18n.t('Category')}</ScreenReaderContent>
</th>
<th>
</Table.ColHeader>
<Table.ColHeader id="actasmodal-userinfo">
<ScreenReaderContent>{I18n.t('User information')}</ScreenReaderContent>
</th>
</tr>
</thead>
</Table.ColHeader>
</Table.Row>
</Table.Head>
{renderRows()}
</Table>
)
@ -122,42 +124,40 @@ export default class ActAsModal extends React.Component {
renderUserInfoRows = () => {
const user = this.props.user
return (
<tbody>
<Table.Body>
{this.renderUserRow(I18n.t('Full Name:'), user.name)}
{this.renderUserRow(I18n.t('Display Name:'), user.short_name)}
{this.renderUserRow(I18n.t('Sortable Name:'), user.sortable_name)}
{this.renderUserRow(I18n.t('Default Email:'), user.email)}
</tbody>
</Table.Body>
)
}
renderLoginInfoRows = pseudonym => (
<tbody>
<Table.Body>
{this.renderUserRow(I18n.t('Login ID:'), pseudonym.login_id)}
{this.renderUserRow(I18n.t('SIS ID:'), pseudonym.sis_id)}
{this.renderUserRow(I18n.t('Integration ID:'), pseudonym.integration_id)}
</tbody>
</Table.Body>
)
renderUserRow(category, info) {
return (
<tr>
<td>
<Table.Row>
<Table.Cell>
<Text size="small">{category}</Text>
</td>
<td>
<View as="div" textAlign="end">
<Text size="small" weight="bold">
{info}
</Text>
</View>
</td>
</tr>
</Table.Cell>
<Table.Cell textAlign="end">
<Text size="small" weight="bold">
{info}
</Text>
</Table.Cell>
</Table.Row>
)
}
render() {
const user = this.props.user
const {user} = this.props
return (
<div>

View File

@ -17,11 +17,13 @@
*/
import React from 'react'
import {shallow} from 'enzyme'
import {shallow, mount} from 'enzyme'
import ActAsModal from '../ActAsModal'
import ActAsMask from '../ActAsMask'
import ActAsPanda from '../ActAsPanda'
import {Text, Avatar, Spinner, Table} from '@instructure/ui-elements'
import {Text, Avatar} from '@instructure/ui-elements'
import {Table} from '@instructure/ui-table'
import {Spinner} from '@instructure/ui-spinner'
import {Button} from '@instructure/ui-buttons'
const props = {
@ -68,7 +70,7 @@ describe('ActAsModal', () => {
})
test('it renders the table with correct user information', () => {
const wrapper = shallow(<ActAsModal {...props} />)
const wrapper = mount(<ActAsModal {...props} />)
const tables = wrapper.find(Table)
expect(tables).toHaveLength(3)

View File

@ -137,38 +137,41 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
textAlign="center"
>
<Table
caption={
<ScreenReaderContent
as="span"
>
User details
</ScreenReaderContent>
}
caption="User details"
hover={false}
layout="auto"
size="medium"
>
<thead>
<tr>
<th>
<Head>
<Row>
<ColHeader
id="actasmodal-category"
sortDirection="none"
textAlign="start"
>
<ScreenReaderContent
as="span"
>
Category
</ScreenReaderContent>
</th>
<th>
</ColHeader>
<ColHeader
id="actasmodal-userinfo"
sortDirection="none"
textAlign="start"
>
<ScreenReaderContent
as="span"
>
User information
</ScreenReaderContent>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
</ColHeader>
</Row>
</Head>
<Body>
<Row>
<Cell
textAlign="start"
>
<Text
as="span"
letterSpacing="normal"
@ -177,36 +180,25 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
>
Full Name:
</Text>
</td>
<td>
<View
as="div"
borderColor="default"
debug={false}
display="auto"
focusColor="info"
focusPosition="offset"
focused={false}
overflowX="visible"
overflowY="visible"
position="static"
shouldAnimateFocus={true}
textAlign="end"
</Cell>
<Cell
textAlign="end"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
test user
</Text>
</View>
</td>
</tr>
<tr>
<td>
test user
</Text>
</Cell>
</Row>
<Row>
<Cell
textAlign="start"
>
<Text
as="span"
letterSpacing="normal"
@ -215,36 +207,25 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
>
Display Name:
</Text>
</td>
<td>
<View
as="div"
borderColor="default"
debug={false}
display="auto"
focusColor="info"
focusPosition="offset"
focused={false}
overflowX="visible"
overflowY="visible"
position="static"
shouldAnimateFocus={true}
textAlign="end"
</Cell>
<Cell
textAlign="end"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
foo
</Text>
</View>
</td>
</tr>
<tr>
<td>
foo
</Text>
</Cell>
</Row>
<Row>
<Cell
textAlign="start"
>
<Text
as="span"
letterSpacing="normal"
@ -253,36 +234,25 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
>
Sortable Name:
</Text>
</td>
<td>
<View
as="div"
borderColor="default"
debug={false}
display="auto"
focusColor="info"
focusPosition="offset"
focused={false}
overflowX="visible"
overflowY="visible"
position="static"
shouldAnimateFocus={true}
textAlign="end"
</Cell>
<Cell
textAlign="end"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
bar, baz
</Text>
</View>
</td>
</tr>
<tr>
<td>
bar, baz
</Text>
</Cell>
</Row>
<Row>
<Cell
textAlign="start"
>
<Text
as="span"
letterSpacing="normal"
@ -291,35 +261,22 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
>
Default Email:
</Text>
</td>
<td>
<View
as="div"
borderColor="default"
debug={false}
display="auto"
focusColor="info"
focusPosition="offset"
focused={false}
overflowX="visible"
overflowY="visible"
position="static"
shouldAnimateFocus={true}
textAlign="end"
</Cell>
<Cell
textAlign="end"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
testUser@test.com
</Text>
</View>
</td>
</tr>
</tbody>
testUser@test.com
</Text>
</Cell>
</Row>
</Body>
</Table>
</View>
<View
@ -339,38 +296,41 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
textAlign="center"
>
<Table
caption={
<ScreenReaderContent
as="span"
>
Login info
</ScreenReaderContent>
}
caption="Login info"
hover={false}
layout="auto"
size="medium"
>
<thead>
<tr>
<th>
<Head>
<Row>
<ColHeader
id="actasmodal-category"
sortDirection="none"
textAlign="start"
>
<ScreenReaderContent
as="span"
>
Category
</ScreenReaderContent>
</th>
<th>
</ColHeader>
<ColHeader
id="actasmodal-userinfo"
sortDirection="none"
textAlign="start"
>
<ScreenReaderContent
as="span"
>
User information
</ScreenReaderContent>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
</ColHeader>
</Row>
</Head>
<Body>
<Row>
<Cell
textAlign="start"
>
<Text
as="span"
letterSpacing="normal"
@ -379,36 +339,25 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
>
Login ID:
</Text>
</td>
<td>
<View
as="div"
borderColor="default"
debug={false}
display="auto"
focusColor="info"
focusPosition="offset"
focused={false}
overflowX="visible"
overflowY="visible"
position="static"
shouldAnimateFocus={true}
textAlign="end"
</Cell>
<Cell
textAlign="end"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
qux
</Text>
</View>
</td>
</tr>
<tr>
<td>
qux
</Text>
</Cell>
</Row>
<Row>
<Cell
textAlign="start"
>
<Text
as="span"
letterSpacing="normal"
@ -417,36 +366,25 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
>
SIS ID:
</Text>
</td>
<td>
<View
as="div"
borderColor="default"
debug={false}
display="auto"
focusColor="info"
focusPosition="offset"
focused={false}
overflowX="visible"
overflowY="visible"
position="static"
shouldAnimateFocus={true}
textAlign="end"
</Cell>
<Cell
textAlign="end"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
555
</Text>
</View>
</td>
</tr>
<tr>
<td>
555
</Text>
</Cell>
</Row>
<Row>
<Cell
textAlign="start"
>
<Text
as="span"
letterSpacing="normal"
@ -455,35 +393,22 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
>
Integration ID:
</Text>
</td>
<td>
<View
as="div"
borderColor="default"
debug={false}
display="auto"
focusColor="info"
focusPosition="offset"
focused={false}
overflowX="visible"
overflowY="visible"
position="static"
shouldAnimateFocus={true}
textAlign="end"
</Cell>
<Cell
textAlign="end"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
222
</Text>
</View>
</td>
</tr>
</tbody>
222
</Text>
</Cell>
</Row>
</Body>
</Table>
</View>
<View
@ -503,38 +428,41 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
textAlign="center"
>
<Table
caption={
<ScreenReaderContent
as="span"
>
Login info
</ScreenReaderContent>
}
caption="Login info"
hover={false}
layout="auto"
size="medium"
>
<thead>
<tr>
<th>
<Head>
<Row>
<ColHeader
id="actasmodal-category"
sortDirection="none"
textAlign="start"
>
<ScreenReaderContent
as="span"
>
Category
</ScreenReaderContent>
</th>
<th>
</ColHeader>
<ColHeader
id="actasmodal-userinfo"
sortDirection="none"
textAlign="start"
>
<ScreenReaderContent
as="span"
>
User information
</ScreenReaderContent>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
</ColHeader>
</Row>
</Head>
<Body>
<Row>
<Cell
textAlign="start"
>
<Text
as="span"
letterSpacing="normal"
@ -543,36 +471,25 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
>
Login ID:
</Text>
</td>
<td>
<View
as="div"
borderColor="default"
debug={false}
display="auto"
focusColor="info"
focusPosition="offset"
focused={false}
overflowX="visible"
overflowY="visible"
position="static"
shouldAnimateFocus={true}
textAlign="end"
</Cell>
<Cell
textAlign="end"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
tic
</Text>
</View>
</td>
</tr>
<tr>
<td>
tic
</Text>
</Cell>
</Row>
<Row>
<Cell
textAlign="start"
>
<Text
as="span"
letterSpacing="normal"
@ -581,36 +498,25 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
>
SIS ID:
</Text>
</td>
<td>
<View
as="div"
borderColor="default"
debug={false}
display="auto"
focusColor="info"
focusPosition="offset"
focused={false}
overflowX="visible"
overflowY="visible"
position="static"
shouldAnimateFocus={true}
textAlign="end"
</Cell>
<Cell
textAlign="end"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
777
</Text>
</View>
</td>
</tr>
<tr>
<td>
777
</Text>
</Cell>
</Row>
<Row>
<Cell
textAlign="start"
>
<Text
as="span"
letterSpacing="normal"
@ -619,35 +525,22 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
>
Integration ID:
</Text>
</td>
<td>
<View
as="div"
borderColor="default"
debug={false}
display="auto"
focusColor="info"
focusPosition="offset"
focused={false}
overflowX="visible"
overflowY="visible"
position="static"
shouldAnimateFocus={true}
textAlign="end"
</Cell>
<Cell
textAlign="end"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
<Text
as="span"
letterSpacing="normal"
size="small"
weight="bold"
wrap="normal"
>
888
</Text>
</View>
</td>
</tr>
</tbody>
888
</Text>
</Cell>
</Row>
</Body>
</Table>
</View>
<View

View File

@ -17,7 +17,7 @@
*/
import React from 'react'
import {shallow} from 'enzyme'
import {shallow, mount} from 'enzyme'
import PeopleReadyList from '../people_ready_list'
const props = {
@ -58,7 +58,7 @@ describe('PeopleReadyList', () => {
})
test('sets the correct values', () => {
const wrapper = shallow(<PeopleReadyList {...props} />)
const wrapper = mount(<PeopleReadyList {...props} />)
const peopleReadyList = wrapper.find('.addpeople__peoplereadylist')
const cols = peopleReadyList.find('thead th')
@ -102,14 +102,14 @@ describe('PeopleReadyList', () => {
})
test('hides SIS ID column if not permitted', () => {
let wrapper = shallow(<PeopleReadyList {...props} canReadSIS />)
let wrapper = mount(<PeopleReadyList {...props} canReadSIS />)
let peopleReadyList = wrapper.find('.addpeople__peoplereadylist')
let cols = peopleReadyList.find('thead th')
expect(cols.length).toEqual(5) // incluldes SIS ID column
expect(cols.at(3).text()).toEqual('SIS ID')
wrapper = shallow(<PeopleReadyList {...props} canReadSIS={false} />)
wrapper = mount(<PeopleReadyList {...props} canReadSIS={false} />)
peopleReadyList = wrapper.find('.addpeople__peoplereadylist')
cols = peopleReadyList.find('thead th')

View File

@ -19,8 +19,9 @@
import I18n from 'i18n!add_people_duplicate_section'
import React from 'react'
import PropTypes from 'prop-types'
import shapes from './shapes'
import {Table, Text} from '@instructure/ui-elements'
import {duplicateSetShape} from './shapes'
import {Text} from '@instructure/ui-elements'
import {Table} from '@instructure/ui-table'
import {ScreenReaderContent} from '@instructure/ui-a11y'
import {TextInput, RadioInput} from '@instructure/ui-forms'
import {Button} from '@instructure/ui-buttons'
@ -36,7 +37,7 @@ function eatEvent(event) {
class DuplicateSection extends React.Component {
static propTypes = {
duplicates: PropTypes.shape(shapes.duplicateSetShape).isRequired,
duplicates: PropTypes.shape(duplicateSetShape).isRequired,
onSelectDuplicate: PropTypes.func.isRequired,
onNewForDuplicate: PropTypes.func.isRequired,
onSkipDuplicate: PropTypes.func.isRequired,
@ -95,8 +96,8 @@ class DuplicateSection extends React.Component {
for (let i = 0; i < this.props.duplicates.userList.length; ++i) {
const user = this.props.duplicates.userList[i]
// match as string or number
// eslint-disable-next-line eqeqeq
if (user.user_id == userId) {
// eslint-disable-line eqeqeq
retval = user
break
}
@ -115,8 +116,8 @@ class DuplicateSection extends React.Component {
const k = `dupe_${duplicateSet.address}_${i}`
const checked = duplicateSet.selectedUserId === dupe.user_id
return (
<tr key={k}>
<th scope="row">
<Table.Row key={k}>
<Table.RowHeader>
<RadioInput
value={dupe.user_id}
name={duplicateSet.address}
@ -128,13 +129,13 @@ class DuplicateSection extends React.Component {
</ScreenReaderContent>
}
/>
</th>
<td>{dupe.user_name}</td>
<td>{dupe.email}</td>
<td>{dupe.login_id}</td>
<td>{dupe.sis_user_id || ''}</td>
<td>{dupe.account_name || ''}</td>
</tr>
</Table.RowHeader>
<Table.Cell>{dupe.user_name}</Table.Cell>
<Table.Cell>{dupe.email}</Table.Cell>
<Table.Cell>{dupe.login_id}</Table.Cell>
<Table.Cell>{dupe.sis_user_id || ''}</Table.Cell>
<Table.Cell>{dupe.account_name || ''}</Table.Cell>
</Table.Row>
)
})
if (this.props.inviteUsersURL) {
@ -142,8 +143,8 @@ class DuplicateSection extends React.Component {
if (duplicateSet.createNew) {
// render the row as an editor
rows.push(
<tr key={duplicateSet.address + CREATE_NEW} className="create-new">
<th scope="row">
<Table.Row key={duplicateSet.address + CREATE_NEW} data-testid="create-new">
<Table.RowHeader>
<RadioInput
value={CREATE_NEW}
name={duplicateSet.address}
@ -157,8 +158,8 @@ class DuplicateSection extends React.Component {
</ScreenReaderContent>
}
/>
</th>
<td>
</Table.RowHeader>
<Table.Cell>
<TextInput
required
name="name"
@ -168,8 +169,8 @@ class DuplicateSection extends React.Component {
value={duplicateSet.newUserInfo.name}
onChange={this.onNewForDuplicateChange}
/>
</td>
<td>
</Table.Cell>
<Table.Cell>
<TextInput
required
name="email"
@ -179,15 +180,15 @@ class DuplicateSection extends React.Component {
value={duplicateSet.newUserInfo.email}
onChange={this.onNewForDuplicateChange}
/>
</td>
<td colSpan="3" />
</tr>
</Table.Cell>
<Table.Cell colSpan="3" />
</Table.Row>
)
} else {
// render the row as a hint to the user
rows.push(
<tr key={duplicateSet.address + CREATE_NEW} className="create-new">
<th scope="row">
<Table.Row key={duplicateSet.address + CREATE_NEW} data-testid="create-new">
<Table.RowHeader>
<RadioInput
value={CREATE_NEW}
name={duplicateSet.address}
@ -201,8 +202,8 @@ class DuplicateSection extends React.Component {
</ScreenReaderContent>
}
/>
</th>
<td colSpan="5">
</Table.RowHeader>
<Table.Cell colSpan="5">
<Button
variant="link"
onClick={this.onSelectNewForDuplicate}
@ -210,15 +211,15 @@ class DuplicateSection extends React.Component {
>
{I18n.t('Create a new user for "%{address}"', {address: duplicateSet.address})}
</Button>
</td>
</tr>
</Table.Cell>
</Table.Row>
)
}
}
// finally, the skip this user row
rows.push(
<tr key={duplicateSet.address + SKIP} className="skip-addr">
<th scope="row">
<Table.Row key={duplicateSet.address + SKIP} data-testid="skip-addr">
<Table.RowHeader>
<RadioInput
value={SKIP}
name={duplicateSet.address}
@ -230,8 +231,8 @@ class DuplicateSection extends React.Component {
</ScreenReaderContent>
}
/>
</th>
<td colSpan="5">
</Table.RowHeader>
<Table.Cell colSpan="5">
<Button
onClick={this.onSkipDuplicate}
variant="link"
@ -239,8 +240,8 @@ class DuplicateSection extends React.Component {
>
{I18n.t('Dont add this user for now.')}
</Button>
</td>
</tr>
</Table.Cell>
</Table.Row>
)
return rows
}
@ -258,19 +259,19 @@ class DuplicateSection extends React.Component {
</Text>
}
>
<thead>
<tr>
<th scope="col">
<Table.Head>
<Table.Row>
<Table.ColHeader id="dupsection-select">
<ScreenReaderContent>{I18n.t('User Selection')}</ScreenReaderContent>
</th>
<th scope="col">{I18n.t('Name')}</th>
<th scope="col">{I18n.t('Email Address')}</th>
<th scope="col">{I18n.t('Login ID')}</th>
<th scope="col">{I18n.t('SIS ID')}</th>
<th scope="col">{I18n.t('Institution')}</th>
</tr>
</thead>
<tbody>{this.renderDupeList()}</tbody>
</Table.ColHeader>
<Table.ColHeader id="dupsection-name">{I18n.t('Name')}</Table.ColHeader>
<Table.ColHeader id="dupsection-email">{I18n.t('Email Address')}</Table.ColHeader>
<Table.ColHeader id="dupsection-loginid">{I18n.t('Login ID')}</Table.ColHeader>
<Table.ColHeader id="dupsection-sisid">{I18n.t('SIS ID')}</Table.ColHeader>
<Table.ColHeader id="dupsection-inst">{I18n.t('Institution')}</Table.ColHeader>
</Table.Row>
</Table.Head>
<Table.Body>{this.renderDupeList()}</Table.Body>
</Table>
</div>
)

View File

@ -19,8 +19,8 @@
import I18n from 'i18n!add_people_missing_people_section'
import React from 'react'
import PropTypes from 'prop-types'
import shapes from './shapes'
import {Table} from '@instructure/ui-elements'
import {missingsShape} from './shapes'
import {Table} from '@instructure/ui-table'
import {ScreenReaderContent} from '@instructure/ui-a11y'
import {TextInput, Checkbox} from '@instructure/ui-forms'
import {Button} from '@instructure/ui-buttons'
@ -36,7 +36,7 @@ function eatEvent(event) {
class MissingPeopleSection extends React.Component {
static propTypes = {
missing: PropTypes.shape(shapes.missingsShape).isRequired,
missing: PropTypes.shape(missingsShape).isRequired,
searchType: PropTypes.string.isRequired,
inviteUsersURL: PropTypes.string,
onChange: PropTypes.func.isRequired
@ -52,10 +52,10 @@ class MissingPeopleSection extends React.Component {
this.state = {
selectAll: false
}
this.tbodyNode = null
this.tbodyNode = React.createRef()
}
componentWillReceiveProps(nextProps) {
UNSAFE_componentWillReceiveProps(nextProps) {
const all = Object.keys(nextProps.missing).every(m => nextProps.missing[m].createNew)
this.setState({selectAll: all})
}
@ -74,7 +74,7 @@ class MissingPeopleSection extends React.Component {
) {
// The link was rendered with the attribute data-address=address for this row.
// Use it to find the checkbox with the matching value.
const checkbox = this.tbodyNode.querySelector(
const checkbox = this.tbodyNode.current.querySelector(
`input[type="checkbox"][value="${event.currentTarget.getAttribute('data-address')}"]`
)
if (checkbox) {
@ -155,14 +155,14 @@ class MissingPeopleSection extends React.Component {
if (!this.props.inviteUsersURL) {
// cannot create new users. Just show the missing ones
row = (
<tr key={`missing_${missing.address}`}>
<th scope="row">{missing.address}</th>
</tr>
<Table.Row key={`missing_${missing.address}`}>
<Table.RowHeader>{missing.address}</Table.RowHeader>
</Table.Row>
)
} else if (missing.createNew) {
row = (
<tr key={`missing_${missing.address}`}>
<td>
<Table.Row key={`missing_${missing.address}`}>
<Table.Cell>
<Checkbox
value={missing.address}
checked
@ -175,8 +175,8 @@ class MissingPeopleSection extends React.Component {
</ScreenReaderContent>
}
/>
</td>
<td>
</Table.Cell>
<Table.Cell>
<TextInput
required
name="name"
@ -187,8 +187,8 @@ class MissingPeopleSection extends React.Component {
onChange={this.onNewForMissingChange}
value={missing.newUserInfo.name || ''}
/>
</td>
<td>
</Table.Cell>
<Table.Cell>
<TextInput
required
name="email"
@ -199,14 +199,14 @@ class MissingPeopleSection extends React.Component {
onChange={this.onNewForMissingChange}
value={missing.newUserInfo.email || ''}
/>
</td>
<th scope="row">{missing.address}</th>
</tr>
</Table.Cell>
<Table.RowHeader>{missing.address}</Table.RowHeader>
</Table.Row>
)
} else {
row = (
<tr key={`missing_${missing.address}`}>
<td>
<Table.Row key={`missing_${missing.address}`}>
<Table.Cell>
<Checkbox
value={missing.address}
checked={false}
@ -217,8 +217,8 @@ class MissingPeopleSection extends React.Component {
</ScreenReaderContent>
}
/>
</td>
<td colSpan="2">
</Table.Cell>
<Table.Cell colSpan="2">
<Button
variant="link"
onClick={this.onSelectNewForMissing}
@ -226,9 +226,9 @@ class MissingPeopleSection extends React.Component {
>
{namePrompt}
</Button>
</td>
<th scope="row">{missing.address}</th>
</tr>
</Table.Cell>
<Table.RowHeader>{missing.address}</Table.RowHeader>
</Table.Row>
)
}
return row
@ -246,14 +246,14 @@ class MissingPeopleSection extends React.Component {
if (!this.props.inviteUsersURL) {
// cannot create new users. Just show the missing ones
row = (
<tr key={`missing_${missing.address}`}>
<th scope="row">{missing.address}</th>
</tr>
<Table.Row key={`missing_${missing.address}`}>
<Table.RowHeader>{missing.address}</Table.RowHeader>
</Table.Row>
)
} else if (missing.createNew) {
row = (
<tr key={`missing_${missing.address}`}>
<td>
<Table.Row key={`missing_${missing.address}`}>
<Table.Cell>
<Checkbox
value={missing.address}
checked
@ -266,8 +266,8 @@ class MissingPeopleSection extends React.Component {
</ScreenReaderContent>
}
/>
</td>
<td>
</Table.Cell>
<Table.Cell>
<TextInput
required
name="name"
@ -278,14 +278,14 @@ class MissingPeopleSection extends React.Component {
onChange={this.onNewForMissingChange}
value={missing.newUserInfo.name || ''}
/>
</td>
<th scope="row">{missing.address}</th>
</tr>
</Table.Cell>
<Table.RowHeader>{missing.address}</Table.RowHeader>
</Table.Row>
)
} else {
row = (
<tr key={`missing_${missing.address}`} checked={false}>
<td>
<Table.Row key={`missing_${missing.address}`}>
<Table.Cell>
<Checkbox
value={missing.address}
checked={false}
@ -296,8 +296,8 @@ class MissingPeopleSection extends React.Component {
</ScreenReaderContent>
}
/>
</td>
<td>
</Table.Cell>
<Table.Cell>
<Button
variant="link"
onClick={this.onSelectNewForMissing}
@ -306,9 +306,9 @@ class MissingPeopleSection extends React.Component {
>
{namePrompt}
</Button>
</td>
<th scope="row">{missing.address}</th>
</tr>
</Table.Cell>
<Table.RowHeader>{missing.address}</Table.RowHeader>
</Table.Row>
)
}
return row
@ -318,16 +318,16 @@ class MissingPeopleSection extends React.Component {
renderTableHead() {
let idColHeader = null
if (this.props.searchType === 'unique_id') {
idColHeader = <th scope="col">{I18n.t('Login ID')}</th>
idColHeader = <Table.ColHeader id="login-id">{I18n.t('Login ID')}</Table.ColHeader>
} else if (this.props.searchType === 'sis_user_id') {
idColHeader = <th scope="col">{I18n.t('SIS ID')}</th>
idColHeader = <Table.ColHeader id="sis-id">{I18n.t('SIS ID')}</Table.ColHeader>
}
if (this.props.inviteUsersURL) {
return (
<thead>
<tr>
<th scope="col">
<Table.Head>
<Table.Row>
<Table.ColHeader id="user-selection">
<ScreenReaderContent>{I18n.t('User Selection')}</ScreenReaderContent>
<Checkbox
id="missing_users_select_all"
@ -336,19 +336,21 @@ class MissingPeopleSection extends React.Component {
onChange={this.onSelectNewForMissingAll}
label={<ScreenReaderContent>{I18n.t('Check to select all')}</ScreenReaderContent>}
/>
</th>
<th scope="col">{I18n.t('Name')}</th>
<th scope="col">{I18n.t('Email Address')}</th>
</Table.ColHeader>
<Table.ColHeader id="name">{I18n.t('Name')}</Table.ColHeader>
<Table.ColHeader id="email">{I18n.t('Email Address')}</Table.ColHeader>
{idColHeader}
</tr>
</thead>
</Table.Row>
</Table.Head>
)
}
idColHeader = idColHeader || <th scope="col">{I18n.t('Email Address')}</th>
idColHeader = idColHeader || (
<Table.ColHeader id="email-id">{I18n.t('Email Address')}</Table.ColHeader>
)
return (
<thead>
<tr>{idColHeader}</tr>
</thead>
<Table.Head>
<Table.Row>{idColHeader}</Table.Row>
</Table.Head>
)
}
@ -360,15 +362,11 @@ class MissingPeopleSection extends React.Component {
caption={<ScreenReaderContent>{I18n.t('Unmatched login list')}</ScreenReaderContent>}
>
{this.renderTableHead()}
<tbody
ref={n => {
this.tbodyNode = n
}}
>
<Table.Body ref={this.tbodyNode}>
{this.props.searchType === 'cc_path'
? this.renderMissingEmail()
: this.renderMissingIds()}
</tbody>
</Table.Body>
</Table>
</div>
)

View File

@ -21,7 +21,7 @@ import React from 'react'
import PropTypes from 'prop-types'
import {personReadyToEnrollShape} from './shapes'
import {Alert} from '@instructure/ui-alerts'
import {Table} from '@instructure/ui-elements'
import {Table} from '@instructure/ui-table'
import {ScreenReaderContent} from '@instructure/ui-a11y'
class PeopleReadyList extends React.Component {
@ -52,26 +52,28 @@ class PeopleReadyList extends React.Component {
if (this.props.nameList.length > 0) {
userTable = (
<Table caption={<ScreenReaderContent>{I18n.t('User list')}</ScreenReaderContent>}>
<thead>
<tr>
<th>{I18n.t('Name')}</th>
<th>{I18n.t('Email Address')}</th>
<th>{I18n.t('Login ID')}</th>
{this.props.canReadSIS ? <th>{I18n.t('SIS ID')}</th> : null}
<th>{I18n.t('Institution')}</th>
</tr>
</thead>
<tbody>
{this.props.nameList.map((n, i) => (
<tr key={`${n.address}_${i}`}>
<th scope="row">{n.user_name}</th>
<td>{n.email}</td>
<td>{n.login_id || ''}</td>
{this.props.canReadSIS ? <td>{n.sis_user_id || ''}</td> : null}
<td>{n.account_name || this.props.defaultInstitutionName}</td>
</tr>
<Table.Head>
<Table.Row>
<Table.ColHeader id="usertable-name">{I18n.t('Name')}</Table.ColHeader>
<Table.ColHeader id="usertable-email">{I18n.t('Email Address')}</Table.ColHeader>
<Table.ColHeader id="usertable-loginid">{I18n.t('Login ID')}</Table.ColHeader>
{this.props.canReadSIS ? (
<Table.ColHeader id="usertable-sisid">{I18n.t('SIS ID')}</Table.ColHeader>
) : null}
<Table.ColHeader id="usertable-inst">{I18n.t('Institution')}</Table.ColHeader>
</Table.Row>
</Table.Head>
<Table.Body>
{this.props.nameList.map(n => (
<Table.Row key={n.address}>
<Table.RowHeader>{n.user_name}</Table.RowHeader>
<Table.Cell>{n.email}</Table.Cell>
<Table.Cell>{n.login_id || ''}</Table.Cell>
{this.props.canReadSIS ? <Table.Cell>{n.sis_user_id || ''}</Table.Cell> : null}
<Table.Cell>{n.account_name || this.props.defaultInstitutionName}</Table.Cell>
</Table.Row>
))}
</tbody>
</Table.Body>
</Table>
)
}

View File

@ -17,10 +17,10 @@
*/
import $ from 'jquery'
import {Table} from '@instructure/ui-elements'
import {Table} from '@instructure/ui-table'
import {ScreenReaderContent} from '@instructure/ui-a11y'
import React from 'react'
import PropTypes from 'prop-types'
import {arrayOf, bool, func, shape, string} from 'prop-types'
import I18n from 'i18n!react_developer_keys'
import DeveloperKey from './DeveloperKey'
@ -105,18 +105,24 @@ class DeveloperKeysTable extends React.Component {
caption={<ScreenReaderContent>{srcontent}</ScreenReaderContent>}
size="medium"
>
<thead>
<tr>
<th scope="col">{I18n.t('Name')}</th>
{!inherited && <th scope="col">{I18n.t('Owner Email')}</th>}
<th scope="col">{I18n.t('Details')}</th>
{!inherited && <th scope="col">{I18n.t('Stats')}</th>}
<th scope="col">{I18n.t('Type')}</th>
<th scope="col">{I18n.t('State')}</th>
{!inherited && <th scope="col">{I18n.t('Actions')}</th>}
</tr>
</thead>
<tbody>
<Table.Head>
<Table.Row>
<Table.ColHeader id="keystable-name">{I18n.t('Name')}</Table.ColHeader>
{!inherited && (
<Table.ColHeader id="keystable-owneremail">{I18n.t('Owner Email')}</Table.ColHeader>
)}
<Table.ColHeader id="keystable-details">{I18n.t('Details')}</Table.ColHeader>
{!inherited && (
<Table.ColHeader id="keystable-stats">{I18n.t('Stats')}</Table.ColHeader>
)}
<Table.ColHeader id="keystable-type">{I18n.t('Type')}</Table.ColHeader>
<Table.ColHeader id="keystable-state">{I18n.t('State')}</Table.ColHeader>
{!inherited && (
<Table.ColHeader id="keystable-actions">{I18n.t('Actions')}</Table.ColHeader>
)}
</Table.Row>
</Table.Head>
<Table.Body>
{this.props.developerKeysList.map(developerKey => (
<DeveloperKey
ref={key => {
@ -131,7 +137,7 @@ class DeveloperKeysTable extends React.Component {
onDelete={this.createSetFocusCallback}
/>
))}
</tbody>
</Table.Body>
</Table>
</div>
)
@ -139,18 +145,18 @@ class DeveloperKeysTable extends React.Component {
}
DeveloperKeysTable.propTypes = {
store: PropTypes.shape({
dispatch: PropTypes.func.isRequired
store: shape({
dispatch: func.isRequired
}).isRequired,
actions: PropTypes.shape({}).isRequired,
developerKeysList: PropTypes.arrayOf(DeveloperKey.propTypes.developerKey).isRequired,
ctx: PropTypes.shape({
params: PropTypes.shape({
contextId: PropTypes.string.isRequired
actions: shape({}).isRequired,
developerKeysList: arrayOf(DeveloperKey.propTypes.developerKey).isRequired,
ctx: shape({
params: shape({
contextId: string.isRequired
})
}).isRequired,
inherited: PropTypes.bool,
setFocus: PropTypes.func
inherited: bool,
setFocus: func
}
DeveloperKeysTable.defaultProps = {inherited: false, setFocus: () => {}}

View File

@ -21,19 +21,22 @@ import 'jquery.instructure_date_and_time'
import 'jqueryui/dialog'
import I18n from 'i18n!react_developer_keys'
import React from 'react'
import PropTypes from 'prop-types'
import {bool, func, number, shape, string} from 'prop-types'
import {Button, CloseButton} from '@instructure/ui-buttons'
import {Flex, View} from '@instructure/ui-layout'
import {IconLtiLine} from '@instructure/ui-icons'
import {Popover, Tooltip} from '@instructure/ui-overlays'
import {Img, Link} from '@instructure/ui-elements'
import {Table} from '@instructure/ui-table'
import {ScreenReaderContent} from '@instructure/ui-a11y'
import DeveloperKeyActionButtons from './ActionButtons'
import DeveloperKeyStateControl from './InheritanceStateControl'
class DeveloperKey extends React.Component {
static displayName = 'Row'
state = {showKey: false}
get isSiteAdmin() {
@ -115,7 +118,7 @@ class DeveloperKey extends React.Component {
handleDelete = () => this.props.onDelete(this.props.developerKey.id)
handleShowKey = () => {
this.setState({showKey: !this.state.showKey})
this.setState(state => ({showKey: !state.showKey}))
}
refActionButtons = link => {
@ -130,21 +133,21 @@ class DeveloperKey extends React.Component {
const {developerKey, inherited} = this.props
return (
<tr>
<td>
<Table.Row>
<Table.Cell>
<Flex>
{this.makeImage(developerKey)}
<Flex.Item shrink>{this.getToolName(developerKey)}</Flex.Item>
</Flex>
</td>
</Table.Cell>
{!inherited && (
<td style={{wordBreak: 'break-all'}} width="200px">
<Table.Cell style={{wordBreak: 'break-all'}}>
{this.makeUserLink(developerKey)}
</td>
</Table.Cell>
)}
<td>
<Table.Cell>
<View maxWidth="200px" as="div">
<div>{developerKey.id}</div>
{!inherited && (
@ -186,10 +189,10 @@ class DeveloperKey extends React.Component {
<div style={{wordBreak: 'break-all'}}>{this.redirectURI(developerKey)}</div>
)}
</View>
</td>
</Table.Cell>
{!inherited && (
<td>
<Table.Cell>
<div>
{I18n.t('Access Token Count: %{access_token_count}', {
access_token_count: developerKey.access_token_count
@ -201,9 +204,9 @@ class DeveloperKey extends React.Component {
})}
</div>
<div>{this.lastUsed(developerKey)}</div>
</td>
</Table.Cell>
)}
<td>
<Table.Cell>
{developerKey.is_lti_key ? (
<Tooltip
tip={I18n.t('Developer key is an external tool.')}
@ -214,8 +217,8 @@ class DeveloperKey extends React.Component {
</Button>
</Tooltip>
) : null}
</td>
<td>
</Table.Cell>
<Table.Cell>
<DeveloperKeyStateControl
ref={this.refToggleGroup}
developerKey={developerKey}
@ -223,9 +226,9 @@ class DeveloperKey extends React.Component {
actions={this.props.actions}
ctx={this.props.ctx}
/>
</td>
</Table.Cell>
{!inherited && (
<td>
<Table.Cell>
<DeveloperKeyActionButtons
ref={this.refActionButtons}
dispatch={this.props.store.dispatch}
@ -236,43 +239,44 @@ class DeveloperKey extends React.Component {
onDelete={this.handleDelete}
showVisibilityToggle={this.isSiteAdmin}
/>
</td>
</Table.Cell>
)}
</tr>
</Table.Row>
)
}
}
DeveloperKey.propTypes = {
store: PropTypes.shape({
dispatch: PropTypes.func.isRequired
store: shape({
dispatch: func.isRequired
}).isRequired,
actions: PropTypes.shape({
makeVisibleDeveloperKey: PropTypes.func.isRequired,
makeInvisibleDeveloperKey: PropTypes.func.isRequired,
activateDeveloperKey: PropTypes.func.isRequired,
deactivateDeveloperKey: PropTypes.func.isRequired,
deleteDeveloperKey: PropTypes.func.isRequired,
editDeveloperKey: PropTypes.func.isRequired,
developerKeysModalOpen: PropTypes.func.isRequired
actions: shape({
makeVisibleDeveloperKey: func.isRequired,
makeInvisibleDeveloperKey: func.isRequired,
activateDeveloperKey: func.isRequired,
deactivateDeveloperKey: func.isRequired,
deleteDeveloperKey: func.isRequired,
editDeveloperKey: func.isRequired,
developerKeysModalOpen: func.isRequired
}).isRequired,
developerKey: PropTypes.shape({
id: PropTypes.string.isRequired,
api_key: PropTypes.string,
created_at: PropTypes.string.isRequired,
visible: PropTypes.bool,
name: PropTypes.string,
user_id: PropTypes.string,
workflow_state: PropTypes.string,
is_lti_key: PropTypes.bool
developerKey: shape({
id: string.isRequired,
access_token_count: number.isRequired,
api_key: string,
created_at: string.isRequired,
visible: bool,
name: string,
user_id: string,
workflow_state: string,
is_lti_key: bool
}).isRequired,
ctx: PropTypes.shape({
params: PropTypes.shape({
contextId: PropTypes.string.isRequired
ctx: shape({
params: shape({
contextId: string.isRequired
})
}).isRequired,
inherited: PropTypes.bool,
onDelete: PropTypes.func.isRequired
inherited: bool,
onDelete: func.isRequired
}
DeveloperKey.defaultProps = {inherited: false}

View File

@ -24,7 +24,9 @@ import 'jquery.instructure_date_and_time'
import I18n from 'i18n!gradebook_history'
import {View} from '@instructure/ui-layout'
import {ScreenReaderContent} from '@instructure/ui-a11y'
import {Spinner, Table, Text} from '@instructure/ui-elements'
import {Text} from '@instructure/ui-elements'
import {Spinner} from '@instructure/ui-spinner'
import {Table} from '@instructure/ui-table'
import {getHistoryNextPage} from './actions/SearchResultsActions'
import SearchResultsRow from './SearchResultsRow'
@ -124,20 +126,18 @@ class SearchResultsComponent extends Component {
showResults = () => (
<div>
<Table caption={this.props.caption}>
<thead>
<tr>
<Table.Head>
<Table.Row>
{colHeaders.map(header => (
<th scope="col" key={`${header}-column`}>
{header}
</th>
<Table.ColHeader key={`${header}-column`}>{header}</Table.ColHeader>
))}
</tr>
</thead>
<tbody>
</Table.Row>
</Table.Head>
<Table.Body>
{this.props.historyItems.map(item => (
<SearchResultsRow key={`history-items-${item.id}`} item={item} />
))}
</tbody>
</Table.Body>
</Table>
</div>
)
@ -185,9 +185,6 @@ const mapDispatchToProps = dispatch => ({
}
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(SearchResultsComponent)
export default connect(mapStateToProps, mapDispatchToProps)(SearchResultsComponent)
export {SearchResultsComponent}

View File

@ -27,7 +27,10 @@ import I18n from 'i18n!gradebook_history'
import {IconOffLine} from '@instructure/ui-icons'
import {ScreenReaderContent} from '@instructure/ui-a11y'
import {Tooltip} from '@instructure/ui-overlays'
import {Table} from '@instructure/ui-table'
// Unclear on why that tab-index is there but not going to mess with it right now
/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
function anonymouslyGraded(gradedAnonymously) {
return gradedAnonymously ? (
<div>
@ -42,6 +45,7 @@ function anonymouslyGraded(gradedAnonymously) {
<ScreenReaderContent>{I18n.t('Not anonymously graded')}</ScreenReaderContent>
)
}
/* eslint-enable jsx-a11y/no-noninteractive-tabindex */
function displayGrade(grade, possible, displayAsPoints) {
// show the points possible if the assignment is set to display grades as
@ -84,18 +88,18 @@ function SearchResultsRow(props) {
} = props.item
return (
<tr>
<td>
<Table.Row>
<Table.Cell>
{$.datetimeString(new Date(date), {format: 'medium', timezone: environment.timezone()})}
</td>
<td>{anonymouslyGraded(gradedAnonymously)}</td>
<td>{displayStudentName(student, assignment)}</td>
<td>{grader || I18n.t('Not available')}</td>
<td>{assignment.name || I18n.t('Not available')}</td>
<td>{displayGrade(gradeBefore, pointsPossibleBefore, displayAsPoints)}</td>
<td>{displayGrade(gradeAfter, pointsPossibleAfter, displayAsPoints)}</td>
<td>{displayGrade(gradeCurrent, pointsPossibleCurrent, displayAsPoints)}</td>
</tr>
</Table.Cell>
<Table.Cell>{anonymouslyGraded(gradedAnonymously)}</Table.Cell>
<Table.Cell>{displayStudentName(student, assignment)}</Table.Cell>
<Table.Cell>{grader || I18n.t('Not available')}</Table.Cell>
<Table.Cell>{assignment.name || I18n.t('Not available')}</Table.Cell>
<Table.Cell>{displayGrade(gradeBefore, pointsPossibleBefore, displayAsPoints)}</Table.Cell>
<Table.Cell>{displayGrade(gradeAfter, pointsPossibleAfter, displayAsPoints)}</Table.Cell>
<Table.Cell>{displayGrade(gradeCurrent, pointsPossibleCurrent, displayAsPoints)}</Table.Cell>
</Table.Row>
)
}
@ -120,4 +124,6 @@ SearchResultsRow.propTypes = {
}).isRequired
}
SearchResultsRow.displayName = 'Row'
export default SearchResultsRow

View File

@ -1,108 +0,0 @@
/*
* Copyright (C) 2017 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react'
import {shallow} from 'enzyme'
import ActAsModal from 'jsx/actAs/ActAsModal'
import ActAsMask from 'jsx/actAs/ActAsMask'
import ActAsPanda from 'jsx/actAs/ActAsPanda'
import {Button} from '@instructure/ui-buttons'
import {Avatar, Table, Text, Spinner} from '@instructure/ui-elements'
QUnit.module('ActAsModal', {
setup() {
this.props = {
user: {
name: 'test user',
short_name: 'foo',
id: '5',
avatar_image_url: 'testImageUrl',
sortable_name: 'bar, baz',
email: 'testUser@test.com',
pseudonyms: [
{
login_id: 'qux',
sis_id: 555,
integration_id: 222
},
{
login_id: 'tic',
sis_id: 777,
integration_id: 888
}
]
}
}
}
})
test('it renders with panda svgs, user avatar, table, and proceed button present', function() {
const wrapper = shallow(<ActAsModal {...this.props} />)
const mask = wrapper.find(ActAsMask)
const panda = wrapper.find(ActAsPanda)
const button = wrapper.find(Button)
ok(mask.exists())
ok(panda.exists())
ok(button.exists())
})
test('it renders avatar with user image url', function() {
const wrapper = shallow(<ActAsModal {...this.props} />)
const avatar = wrapper.find(Avatar)
ok(avatar.exists())
equal(avatar.props().src, 'testImageUrl')
})
test('it renders the table with correct user information', function() {
const wrapper = shallow(<ActAsModal {...this.props} />)
const tables = wrapper.find(Table)
ok(tables.length === 3)
const textContent = []
tables.find('tr').forEach(row => {
row.find(Text).forEach(rowContent => {
textContent.push(rowContent.props().children)
})
})
const tableText = textContent.join(' ')
const user = this.props.user
ok(tableText.indexOf(user.name) > -1)
ok(tableText.indexOf(user.short_name) > -1)
ok(tableText.indexOf(user.sortable_name) > -1)
ok(tableText.indexOf(user.email) > -1)
user.pseudonyms.forEach(pseudonym => {
ok(tableText.indexOf(pseudonym.login_id) > -1)
ok(tableText.indexOf(pseudonym.sis_id) > -1)
ok(tableText.indexOf(pseudonym.integration_id) > -1)
})
})
test('it should only display loading spinner if state is loading', function(assert) {
const done = assert.async()
const wrapper = shallow(<ActAsModal {...this.props} />)
notOk(wrapper.find(Spinner).exists())
wrapper.setState({isLoading: true}, () => {
ok(wrapper.find(Spinner).exists())
done()
})
})

View File

@ -83,14 +83,14 @@ test('renders the table', () => {
equal(rows.length, 5, 'five rows')
const headings = rows[0].querySelectorAll('th')
equal(headings.length, 6, 'six column headings')
const createNewRow = duplicateSection.querySelector('tr.create-new')
const createNewRow = duplicateSection.querySelector('tr[data-testid="create-new"]')
ok(createNewRow, 'create new row exists')
const createUserBtn = createNewRow.querySelector('button')
ok(createUserBtn)
equal(createUserBtn.innerText, 'Create a new user for "addr1"')
const skipUserRow = duplicateSection.querySelector('tr.skip-addr')
const skipUserRow = duplicateSection.querySelector('tr[data-testid="skip-addr"]')
ok(skipUserRow, 'skip user row exists')
const skipUserBtn = skipUserRow.querySelector('button')
@ -171,6 +171,6 @@ test('cannot create a user', () => {
)
const duplicateSection = TestUtils.findRenderedDOMComponentWithClass(component, 'namelist')
const createNewRow = duplicateSection.querySelector('tr.create-new')
const createNewRow = duplicateSection.querySelector('tr[data-testid="create-new"]')
equal(createNewRow, null, 'create new user row does not exist')
})

View File

@ -18,7 +18,9 @@
import React from 'react'
import {mount, shallow} from 'enzyme'
import {Spinner, Table, Text} from '@instructure/ui-elements'
import {Text} from '@instructure/ui-elements'
import {Spinner} from '@instructure/ui-spinner'
import {Table} from '@instructure/ui-table'
import {SearchResultsComponent} from 'jsx/gradebook-history/SearchResults'
function defaultHistoryItems() {