Introduce Storybook to Canvas + i18N Canvas Inbox fixes VICE-947

This story contains work to move Canvas Inbox and Storybook to `app/jsx` inside our Monolith. Storybook should now be enabled for all of `app/jsx` and works with our existing I18n code after updating webpack to load translations correctly.

Test Plan
1. Install node modules with `yarn`
2. At Canvas-LMS root, run `yarn storybook`
3. Storybook should open, test that Canvas Inbox components work as expected

Change-Id: Iccd2b103164a2da4e37b5746f4d9d2b5faf5f29d
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/253024
QA-Review: Davis Hyer <dhyer@instructure.com>
Product-Review: Davis Hyer <dhyer@instructure.com>
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Davis Hyer <dhyer@instructure.com>
This commit is contained in:
Jeffrey Johnson 2020-11-17 11:27:40 -08:00
parent 1240475b49
commit 408999d667
47 changed files with 170 additions and 128 deletions

1
.gitignore vendored
View File

@ -37,6 +37,7 @@ npm-debug.log
/public/dist/
/public/doc/api/
/public/javascripts/translations/
/storybook-static/
/tmp/*
!/tmp/.keep
/vendor/bundle

40
.storybook/main.js Normal file
View File

@ -0,0 +1,40 @@
const I18nPlugin = require('../frontend_build/i18nPlugin')
const path = require('path')
const baseWebpackConfig = require('../frontend_build/baseWebpackConfig')
const CompiledReferencePlugin = require('../frontend_build/CompiledReferencePlugin')
const root = path.resolve(__dirname, '..')
module.exports = {
"stories": [
"../app/jsx/**/*.stories.mdx",
"../app/jsx/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials"
],
"webpackFinal": async (config, { configType }) => {
config.module.noParse = [/i18nliner\/dist\/lib\/i18nliner/]
config.plugins.push(
new I18nPlugin()
);
config.plugins.push(
new CompiledReferencePlugin(),
);
config.resolveLoader.modules = [
path.resolve(__dirname, '../public/javascripts/'),
path.resolve(__dirname, '../app/coffeescripts/'),
path.resolve(__dirname, '../frontend_build/'),
'node_modules'
]
config.resolve.modules = [
path.resolve(__dirname, '../public/javascripts/'),
path.resolve(__dirname, '../app/coffeescripts'),
path.resolve(__dirname, '../frontend_build/'),
'node_modules'
]
config.resolve.alias['coffeescripts/str/i18nLolcalize.js'] = path.resolve(__dirname, '../app/coffeescripts/str/i18nLolcalize.js')
return config;
}
}

View File

@ -0,0 +1 @@
<div id="screenreader_alert_holder" role="alert" aria-live="assertive" aria-relevant="additions text" aria-atomic="false"></div>

5
.storybook/preview.js Normal file
View File

@ -0,0 +1,5 @@
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
}
import '@instructure/canvas-theme'

View File

@ -23,19 +23,17 @@ import {Flex} from '@instructure/ui-flex'
import {Text} from '@instructure/ui-text'
import {TruncateText} from '@instructure/ui-truncate-text'
import {RemovableItem} from '../RemovableItem/RemovableItem'
// import {t} from 'i18n!conversations'
// TODO: replace with frd translation function
const t = str => str
import I18n from 'i18n!conversations_2'
export const Attachment = ({...props}) => {
let attachmentInput = null
const handleAttachment = () => attachmentInput?.click()
const attachmentDisplayName = props.attachment.displayName
return (
<RemovableItem
onRemove={props.onDelete}
screenReaderLabel={t('Remove Attachment')}
// TODO: handle translation arguments when i18n frd works
childrenAriaLabel={t(`Replace ${props.attachment.displayName} button`)}
screenReaderLabel={I18n.t('Remove Attachment')}
childrenAriaLabel={I18n.t(`Replace %{attachmentDisplayName} button`, {attachmentDisplayName})}
>
<Flex
direction="column"

View File

@ -21,7 +21,7 @@ import React from 'react'
import {AttachmentDisplay} from './AttachmentDisplay'
export default {
title: 'Playground/AttachmentDisplay',
title: 'Examples/Canvas Inbox/AttachmentDisplay',
component: AttachmentDisplay,
argTypes: {
onReplaceItem: {action: 'Replace'},

View File

@ -22,9 +22,7 @@ import {Button, IconButton} from '@instructure/ui-buttons'
import {IconAttachMediaLine, IconPaperclipLine} from '@instructure/ui-icons'
import {Tooltip} from '@instructure/ui-tooltip'
import {Flex} from '@instructure/ui-flex'
// import {t} from 'i18n!conversations'
// TODO: replace with frd translation function
const t = str => str
import I18n from 'i18n!conversations_2'
export const ComposeActionButtons = ({...props}) => {
return (
@ -40,9 +38,9 @@ const renderUploadButtons = props => {
const handleAttachmentClick = () => attachmentInput?.click()
return (
<>
<Tooltip renderTip={t('Add an attachment')} placement="top">
<Tooltip renderTip={I18n.t('Add an attachment')} placement="top">
<IconButton
screenReaderLabel={t('Add an attachment')}
screenReaderLabel={I18n.t('Add an attachment')}
onClick={handleAttachmentClick}
margin="xx-small"
data-testid="attachment-upload"
@ -59,9 +57,9 @@ const renderUploadButtons = props => {
onChange={props.onAttachmentUpload}
/>
{props.onMediaUpload && (
<Tooltip renderTip={t('Record an audio or video comment')} placement="top">
<Tooltip renderTip={I18n.t('Record an audio or video comment')} placement="top">
<IconButton
screenReaderLabel={t('Record an audio or video comment')}
screenReaderLabel={I18n.t('Record an audio or video comment')}
onClick={props.onMediaUpload}
margin="xx-small"
data-testid="media-upload"
@ -83,7 +81,7 @@ const renderMessageButtons = props => {
onClick={props.onCancel}
data-testid="cancel-button"
>
{t('Cancel')}
{I18n.t('Cancel')}
</Button>
<Button
type="submit"
@ -92,7 +90,7 @@ const renderMessageButtons = props => {
onClick={props.onSend}
data-testid="send-button"
>
{props.isSending ? t('Sending...') : t('Send')}
{props.isSending ? I18n.t('Sending...') : I18n.t('Send')}
</Button>
</>
)

View File

@ -21,7 +21,7 @@ import React from 'react'
import {ComposeActionButtons} from './ComposeActionButtons'
export default {
title: 'Playground/ComposeActionButtons',
title: 'Examples/Canvas Inbox/ComposeActionButtons',
component: ComposeActionButtons,
argTypes: {
onAttachmentUpload: {action: 'Attachment'},

View File

@ -22,9 +22,7 @@ import React from 'react'
import {Alert} from '@instructure/ui-alerts'
import {ScreenReaderContent} from '@instructure/ui-a11y-content'
import {Select} from '@instructure/ui-select'
// import {t} from 'i18n!conversations'
// TODO: replace with frd translation function
const t = str => str
import I18n from 'i18n!conversations_2'
export class CourseSelect extends React.Component {
static propTypes = {
@ -91,8 +89,12 @@ export class CourseSelect extends React.Component {
const newOptionGroup = this.getOptionGroup(newOption)
const isNewGroup = !currentOption || currentOptionGroup !== newOptionGroup
const newOptionContextName = newOption.contextName
const message = isNewGroup
? t(`Group ${newOptionGroup} entered. ${newOption.contextName}`)
? I18n.t('Group %{newOptionGroup} entered. %{newOptionContextName}', {
newOptionContextName,
newOptionGroup
})
: newOption.contextName
return message
}
@ -130,13 +132,14 @@ export class CourseSelect extends React.Component {
handleSelectOption = (event, {id}) => {
const option = this.getOptionById(id)
const contextName = option.contextName
if (!option) return // prevent selecting of empty options
this.setState({
selectedOptionId: id,
inputValue: option.contextName,
isShowingOptions: false,
filteredOptions: this.props.options,
announcement: t(`${option.contextName} selected`)
announcement: I18n.t('%{contextName} selected', {contextName})
})
}
@ -168,13 +171,13 @@ export class CourseSelect extends React.Component {
getGroupLabel = groupKey => {
switch (groupKey) {
case 'favoriteCourses':
return t('Favorite Courses')
return I18n.t('Favorite Courses')
case 'moreCourses':
return t('More Courses')
return I18n.t('More Courses')
case 'concludedCourses':
return t('Concluded Courses')
return I18n.t('Concluded Courses')
case 'groups':
return t('Groups')
return I18n.t('Groups')
}
}
@ -207,11 +210,11 @@ export class CourseSelect extends React.Component {
<Select
renderLabel={
<ScreenReaderContent>
{this.props.mainPage ? t('Filter messages by course') : t('Select course')}
{this.props.mainPage ? I18n.t('Filter messages by course') : I18n.t('Select course')}
</ScreenReaderContent>
}
assistiveText={t('Type or use arrow keys to navigate options')}
placeholder={this.props.mainPage ? t('All Courses') : t('Select Course')}
assistiveText={I18n.t('Type or use arrow keys to navigate options')}
placeholder={this.props.mainPage ? I18n.t('All Courses') : I18n.t('Select Course')}
inputValue={inputValue}
isShowingOptions={isShowingOptions}
onBlur={this.handleBlur}
@ -225,7 +228,7 @@ export class CourseSelect extends React.Component {
{this.renderGroups()}
</Select>
<Alert
liveRegion={() => document.getElementById('canvas_inbox_screenreader_holder')}
liveRegion={() => document.getElementById('screenreader_alert_holder')}
liveRegionPoliteness="assertive"
screenReaderOnly
>

View File

@ -20,7 +20,7 @@ import React from 'react'
import {CourseSelect} from './CourseSelect'
export default {
title: 'Playground/Course Select',
title: 'Examples/Canvas Inbox/Course Select',
component: CourseSelect
}

View File

@ -50,7 +50,7 @@ const createProps = overrides => {
beforeEach(() => {
const liveRegion = document.createElement('DIV')
liveRegion.setAttribute('id', 'canvas_inbox_screenreader_holder')
liveRegion.setAttribute('id', 'screenreader_alert_holder')
liveRegion.setAttribute('role', 'alert')
document.body.appendChild(liveRegion)
})

View File

@ -19,12 +19,15 @@
import React from 'react'
import PropTypes from 'prop-types'
import {Checkbox} from '@instructure/ui-checkbox'
const t = str => str
import I18n from 'i18n!conversations_2'
export const IndividualMessageCheckbox = props => {
return (
<Checkbox label={t('Send an individual message to each recipient')} size="small" {...props} />
<Checkbox
label={I18n.t('Send an individual message to each recipient')}
size="small"
{...props}
/>
)
}

View File

@ -21,7 +21,7 @@ import React from 'react'
import {IndividualMessageCheckbox} from './IndividualMessageCheckbox'
export default {
title: 'Playground/Individual Message Checkbox',
title: 'Examples/Canvas Inbox/Individual Message Checkbox',
component: IndividualMessageCheckbox,
argTypes: {
checked: {control: 'boolean'},

View File

@ -20,23 +20,21 @@ import PropTypes from 'prop-types'
import React from 'react'
import {ScreenReaderContent} from '@instructure/ui-a11y-content'
import {SimpleSelect} from '@instructure/ui-simple-select'
// import {t} from 'i18n!conversations'
// TODO: replace with frd translation function
const t = str => str
import I18n from 'i18n!conversations_2'
export const mailboxLabels = {
inbox: () => t('Inbox'),
unread: () => t('Unread'),
starred: () => t('Starred'),
sent: () => t('Sent'),
archived: () => t('Archived'),
submission_comments: () => t('Submission Comments')
inbox: () => I18n.t('Inbox'),
unread: () => I18n.t('Unread'),
starred: () => I18n.t('Starred'),
sent: () => I18n.t('Sent'),
archived: () => I18n.t('Archived'),
submission_comments: () => I18n.t('Submission Comments')
}
export const MailboxSelectionDropdown = ({...props}) => {
return (
<SimpleSelect
renderLabel={() => <ScreenReaderContent>{t('Mailbox Selection')}</ScreenReaderContent>}
renderLabel={() => <ScreenReaderContent>{I18n.t('Mailbox Selection')}</ScreenReaderContent>}
onChange={(_event, data) => props.onSelect(data.value)}
value={props.activeMailbox}
size="small"

View File

@ -21,7 +21,7 @@ import React from 'react'
import {MailboxSelectionDropdown} from './MailboxSelectionDropdown'
export default {
title: 'Playground/MailboxSelectionDropdown',
title: 'Examples/Canvas Inbox/MailboxSelectionDropdown',
component: MailboxSelectionDropdown,
argTypes: {
onSelect: {action: 'Changed'}

View File

@ -24,28 +24,27 @@ import PropTypes from 'prop-types'
import React from 'react'
import {ScreenReaderContent} from '@instructure/ui-a11y-content'
import {Tabs} from '@instructure/ui-tabs'
// TODO: replace with frd translation function
const t = str => str
import I18n from 'i18n!conversations_2'
const translations = {
ARIA_VIDEO_LABEL: () => t('Video Player'),
ARIA_VOLUME: () => t('Current Volume Level'),
ARIA_RECORDING: () => t('Recording'),
DEFAULT_ERROR: () => t('Something went wrong accessing your mic or webcam.'),
DEVICE_AUDIO: () => t('Mic'),
DEVICE_VIDEO: () => t('Webcam'),
FILE_PLACEHOLDER: () => t('Untitled'),
FINISH: () => t('Finish'),
NO_WEBCAM: () => t('No Video'),
NOT_ALLOWED_ERROR: () => t('Please allow Canvas to access your microphone and webcam.'),
NOT_READABLE_ERROR: () => t('Your webcam may already be in use.'),
PLAYBACK_PAUSE: () => t('Pause'),
PLAYBACK_PLAY: () => t('Play'),
PREVIEW: () => t('PREVIEW'),
SAVE: () => t('Save'),
SR_FILE_INPUT: () => t('File name'),
START: () => t('Start Recording'),
START_OVER: () => t('Start Over')
ARIA_VIDEO_LABEL: () => I18n.t('Video Player'),
ARIA_VOLUME: () => I18n.t('Current Volume Level'),
ARIA_RECORDING: () => I18n.t('Recording'),
DEFAULT_ERROR: () => I18n.t('Something went wrong accessing your mic or webcam.'),
DEVICE_AUDIO: () => I18n.t('Mic'),
DEVICE_VIDEO: () => I18n.t('Webcam'),
FILE_PLACEHOLDER: () => I18n.t('Untitled'),
FINISH: () => I18n.t('Finish'),
NO_WEBCAM: () => I18n.t('No Video'),
NOT_ALLOWED_ERROR: () => I18n.t('Please allow Canvas to access your microphone and webcam.'),
NOT_READABLE_ERROR: () => I18n.t('Your webcam may already be in use.'),
PLAYBACK_PAUSE: () => I18n.t('Pause'),
PLAYBACK_PLAY: () => I18n.t('Play'),
PREVIEW: () => I18n.t('PREVIEW'),
SAVE: () => I18n.t('Save'),
SR_FILE_INPUT: () => I18n.t('File name'),
START: () => I18n.t('Start Recording'),
START_OVER: () => I18n.t('Start Over')
}
// Function for applying a function to each value of an object, returning a new object
@ -63,7 +62,7 @@ export function MediaUploadModal({onClose, onFileUpload, onOpen, onRecordingSave
return (
<Modal
label={t('Record/Upload Media Comment')}
label={I18n.t('Record/Upload Media Comment')}
onClose={onClose}
onOpen={onOpen}
open={open}
@ -71,18 +70,18 @@ export function MediaUploadModal({onClose, onFileUpload, onOpen, onRecordingSave
shouldCloseOnDocumentClick
>
<Modal.Header>
<Heading>{t('Record/Upload Media Comment')}</Heading>
<Heading>{I18n.t('Record/Upload Media Comment')}</Heading>
<CloseButton
data-test="CloseBtn"
placement="end"
offset="small"
onClick={onClose}
screenReaderLabel={t('Close')}
screenReaderLabel={I18n.t('Close')}
/>
</Modal.Header>
<Modal.Body>
<Tabs onRequestTabChange={handleTabChange}>
<Tabs.Panel renderTitle={t('Record Media')} isSelected={selectedTab === 0}>
<Tabs.Panel renderTitle={I18n.t('Record Media')} isSelected={selectedTab === 0}>
{canUseMediaCapture() && (
<MediaCapture
onCompleted={onRecordingSave}
@ -90,9 +89,9 @@ export function MediaUploadModal({onClose, onFileUpload, onOpen, onRecordingSave
/>
)}
</Tabs.Panel>
<Tabs.Panel renderTitle={t('Upload Media')} isSelected={selectedTab === 1}>
<Tabs.Panel renderTitle={I18n.t('Upload Media')} isSelected={selectedTab === 1}>
<label htmlFor="media-upload-file-input">
<ScreenReaderContent>{t('File Upload')}</ScreenReaderContent>
<ScreenReaderContent>{I18n.t('File Upload')}</ScreenReaderContent>
</label>
<input
id="media-upload-file-input"
@ -108,13 +107,13 @@ export function MediaUploadModal({onClose, onFileUpload, onOpen, onRecordingSave
onClick={() => hiddenFileInput.current.click()}
renderIcon={<IconAudioSolid />}
>
{t('Select Audio File')}
{I18n.t('Select Audio File')}
</Button>
<Button
onClick={() => hiddenFileInput.current.click()}
renderIcon={<IconVideoCameraSolid />}
>
{t('Select Video File')}
{I18n.t('Select Video File')}
</Button>
</Tabs.Panel>
</Tabs>

View File

@ -21,7 +21,7 @@ import React from 'react'
import {MediaUploadModal} from './MediaUploadModal'
export default {
title: 'Playground/MediaUploadModal',
title: 'Examples/Canvas Inbox/MediaUploadModal',
component: MediaUploadModal,
argTypes: {
onClose: {action: 'onClose'},

View File

@ -31,15 +31,13 @@ import {
} from '@instructure/ui-icons'
import {Menu} from '@instructure/ui-menu'
import {Tooltip} from '@instructure/ui-tooltip'
// import {t} from 'i18n!conversations'
// TODO: replace with frd translation function
const t = str => str
import I18n from 'i18n!conversations_2'
const Settings = props => (
<Menu
placement="bottom"
trigger={
<Tooltip renderTip={t('More options')} placement="top">
<Tooltip renderTip={I18n.t('More options')} placement="top">
<Button
renderIcon={IconSettingsLine}
disabled={props.settingsDisabled}
@ -52,13 +50,13 @@ const Settings = props => (
disabled={props.settingsDisabled}
>
<Menu.Item value="MarkAsUnread" onSelect={() => props.markAsUnread()}>
{t('Mark as unread')}
{I18n.t('Mark as unread')}
</Menu.Item>
<Menu.Item value="Forward" onSelect={() => props.forward()}>
{t('Forward')}
{I18n.t('Forward')}
</Menu.Item>
<Menu.Item value="Star" onSelect={() => props.star()}>
{t('Star')}
{I18n.t('Star')}
</Menu.Item>
</Menu>
)
@ -79,7 +77,7 @@ export const MessageActionButtons = props => {
if (props.isSubmissionComment) {
return (
<ActionButton
tip={t('Reply')}
tip={I18n.t('Reply')}
icon={IconReplyLine}
onClick={props.reply}
disabled={props.replyDisabled}
@ -91,34 +89,34 @@ export const MessageActionButtons = props => {
return (
<>
<ActionButton
tip={t('Compose a new message')}
tip={I18n.t('Compose a new message')}
icon={IconComposeLine}
onClick={props.compose}
testid="compose"
/>
<ActionButton
tip={t('Reply')}
tip={I18n.t('Reply')}
icon={IconReplyLine}
onClick={props.reply}
disabled={props.replyDisabled}
testid="reply"
/>
<ActionButton
tip={t('Reply all')}
tip={I18n.t('Reply all')}
icon={IconReplyAll2Line}
onClick={props.replyAll}
disabled={props.replyDisabled}
testid="reply-all"
/>
<ActionButton
tip={t('Archive')}
tip={I18n.t('Archive')}
icon={IconCollectionSaveLine}
onClick={props.archive}
disabled={props.archiveDisabled}
testid="archive"
/>
<ActionButton
tip={t('Delete')}
tip={I18n.t('Delete')}
icon={IconTrashLine}
onClick={props.delete}
disabled={props.deleteDisabled}

View File

@ -20,7 +20,7 @@ import React from 'react'
import {MessageActionButtons} from './MessageActionButtons'
export default {
title: 'Playground/Message Action Buttons',
title: 'Examples/Canvas Inbox/Message Action Buttons',
component: MessageActionButtons,
argTypes: {
compose: {action: 'compose'},

View File

@ -21,9 +21,7 @@ import React, {useState} from 'react'
import {ScreenReaderContent} from '@instructure/ui-a11y-content'
import {TextArea} from '@instructure/ui-text-area'
// import {t} from 'i18n!conversations'
// TODO: replace with frd translation function
const t = str => str
import I18n from 'i18n!conversations_2'
export const MessageBody = props => {
const [body, setBody] = useState('')
@ -35,7 +33,7 @@ export const MessageBody = props => {
return (
<TextArea
label={<ScreenReaderContent>{t('Message Body')}</ScreenReaderContent>}
label={<ScreenReaderContent>{I18n.t('Message Body')}</ScreenReaderContent>}
messages={props.messages}
required
autoGrow={false}

View File

@ -20,7 +20,7 @@ import React from 'react'
import {MessageBody} from './MessageBody'
export default {
title: 'Playground/Message Body',
title: 'Examples/Canvas Inbox/Message Body',
component: MessageBody,
argTypes: {
onBodyChange: {action: 'changed'}

View File

@ -25,9 +25,7 @@ import {Heading} from '@instructure/ui-heading'
import {IconMiniArrowDownLine, IconReplyLine, IconSettingsLine} from '@instructure/ui-icons'
import {Menu} from '@instructure/ui-menu'
import {Tooltip} from '@instructure/ui-tooltip'
// import {t} from 'i18n!conversations'
// TODO: replace with frd translation function
const t = str => str
import I18n from 'i18n!conversations_2'
export const MessageDetailHeader = ({...props}) => {
return (
@ -36,10 +34,10 @@ export const MessageDetailHeader = ({...props}) => {
<Heading>{props.text}</Heading>
</Flex.Item>
<Flex.Item>
<Tooltip renderTip={t('Reply')} on={['hover', 'focus']}>
<Tooltip renderTip={I18n.t('Reply')} on={['hover', 'focus']}>
<IconButton
margin="0 x-small 0 0"
screenReaderLabel={t('Reply')}
screenReaderLabel={I18n.t('Reply')}
onClick={() => props.handleOptionSelect('reply')}
>
<IconReplyLine />
@ -53,18 +51,18 @@ export const MessageDetailHeader = ({...props}) => {
props.handleOptionSelect(value)
}}
trigger={
<Tooltip renderTip={t('More options')} on={['hover', 'focus']}>
<Tooltip renderTip={I18n.t('More options')} on={['hover', 'focus']}>
<Button margin="0 x-small 0 0" renderIcon={IconSettingsLine}>
<ScreenReaderContent>{t('More options')}</ScreenReaderContent>
<ScreenReaderContent>{I18n.t('More options')}</ScreenReaderContent>
<IconMiniArrowDownLine />
</Button>
</Tooltip>
}
>
<Menu.Item value="reply-all">{t('Reply All')}</Menu.Item>
<Menu.Item value="forward">{t('Forward')}</Menu.Item>
<Menu.Item value="star">{t('Star')}</Menu.Item>
<Menu.Item value="delete">{t('Delete')}</Menu.Item>
<Menu.Item value="reply-all">{I18n.t('Reply All')}</Menu.Item>
<Menu.Item value="forward">{I18n.t('Forward')}</Menu.Item>
<Menu.Item value="star">{I18n.t('Star')}</Menu.Item>
<Menu.Item value="delete">{I18n.t('Delete')}</Menu.Item>
</Menu>
</Flex.Item>
<Flex.Item></Flex.Item>

View File

@ -21,7 +21,7 @@ import React from 'react'
import {MessageDetailHeader} from './MessageDetailHeader'
export default {
title: 'Playground/MessageDetailHeader',
title: 'Examples/Canvas Inbox/MessageDetailHeader',
component: MessageDetailHeader,
argTypes: {
handleOptionSelect: {action: 'handleOptionSelect'}

View File

@ -28,9 +28,7 @@ import {Text} from '@instructure/ui-text'
import {Tooltip} from '@instructure/ui-tooltip'
import {TruncateText} from '@instructure/ui-truncate-text'
import {View} from '@instructure/ui-view'
// import {t} from 'i18n!conversations'
// TODO: replace with frd translation function
const t = str => str
import I18n from 'i18n!conversations_2'
export const MessageDetailItem = ({...props}) => {
const formatParticipants = () => {
@ -53,11 +51,11 @@ export const MessageDetailItem = ({...props}) => {
const renderActionButtons = () => {
return (
<>
<Tooltip renderTip={t('Reply')} on={['hover', 'focus']}>
<Tooltip renderTip={I18n.t('Reply')} on={['hover', 'focus']}>
<IconButton
size="small"
margin="0 x-small 0 0"
screenReaderLabel={t('Reply')}
screenReaderLabel={I18n.t('Reply')}
onClick={() => props.handleOptionSelect('reply')}
>
<IconReplyLine />
@ -69,17 +67,17 @@ export const MessageDetailItem = ({...props}) => {
props.handleOptionSelect(value)
}}
trigger={
<Tooltip renderTip={t('More options')} on={['hover', 'focus']}>
<Tooltip renderTip={I18n.t('More options')} on={['hover', 'focus']}>
<Button margin="0 x-small 0 0" size="small" renderIcon={IconSettingsLine}>
<ScreenReaderContent>{t('More options')}</ScreenReaderContent>
<ScreenReaderContent>{I18n.t('More options')}</ScreenReaderContent>
<IconMiniArrowDownLine />
</Button>
</Tooltip>
}
>
<Menu.Item value="reply-all">{t('Reply All')}</Menu.Item>
<Menu.Item value="forward">{t('Forward')}</Menu.Item>
<Menu.Item value="delete">{t('Delete')}</Menu.Item>
<Menu.Item value="reply-all">{I18n.t('Reply All')}</Menu.Item>
<Menu.Item value="forward">{I18n.t('Forward')}</Menu.Item>
<Menu.Item value="delete">{I18n.t('Delete')}</Menu.Item>
</Menu>
</>
)

View File

@ -21,7 +21,7 @@ import React from 'react'
import {MessageDetailItem} from './MessageDetailItem'
export default {
title: 'Playground/MessageDetailItem',
title: 'Examples/Canvas Inbox/MessageDetailItem',
component: MessageDetailItem,
argTypes: {
handleOptionSelect: {action: 'handleOptionSelect'}

View File

@ -20,7 +20,7 @@ import React from 'react'
import {PastMessages} from './PastMessages'
export default {
title: 'Playground/Past Messages',
title: 'Examples/Canvas Inbox/Past Messages',
component: PastMessages
}

View File

@ -21,7 +21,7 @@ import React from 'react'
import {RemovableItem} from './RemovableItem'
export default {
title: 'Playground/RemovableItem',
title: 'Examples/Canvas Inbox/RemovableItem',
component: RemovableItem,
argTypes: {
onRemove: {action: 'Remove'}

View File

@ -22,21 +22,20 @@ import {PresentationContent, ScreenReaderContent} from '@instructure/ui-a11y-con
import {Text} from '@instructure/ui-text'
import {TextInput} from '@instructure/ui-text-input'
import {Flex} from '@instructure/ui-flex'
const t = str => str
import I18n from 'i18n!conversations_2'
export const SubjectInput = ({value, onChange, onBlur, onFocus}) => {
return (
<Flex width="100%">
<Flex.Item padding="xx-small">
<PresentationContent>
<Text size="small">{t('Subject')}</Text>
<Text size="small">{I18n.t('Subject')}</Text>
</PresentationContent>
</Flex.Item>
<Flex.Item shouldGrow padding="xx-small">
<TextInput
renderLabel={<ScreenReaderContent>{t('Subject')}</ScreenReaderContent>}
placeholder={t('No Subject')}
renderLabel={<ScreenReaderContent>{I18n.t('Subject')}</ScreenReaderContent>}
placeholder={I18n.t('No Subject')}
value={value}
width="100%"
onChange={onChange}

View File

@ -21,7 +21,7 @@ import React from 'react'
import {SubjectInput} from './SubjectInput'
export default {
title: 'Playground/SubjectInput',
title: 'Examples/Canvas Inbox/SubjectInput',
component: SubjectInput,
argTypes: {
value: {control: 'text'},

View File

@ -4,7 +4,7 @@
"scripts": {
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook",
"test": "jest"
"test": "echo"
},
"description": "An application for Canvas Conversations",
"repository": "https://github.com/instructure/canvas-lms.git",

View File

@ -281,6 +281,10 @@
"yaml-loader": "^0.5"
},
"optionalDependencies": {
"@storybook/addon-actions": "^6.0.26",
"@storybook/addon-essentials": "^6.0.26",
"@storybook/addon-links": "^6.0.26",
"@storybook/react": "^6.0.26",
"inspect-process": "^0.5"
},
"browserslist": [
@ -330,7 +334,8 @@
"upgrade-and-dedupe": "rm -rf yarn.lock node_modules && yes 1 | yarn install --flat --production --ignore-scripts && git checkout package.json && yarn install && git add yarn.lock",
"upgrade-instructure-ui": "script/upgrade-instructure-ui",
"dedupe-yarn": "npx yarn-deduplicate",
"clean": "yarn workspace-run-serial clean"
"clean": "yarn workspace-run-serial clean",
"storybook": "start-storybook -p 6006 --no-dll =s ../public/javascripts"
},
"resolutions": {
"jquery": "https://github.com/instructure/jquery.git#1.7.2-with-AMD-and-CommonJS",