Update RCE StatusBar
closes CORE-2965 CORE-2966 1. moves html-rich view toggle to the right 2. hides everything but the toggle when in html view 3. adds tooltips to the status bar buttons via a title attribute (so they look like the tooltips the toolbar buttons have) 4. removes the language select test plan: - load up an rce and see that ^those changes are reflected in what you see Change-Id: Iac1dd133a779789addca2e8db9af6f192c394b70 Reviewed-on: https://gerrit.instructure.com/194102 Reviewed-by: Ryan Shaw <ryan@instructure.com> QA-Review: Ryan Shaw <ryan@instructure.com> Tested-by: Jenkins Product-Review: Ed Schiebel <eschiebel@instructure.com>
This commit is contained in:
parent
fe01162306
commit
946c34b1c6
|
@ -44,7 +44,7 @@ function getProps(textareaId, state) {
|
|||
};
|
||||
},
|
||||
|
||||
textareaClassName: "exampleClassOne exampleClassTwo",
|
||||
textareaClassName: "exampleClassOne",
|
||||
textareaId,
|
||||
|
||||
trayProps: {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"test:cafe": "yarn build:cafe && yarn test:cafe:only",
|
||||
"test:cafe:only": "testcafe chrome testcafe/**/*.test.js",
|
||||
"test:cafe:all": "yarn build:cafe && testcafe chrome,firefox,safari testcafe/**/*.test.js",
|
||||
"test:cafe:debug": "testcafe --inspect-brk chrome testcafe/**/*.test.js",
|
||||
"test:watch": "BABEL_ENV=test-node mocha 'test/**/*.test.js' --require @instructure/ui-themes/lib/canvas --require @babel/register --watch",
|
||||
"test:coverage": "cross-env BABEL_ENV=test-node nyc -r html -r json node_modules/.bin/mocha 'test/**/*.test.js'",
|
||||
"debug": "BABEL_ENV=test-node inspect _mocha --no-timeouts --debug-brk 'test/**/*.test.js' --require @instructure/ui-themes/lib/canvas --require @babel/register",
|
||||
|
|
|
@ -138,7 +138,8 @@ class RCEWrapper extends React.Component {
|
|||
|
||||
this.state = {
|
||||
path: [],
|
||||
wordCount: 0
|
||||
wordCount: 0,
|
||||
isHtmlView: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,12 +288,10 @@ class RCEWrapper extends React.Component {
|
|||
|
||||
toggle = () => {
|
||||
if (this.isHidden()) {
|
||||
this.setCode(this.textareaValue());
|
||||
this.getTextarea().setAttribute('aria-hidden', true);
|
||||
this.setState({isHtmlView: false})
|
||||
} else {
|
||||
this.getTextarea().removeAttribute('aria-hidden');
|
||||
this.setState({isHtmlView: true})
|
||||
}
|
||||
this.onTinyMCEInstance("mceToggleEditor");
|
||||
}
|
||||
|
||||
focus() {
|
||||
|
@ -453,8 +452,18 @@ class RCEWrapper extends React.Component {
|
|||
this.registerTextareaChange();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
componentDidUpdate(_prevProps, prevState) {
|
||||
this.registerTextareaChange();
|
||||
if(prevState.isHtmlView !== this.state.isHtmlView) {
|
||||
if (this.state.isHtmlView) {
|
||||
this.getTextarea().removeAttribute('aria-hidden');
|
||||
this.mceInstance().hide()
|
||||
} else {
|
||||
this.setCode(this.textareaValue());
|
||||
this.getTextarea().setAttribute('aria-hidden', true);
|
||||
this.mceInstance().show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -481,6 +490,7 @@ class RCEWrapper extends React.Component {
|
|||
onToggleHtml={this.toggle}
|
||||
path={this.state.path}
|
||||
wordCount={this.state.wordCount}
|
||||
isHtmlView={this.state.isHtmlView}
|
||||
/>
|
||||
<CanvasContentTray bridge={Bridge} {...trayProps} />
|
||||
</div>
|
||||
|
|
|
@ -17,13 +17,12 @@
|
|||
*/
|
||||
|
||||
import React from 'react'
|
||||
import {arrayOf, func, number, string} from 'prop-types'
|
||||
import {arrayOf, bool, func, number, string} from 'prop-types'
|
||||
import {StyleSheet, css} from 'aphrodite'
|
||||
import { Button } from '@instructure/ui-buttons'
|
||||
import { Flex, View } from '@instructure/ui-layout'
|
||||
import { ScreenReaderContent } from '@instructure/ui-a11y'
|
||||
import { Text } from '@instructure/ui-elements'
|
||||
import { Select } from '@instructure/ui-forms'
|
||||
import {SVGIcon} from '@instructure/ui-svg-images'
|
||||
import { IconA11yLine, IconKeyboardShortcutsLine, IconMiniArrowEndLine } from '@instructure/ui-icons'
|
||||
import formatMessage from '../format-message'
|
||||
|
@ -31,7 +30,8 @@ import formatMessage from '../format-message'
|
|||
StatusBar.propTypes = {
|
||||
onToggleHtml: func,
|
||||
path: arrayOf(string),
|
||||
wordCount: number
|
||||
wordCount: number,
|
||||
isHtmlView: bool
|
||||
}
|
||||
|
||||
function renderPath({path}) {
|
||||
|
@ -59,53 +59,60 @@ function emptyTagIcon() {
|
|||
)
|
||||
}
|
||||
export default function StatusBar(props) {
|
||||
return (
|
||||
<div>
|
||||
<Flex margin="x-small">
|
||||
if (props.isHtmlView) {
|
||||
const toggleToRich = formatMessage('Switch to rich text view')
|
||||
return (
|
||||
<View display="block" margin="x-small" textAlign="end" data-testid="RCEStatusBar">
|
||||
<Button variant="link" icon={emptyTagIcon()} onClick={props.onToggleHtml} title={toggleToRich}>
|
||||
<ScreenReaderContent>{toggleToRich}</ScreenReaderContent>
|
||||
</Button>
|
||||
</View>
|
||||
)
|
||||
} else {
|
||||
const kbshortcut = formatMessage('View keyboard shortcuts')
|
||||
const a11y = formatMessage('Accessibility')
|
||||
const wordCount = formatMessage(`{count, plural,
|
||||
=0 {0 words}
|
||||
one {1 word}
|
||||
other {# words}
|
||||
}`, {count: props.wordCount})
|
||||
const toggleToHtml = formatMessage('Switch to raw html view')
|
||||
|
||||
return (
|
||||
<Flex margin="x-small" data-testid="RCEStatusBar">
|
||||
<Flex.Item grow>
|
||||
<View>{renderPath(props)}</View>
|
||||
</Flex.Item>
|
||||
|
||||
<Flex.Item>
|
||||
<View display="inline-block" padding="0 x-small">
|
||||
<Button variant="link" icon={IconKeyboardShortcutsLine}>
|
||||
<ScreenReaderContent>{formatMessage('View keyboard shortcuts')}</ScreenReaderContent>
|
||||
<Button variant="link" icon={IconKeyboardShortcutsLine} title={kbshortcut}>
|
||||
<ScreenReaderContent>{kbshortcut}</ScreenReaderContent>
|
||||
</Button>
|
||||
<Button variant="link" icon={IconA11yLine}>
|
||||
<ScreenReaderContent>{formatMessage('Accessibility')}</ScreenReaderContent>
|
||||
<Button variant="link" icon={IconA11yLine} title={a11y}>
|
||||
<ScreenReaderContent>{a11y}</ScreenReaderContent>
|
||||
</Button>
|
||||
</View>
|
||||
<div className={css(styles.separator)}/>
|
||||
</Flex.Item>
|
||||
|
||||
<Flex.Item>
|
||||
<View display="inline-block" padding="0 x-small">
|
||||
<Button variant="link" icon={emptyTagIcon()} onClick={props.onToggleHtml}>
|
||||
<ScreenReaderContent>{formatMessage('Toggle raw html view')}</ScreenReaderContent>
|
||||
</Button>
|
||||
</View>
|
||||
<div className={css(styles.separator)}/>
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<View display="inline-block" padding="0 x-small xxx-small x-small">
|
||||
<Select
|
||||
inline
|
||||
width="12rem"
|
||||
size="small"
|
||||
label={<ScreenReaderContent>Select a language</ScreenReaderContent>}
|
||||
>
|
||||
<option value="en-us">English (US)</option>
|
||||
<option value="fr">French</option>
|
||||
</Select>
|
||||
<View display="inline-block" padding="0 small xx-small small">
|
||||
<Text>{wordCount}</Text>
|
||||
</View>
|
||||
<div className={css(styles.separator)}/>
|
||||
</Flex.Item>
|
||||
|
||||
<Flex.Item>
|
||||
<View display="inline-block" padding="0 0 0 small">
|
||||
<Text>{formatMessage('{count} words', {count: props.wordCount})}</Text>
|
||||
<Button variant="link" icon={emptyTagIcon()} onClick={props.onToggleHtml} title={toggleToHtml}>
|
||||
<ScreenReaderContent>{toggleToHtml}</ScreenReaderContent>
|
||||
</Button>
|
||||
</View>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
|
|
@ -149,13 +149,6 @@ describe("RCEWrapper", () => {
|
|||
element = createBasicElement();
|
||||
});
|
||||
|
||||
it("calls toggle on its tinyMCE instance", () => {
|
||||
element.toggle();
|
||||
assert(
|
||||
execCommandSpy.withArgs("mceToggleEditor", false, textareaId).called
|
||||
);
|
||||
});
|
||||
|
||||
it("syncs content during toggle if coming back from hidden instance", () => {
|
||||
element = createdMountedElement().getMountedInstance();
|
||||
editor.hidden = true;
|
||||
|
|
|
@ -28,19 +28,31 @@ test('toggles between rce and html views', async t => {
|
|||
const textarea = Selector('#textarea')
|
||||
const rceContainer = Selector('.tox-tinymce')
|
||||
const toggleButton = Selector('button').withText('</>')
|
||||
const wordCount = Selector('span').withText('0 words').nth(-1)
|
||||
await t.expect(rceContainer.visible).ok('rce should be initially visible')
|
||||
await t.expect(wordCount.count).eql(1)
|
||||
await t.expect(textarea.visible).notOk('textarea should be initially invisible')
|
||||
await t.click(toggleButton)
|
||||
await t.expect(rceContainer.visible).notOk('rce should be invisible after toggle')
|
||||
await t.expect(wordCount.count).eql(0)
|
||||
await t.expect(textarea.visible).ok('textarea should be visible after toggle')
|
||||
await t.click(toggleButton)
|
||||
await t.expect(rceContainer.visible).ok('rce should be visible after toggling again')
|
||||
await t.expect(wordCount.count).eql(1)
|
||||
await t.expect(textarea.visible).notOk('textarea should be hidden after toggling again')
|
||||
})
|
||||
|
||||
test('counts words', async t => {
|
||||
// search for the exact text for the selector will wait for it to change to this text
|
||||
await t.switchToIframe(tinyIframe).typeText('body', 'foo bar baz bing')
|
||||
await t.expect(Selector('span').withText('0 words')).exists
|
||||
|
||||
await t.switchToIframe(tinyIframe).typeText('body', 'foo')
|
||||
await t
|
||||
.switchToMainWindow()
|
||||
.expect(Selector('span').withText('1 word').exists)
|
||||
.ok()
|
||||
|
||||
await t.switchToIframe(tinyIframe).typeText('body', ' bar baz bing')
|
||||
await t
|
||||
.switchToMainWindow()
|
||||
.expect(Selector('span').withText('4 words').exists)
|
||||
|
|
Loading…
Reference in New Issue