diff --git a/app/assets/stylesheets/application/drafts.scss b/app/assets/stylesheets/application/drafts.scss deleted file mode 100644 index dc528541b8..0000000000 --- a/app/assets/stylesheets/application/drafts.scss +++ /dev/null @@ -1,95 +0,0 @@ -.draft_label { - display: inline-block; -} - -.slider-container { - display: flex; - flex-wrap: wrap; - justify-content: space-between; -} - -.switch { - @include setMargin($size-0, $size-0, $size-20, $size-0); - position: relative; - display: inline-block; - width: 150px; - height: $size-40; -} - -.switch input { - display: none; -} - -.slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: $key-lime; - -webkit-transition: .4s; - transition: .4s; -} - -.slider:before { - position: absolute; - content: ""; - height: 32px; - width: 32px; - left: 4px; - bottom: 4px; - background-color: $white; - -webkit-transition: .4s; - transition: .4s; -} - -input:checked + .slider { - background-color: $mulberry; -} - -input:focus + .slider { - box-shadow: 0 0 $size-1 $mulberry; -} - -input:checked + .slider:before { - -webkit-transform: translateX(110px); - -ms-transform: translateX(110px); - transform: translateX(110px); -} - -.on -{ - display: none; -} - -.on, .off -{ - @include setFontSize($size-16); - color: white; - font-weight: bold; - position: absolute; - transform: translate(-50%,-50%); - top: 50%; - left: 50%; -} - -.off { - color: $mulberry; -} - -input:checked+ .slider .on { - display: block; -} - -input:checked + .slider .off { - display: none; -} - -.slider.round { - border-radius: 999px; -} - -.slider.round:before { - border-radius: 50%; -} diff --git a/app/helpers/moments_helper.rb b/app/helpers/moments_helper.rb index 8eaca80c87..3564fb07fe 100644 --- a/app/helpers/moments_helper.rb +++ b/app/helpers/moments_helper.rb @@ -93,7 +93,7 @@ def moment_form_inputs moments_viewers_input, { id: 'moment_comment', - type: 'checkbox', + type: 'switch', name: 'moment[comment]', label: t('comment.allow_comments'), value: true, @@ -104,7 +104,7 @@ def moment_form_inputs }, { id: 'moment_publishing', - type: 'checkbox', + type: 'switch', label: t('moments.form.draft_question'), dark: true, name: 'publishing', diff --git a/app/helpers/strategies_helper.rb b/app/helpers/strategies_helper.rb index 75bba50760..6d1f999672 100644 --- a/app/helpers/strategies_helper.rb +++ b/app/helpers/strategies_helper.rb @@ -56,7 +56,7 @@ def strategy_form_inputs strategy_viewers_input, { id: 'strategy_comment', - type: 'checkbox', + type: 'switch', name: 'strategy[comment]', label: t('comment.allow_comments'), value: true, @@ -65,6 +65,16 @@ def strategy_form_inputs info: t('comment.hint'), dark: true }, + { + id: 'strategy_publishing', + type: 'switch', + label: t('strategies.form.draft_question'), + dark: true, + name: 'publishing', + value: '0', + uncheckedValue: '1', + checked: !@strategy.published? + }, { id: 'strategy_perform_strategy_reminder', type: 'checkbox', @@ -76,16 +86,6 @@ def strategy_form_inputs checked: @strategy&.perform_strategy_reminder&.active, dark: true }, - { - id: 'strategy_publishing', - type: 'checkbox', - label: t('strategies.form.draft_question'), - dark: true, - name: 'publishing', - value: '0', - uncheckedValue: '1', - checked: !@strategy.published? - }, { id: 'strategy_perform_strategy_reminder_attributes_id', name: 'strategy[perform_strategy_reminder_attributes][id]', diff --git a/app/views/shared/_draft_slider.html.erb b/app/views/shared/_draft_slider.html.erb deleted file mode 100644 index bebcae1c4d..0000000000 --- a/app/views/shared/_draft_slider.html.erb +++ /dev/null @@ -1,11 +0,0 @@ - - <% if local_assigns[:published] %> - <%= check_box_tag 'publishing', 1, true, {id:'togBtn'} %> - <% else %> - <%= check_box_tag 'publishing', 1, false, {id:'togBtn'} %> - <% end %> - - <%= I18n.t 'common.actions.submit_publish' %> - <%= I18n.t 'common.actions.submit_draft' %> - - diff --git a/client/.storybook/addons.js b/client/.storybook/addons.js index 3ee8bac631..bfd04d705c 100644 --- a/client/.storybook/addons.js +++ b/client/.storybook/addons.js @@ -2,5 +2,4 @@ import '@storybook/addon-actions/register'; import '@storybook/addon-backgrounds/register'; -import 'storybook-addon-intl/register'; import '@storybook/addon-links/register'; diff --git a/client/.storybook/config.js b/client/.storybook/config.js index fdd2b3253a..94ddee5908 100644 --- a/client/.storybook/config.js +++ b/client/.storybook/config.js @@ -1,11 +1,9 @@ /* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */ import backgrounds from '@storybook/addon-backgrounds'; import { setDefaults, withInfo } from '@storybook/addon-info'; -import { setIntlConfig, withIntl } from 'storybook-addon-intl'; import { addDecorator, configure } from '@storybook/react'; -import { loadLocales } from 'libs/i18n/I18nSetup'; -import { availableLocalesAsCodeArray, defaultLocale, getMessages } from 'libs/i18n/I18nUtils'; +import { setup } from 'libs/i18n/setup'; import './stories.scss'; @@ -28,13 +26,7 @@ const withInfoConfig = { const globalDecorator = (storyFn, context) => withInfo(withInfoConfig)(storyFn)(context); addDecorator(globalDecorator); -loadLocales(); -setIntlConfig({ - locales: availableLocalesAsCodeArray, - defaultLocale, - getMessages, -}); -addDecorator(withIntl); +setup(); addDecorator(backgrounds([ { name: 'mulberry-wood', value: '#6D0839' }, { name: 'dark-gray', value: '#3F3F3F' }, // 25% gray diff --git a/client/app/components/Form/__tests__/Form.spec.jsx b/client/app/components/Form/__tests__/Form.spec.jsx index 5aab03012d..222ca41284 100644 --- a/client/app/components/Form/__tests__/Form.spec.jsx +++ b/client/app/components/Form/__tests__/Form.spec.jsx @@ -15,6 +15,7 @@ const component = (noFormTag: boolean) => ( InputMocks.inputSelectProps, Object.assign({}, InputMocks.inputCheckboxGroupProps, { required: true }), InputMocks.inputTagProps, + InputMocks.inputSwitchProps, InputMocks.inputSubmitProps, ]} /> diff --git a/client/app/components/Header/index.jsx b/client/app/components/Header/index.jsx index 42e5590e29..9e2dd527da 100644 --- a/client/app/components/Header/index.jsx +++ b/client/app/components/Header/index.jsx @@ -6,6 +6,7 @@ import ReactHtmlParser from 'react-html-parser'; import { Logo } from '../Logo'; import { HeaderProfile } from './HeaderProfile'; import css from './Header.scss'; +import { I18n } from '../../libs/i18n'; export type Link = { name: string, @@ -81,6 +82,7 @@ export class Header extends React.Component { displayDesktop = () => { const { home } = this.props; + const { mobileNavOpen } = this.state; return ( @@ -94,7 +96,7 @@ export class Header extends React.Component { onKeyDown={this.toggle} role="button" tabIndex="0" - aria-label="Expand menu" // TODO: intl in React not working in Rails + aria-label={mobileNavOpen ? I18n.t('close') : I18n.t('expand_menu')} > {this.displayToggle()} diff --git a/client/app/components/Input/InputSwitch.jsx b/client/app/components/Input/InputSwitch.jsx new file mode 100644 index 0000000000..9a89196d0b --- /dev/null +++ b/client/app/components/Input/InputSwitch.jsx @@ -0,0 +1,88 @@ +// @flow +import React from 'react'; +import { Utils } from '../../utils'; +import { Input } from './index'; +import css from './InputSwitch.scss'; +import { I18n } from '../../libs/i18n'; + +export type Props = { + id: string, + name: string, + label: string, + value?: any, + checked?: boolean, + uncheckedValue?: any, +}; + +export type State = { + checked: boolean, + key?: string, +}; + +export class InputSwitch extends React.Component { + constructor(props: Props) { + super(props); + this.state = { checked: !!props.checked }; + } + + toggleChecked = () => { + this.setState((prevState: State) => ({ + checked: !prevState.checked, + key: Utils.randomString(), + })); + }; + + onKeyPress = (e: SyntheticKeyboardEvent) => { + if (e.key === 'Enter') { + this.toggleChecked(); + } + }; + + displaySwitchHidden = () => { + const { + id, name, label, value, uncheckedValue, + } = this.props; + const { checked, key } = this.state; + return ( + + + + ); + }; + + render() { + const { id } = this.props; + const { checked } = this.state; + return ( + + + + {checked ? I18n.t('true') : I18n.t('false')} + + + {this.displaySwitchHidden()} + + ); + } +} diff --git a/client/app/components/Input/InputSwitch.scss b/client/app/components/Input/InputSwitch.scss new file mode 100644 index 0000000000..d22818d1d2 --- /dev/null +++ b/client/app/components/Input/InputSwitch.scss @@ -0,0 +1,39 @@ +@import "../../styles/_global.scss"; + +.switch { + @include setFontSize($size-16); + display: inline-block; + min-width: 120px; + letter-spacing: 0.095em; + + &Wrapper { + display: flex; + flex-direction: row; + border-radius: $size-4; + padding: $size-4; + } + + &On { + @include linearTransition(0.5s); + background: $key-lime; + justify-content: flex-end; + } + + &Off { + @include linearTransition(0.5s); + background: $mulberry-70; + } + + &Toggle { + background: $white-70; + padding: $size-8 $size-16; + border-radius: $size-4; + color: $mulberry-80; + cursor: pointer; + text-transform: uppercase; + } + + &Hidden { + display: none; + } +} \ No newline at end of file diff --git a/client/app/components/Input/__tests__/Input.spec.jsx b/client/app/components/Input/__tests__/Input.spec.jsx index 4cd7740f38..db0b3099a4 100644 --- a/client/app/components/Input/__tests__/Input.spec.jsx +++ b/client/app/components/Input/__tests__/Input.spec.jsx @@ -130,4 +130,36 @@ describe('Input', () => { }); }); }); + + describe('Switch', () => { + describe('with accordion prop', () => { + it('toggles correctly', () => { + const wrapper = mount( + InputMocks.createInput(InputMocks.inputSwitchProps, { + accordion: true, + }), + ); + expect( + wrapper + .find('.accordionContent') + .find('.switch') + .exists(), + ).toEqual(false); + wrapper.find('.accordion').simulate('click'); + expect( + wrapper + .find('.accordionContent') + .find('.switch') + .exists(), + ).toEqual(true); + wrapper.find('.accordion').simulate('click'); + expect( + wrapper + .find('.accordionContent') + .find('.switch') + .exists(), + ).toEqual(false); + }); + }); + }); }); diff --git a/client/app/components/Input/__tests__/InputSwitch.spec.jsx b/client/app/components/Input/__tests__/InputSwitch.spec.jsx new file mode 100644 index 0000000000..1751110121 --- /dev/null +++ b/client/app/components/Input/__tests__/InputSwitch.spec.jsx @@ -0,0 +1,30 @@ +// @flow +import { mount } from 'enzyme'; +import { InputMocks } from '../../../mocks/InputMocks'; + +const component = InputMocks.createInput(InputMocks.inputSwitchProps); +const input = `input#${InputMocks.inputSwitchProps.id}`; + +describe('InputSwitch', () => { + describe('with mouse', () => { + it('toggles correctly', () => { + const wrapper = mount(component); + expect(wrapper.find(input).props().defaultChecked).toEqual(false); + wrapper.find('.switchToggle').simulate('click'); + expect(wrapper.find(input).props().defaultChecked).toEqual(true); + wrapper.find('.switchToggle').simulate('click'); + expect(wrapper.find(input).props().defaultChecked).toEqual(false); + }); + }); + + describe('with keyboard', () => { + it('toggles correctly', () => { + const wrapper = mount(component); + expect(wrapper.find(input).props().defaultChecked).toEqual(false); + wrapper.find('.switchToggle').simulate('keypress', { key: 'Enter' }); + expect(wrapper.find(input).props().defaultChecked).toEqual(true); + wrapper.find('.switchToggle').simulate('keypress', { key: 'Enter' }); + expect(wrapper.find(input).props().defaultChecked).toEqual(false); + }); + }); +}); diff --git a/client/app/components/Input/index.jsx b/client/app/components/Input/index.jsx index 36a1b76db1..a7bd5fa2bf 100644 --- a/client/app/components/Input/index.jsx +++ b/client/app/components/Input/index.jsx @@ -7,6 +7,7 @@ import { InputCheckbox } from './InputCheckbox'; import { InputCheckboxGroup } from './InputCheckboxGroup'; import { InputSelect } from './InputSelect'; import { InputTag } from './InputTag'; +import { InputSwitch } from './InputSwitch'; import { InputDefault, REQUIRES_DEFAULT, @@ -22,6 +23,7 @@ export const TYPES = REQUIRES_DEFAULT.concat([ 'select', 'checkboxGroup', 'tag', + 'switch', ]); const REQUIRES_LABEL = DEFAULT_WITH_LABEL.concat([ @@ -29,6 +31,7 @@ const REQUIRES_LABEL = DEFAULT_WITH_LABEL.concat([ 'select', 'checkboxGroup', 'tag', + 'switch', ]); const REQUIRED_POSSIBLE = DEFAULT_WITH_LABEL.concat([ @@ -65,7 +68,8 @@ export type Props = { | 'select' | 'checkboxGroup' | 'tag' - | 'hidden', + | 'hidden' + | 'switch', name?: string, label?: string, placeholder?: string, @@ -274,6 +278,31 @@ export class Input extends React.Component { return null; }; + displaySwitch = () => { + const { + type, + id, + name, + label, + value, + checked, + uncheckedValue, + } = this.props; + if (type === 'switch' && label && name) { + return ( + + ); + } + return null; + }; + displayLabel = () => { const { label, info, required, type, @@ -318,6 +347,7 @@ export class Input extends React.Component { {this.displaySelect()} {this.displayTextarea()} {this.displayTag()} + {this.displaySwitch()} {this.displaySubmit()} ); diff --git a/client/app/components/Modal/index.jsx b/client/app/components/Modal/index.jsx index fdee08c29a..dc3d81d1c3 100644 --- a/client/app/components/Modal/index.jsx +++ b/client/app/components/Modal/index.jsx @@ -4,6 +4,7 @@ import renderHTML from 'react-render-html'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import css from './Modal.scss'; +import { I18n } from '../../libs/i18n'; export type Props = { element?: any, @@ -50,7 +51,7 @@ export class Modal extends React.Component { onKeyDown={this.toggleOpen} role="button" tabIndex={0} - aria-label="Close" // TODO: intl in React not working in Rails + aria-label={I18n.t('close')} > diff --git a/client/app/libs/i18n/.gitkeep b/client/app/libs/i18n/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/client/app/libs/i18n/I18nSetup.js b/client/app/libs/i18n/I18nSetup.js deleted file mode 100644 index 523063b2ac..0000000000 --- a/client/app/libs/i18n/I18nSetup.js +++ /dev/null @@ -1,22 +0,0 @@ -import { addLocaleData } from 'react-intl'; -// every locale is an array of various sub locales, that follow the type defined below -// https://github.com/yahoo/react-intl/wiki/API#addlocaledata -/* - type LocaleData = { - locale: string, - [key: string]: any, - } - */ -import en from 'react-intl/locale-data/en'; -import es from 'react-intl/locale-data/es'; -import it from 'react-intl/locale-data/it'; -import nb from 'react-intl/locale-data/nb'; -import nl from 'react-intl/locale-data/nl'; -import pt from 'react-intl/locale-data/pt'; -import sv from 'react-intl/locale-data/sv'; -import vi from 'react-intl/locale-data/vi'; - -// Initizalize all locales for react-intl. -export const loadLocales = () => { - addLocaleData([].concat(en, es, it, nb, nl, pt, sv, vi)); -}; diff --git a/client/app/libs/i18n/I18nUtils.js b/client/app/libs/i18n/I18nUtils.js deleted file mode 100644 index 34750f83ab..0000000000 --- a/client/app/libs/i18n/I18nUtils.js +++ /dev/null @@ -1,27 +0,0 @@ -import Cookies from 'js-cookie'; -import enYML from '../../../../config/locales/en.yml'; - -import { defaultLocale } from './default'; -import { translations } from './translations'; - -export const safeGetLocale = () => Cookies.get('locale') || defaultLocale; - -export const getMessages = locale => translations[locale]; - -export const availableLocalesAsMap = enYML.en.languages; - -const filterNonLanguage = ([value]) => !['various'].includes(value); - -export const availableLocalesAsCodeArray = Object.entries(availableLocalesAsMap) - .filter(filterNonLanguage) // Exclude non-language text - .map(([value]) => value) - .sort((a, b) => a.localeCompare(b)); - -export const availableLocalesAsSelectOptions = Object.entries( - availableLocalesAsMap, -) - .filter(filterNonLanguage) // Exclude non-language text - .map(([value, label]) => ({ label, value })) - .sort((a, b) => a.label.localeCompare(b.label)); - -export { defaultLocale, translations }; diff --git a/client/app/libs/i18n/index.js b/client/app/libs/i18n/index.js new file mode 100644 index 0000000000..d54dcc1e91 --- /dev/null +++ b/client/app/libs/i18n/index.js @@ -0,0 +1,9 @@ +export const I18n = Object.assign({}, window.I18n, { + lookup: (scope, options = {}) => { + const result = window.I18n.lookup(scope, options); + if (typeof result === 'undefined' || result.match(/%{.*}/g)) return result; + // Generating client/app/libs/i18n/translations.js strips % from interpolation values + // react_on_rails.rb does not provide functionality to change this + return result.replace('{', '%{'); + }, +}); diff --git a/client/app/libs/i18n/setup.js b/client/app/libs/i18n/setup.js new file mode 100644 index 0000000000..f33483ae34 --- /dev/null +++ b/client/app/libs/i18n/setup.js @@ -0,0 +1,12 @@ +import Cookies from 'js-cookie'; +import * as I18n from 'i18n-js'; +import { translations } from './translations'; + +export const setup = () => { + if (!window.I18n && translations) { + I18n.defaultLocale = 'en'; + I18n.locale = Cookies.get('locale'); + I18n.translations = translations; + window.I18n = I18n; + } +}; diff --git a/client/app/libs/testHelper.js b/client/app/libs/testHelper.js index 57f2a886b7..11cd55a95b 100644 --- a/client/app/libs/testHelper.js +++ b/client/app/libs/testHelper.js @@ -3,8 +3,10 @@ import React from 'react'; import TestUtils from 'react-dom/test-utils'; import Enzyme from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; +import { setup } from './i18n/setup'; Enzyme.configure({ adapter: new Adapter() }); window.alert = () => {}; +setup(); export { React, TestUtils }; diff --git a/client/app/mocks/InputMocks.jsx b/client/app/mocks/InputMocks.jsx index 8cd39e4f31..5726cff029 100644 --- a/client/app/mocks/InputMocks.jsx +++ b/client/app/mocks/InputMocks.jsx @@ -80,6 +80,16 @@ const inputTagProps = { checkboxes, }; +const inputSwitchProps = { + id: 'some-switch-id', + type: 'switch', + name: 'some-switch-name', + label: 'Some Switch Label', + info: 'Some Switch Info', + value: true, + uncheckedValue: false, +}; + const inputSubmitProps = { id: 'some-submit-id', type: 'submit', @@ -102,6 +112,7 @@ export const InputMocks = { inputCheckboxGroupProps, inputSubmitProps, inputTagProps, + inputSwitchProps, event, createInput, }; diff --git a/client/app/startup/registration.js b/client/app/startup/registration.js index 334c1f9663..4b12a0b94a 100644 --- a/client/app/startup/registration.js +++ b/client/app/startup/registration.js @@ -4,7 +4,7 @@ * this bundle will be loaded in production. */ import ReactOnRails from 'react-on-rails'; -import { loadLocales } from '../libs/i18n/I18nSetup'; +import { setup } from '../libs/i18n/setup'; import '../styles/_global.scss'; import { Avatar } from '../components/Avatar'; import { Chart } from '../components/Chart'; @@ -27,7 +27,7 @@ import { Accordion } from '../components/Accordion'; import { Resources } from '../widgets/Resources'; import { Notifications } from '../widgets/Notifications'; -loadLocales(); +setup(); // This is how react_on_rails can see the Components in the browser. ReactOnRails.register({ diff --git a/client/app/stories/Checkbox.stories.jsx b/client/app/stories/Checkbox.stories.jsx index 8603ee3bc9..471d367402 100644 --- a/client/app/stories/Checkbox.stories.jsx +++ b/client/app/stories/Checkbox.stories.jsx @@ -7,7 +7,7 @@ function handleCheckboxClick(allChecked) { window.alert(`Here's an example of an action: ${allChecked}`); } -storiesOf('Checkbox', module).add('Checkbox', () => ( +storiesOf("Checkbox (don't use)", module).add('Checkbox', () => ( One CheckboxGroup handleCheckboxClick(allChecked)}> diff --git a/client/app/stories/Form.stories.jsx b/client/app/stories/Form.stories.jsx index 197418be72..e4dab0499e 100644 --- a/client/app/stories/Form.stories.jsx +++ b/client/app/stories/Form.stories.jsx @@ -15,6 +15,7 @@ storiesOf('Form', module).add('Form', () => ( InputMocks.inputSelectProps, Object.assign({}, InputMocks.inputCheckboxGroupProps, { required: true }), InputMocks.inputTagProps, + InputMocks.inputSwitchProps, InputMocks.inputSubmitProps, ]} /> diff --git a/client/app/stories/I18n.stories.jsx b/client/app/stories/I18n.stories.jsx index a11a3665bb..e797ff786f 100644 --- a/client/app/stories/I18n.stories.jsx +++ b/client/app/stories/I18n.stories.jsx @@ -1,21 +1,10 @@ import React from 'react'; -import { FormattedMessage, injectIntl } from 'react-intl'; import { storiesOf } from '@storybook/react'; -import { defaultMessages } from '../libs/i18n/default'; - -const TitleComponent = injectIntl(({ count, intl }) => ( - - {intl.formatMessage(defaultMessages.sharedCharacterCount, { - number: count, - })} - -)); +import { I18n } from '../libs/i18n'; storiesOf('I18n', module).add('Message with variable', () => ( - - - - + {I18n.t('created', { created_at: 'Blah' })} + {I18n.t('draft')} )); diff --git a/client/app/stories/Input.stories.jsx b/client/app/stories/Input.stories.jsx index 8877807546..4ad8109e89 100644 --- a/client/app/stories/Input.stories.jsx +++ b/client/app/stories/Input.stories.jsx @@ -184,7 +184,6 @@ storiesOf('Input', module) {InputMocks.createInput(InputMocks.inputSelectProps, { onChange: InputMocks.event, - large: true, })} {InputMocks.createInput(InputMocks.inputSelectProps, { onChange: InputMocks.event, @@ -205,7 +204,6 @@ storiesOf('Input', module) {InputMocks.createInput(InputMocks.inputSelectProps, { onChange: InputMocks.event, - large: true, accordion: true, })} {InputMocks.createInput(InputMocks.inputSelectProps, { @@ -258,4 +256,37 @@ storiesOf('Input', module) accordion: true, })} + )) + .add('Switch', () => ( + + {InputMocks.createInput(InputMocks.inputSwitchProps)} + {InputMocks.createInput(InputMocks.inputSwitchProps, { + large: true, + })} + {InputMocks.createInput(InputMocks.inputSwitchProps, { + dark: true, + })} + {InputMocks.createInput(InputMocks.inputSwitchProps, { + dark: true, + large: true, + })} + + )) + .add('Switch with accordion', () => ( + + {InputMocks.createInput(InputMocks.inputSwitchProps, { accordion: true })} + {InputMocks.createInput(InputMocks.inputSwitchProps, { + large: true, + accordion: true, + })} + {InputMocks.createInput(InputMocks.inputSwitchProps, { + dark: true, + accordion: true, + })} + {InputMocks.createInput(InputMocks.inputSwitchProps, { + dark: true, + large: true, + accordion: true, + })} + )); diff --git a/client/package.json b/client/package.json index c32f75545f..5c3396350f 100644 --- a/client/package.json +++ b/client/package.json @@ -2,9 +2,10 @@ "name": "ifme", "private": true, "scripts": { - "build:test": "NODE_ENV=test webpack --config webpack.config.js --display-error-details", - "build:production": "NODE_ENV=production webpack --config webpack.config.js", - "build:development": "NODE_ENV=development webpack -w --config webpack.config.js", + "build-i18n": "rm app/libs/i18n/translations.js app/libs/i18n/default.js; rake react_on_rails:locale", + "build:test": "rm -rf public/webpack-test && NODE_ENV=test webpack --config webpack.config.js --display-error-details", + "build:production": "yarn build-i18n && NODE_ENV=production webpack --config webpack.config.js", + "build:development": "yarn build-i18n && NODE_ENV=development webpack -w --config webpack.config.js", "debug": "yarn build:production --display-modules --sort-modules-by size", "lint:setup": "flow-typed install", "lint:flow": "flow", @@ -14,7 +15,7 @@ "lint:build": "yarn lint:flow && yarn lint:eslint", "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook", - "start": "yarn storybook", + "start": "yarn build-i18n && yarn storybook", "test": "NODE_PATH=./app jest", "test:debug": "NODE_PATH=./app node --inspect-brk jest --runInBand" }, @@ -36,7 +37,7 @@ "chartkick": "^2.2.4", "es5-shim": "^4.5.9", "font-awesome": "^4.7.0", - "intl": "^1.2.5", + "i18n-js": "^3.0.11", "js-cookie": "^2.2.0", "jstimezonedetect": "^1.0.6", "pell": "^1.0.4", @@ -45,7 +46,6 @@ "react-chartkick": "^0.1.3", "react-dom": "^16.5.2", "react-html-parser": "^2.0.2", - "react-intl": "^2.4.0", "react-lazyload": "^2.3.0", "react-on-rails": "11.1.5", "react-render-html": "^0.6.0" @@ -94,7 +94,6 @@ "prettier-eslint": "^8.8.2", "react-test-renderer": "^16.5.2", "sass-loader": "^7.1.0", - "storybook-addon-intl": "^2.3.0", "uglifyjs-webpack-plugin": "^2.0.1", "url-loader": "^1.1.1", "webpack": "^4.19.1", diff --git a/client/yarn.lock b/client/yarn.lock index 74baa4d7ea..8169012023 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -4991,6 +4991,10 @@ hyphenate-style-name@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz#31160a36930adaf1fc04c6074f7eb41465d4ec4b" +i18n-js@^3.0.11: + version "3.0.11" + resolved "https://registry.yarnpkg.com/i18n-js/-/i18n-js-3.0.11.tgz#f9e96bdb641c5b9d6be12759d7c422089987ef02" + iconv-lite@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" @@ -5193,31 +5197,7 @@ interpret@^1.0.0, interpret@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" -intl-format-cache@^2.0.5: - version "2.1.0" - resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.1.0.tgz#04a369fecbfad6da6005bae1f14333332dcf9316" - -intl-messageformat-parser@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz#b43d45a97468cadbe44331d74bb1e8dea44fc075" - -intl-messageformat@^2.0.0, intl-messageformat@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-2.2.0.tgz#345bcd46de630b7683330c2e52177ff5eab484fc" - dependencies: - intl-messageformat-parser "1.4.0" - -intl-relativeformat@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz#010f1105802251f40ac47d0e3e1a201348a255df" - dependencies: - intl-messageformat "^2.0.0" - -intl@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/intl/-/intl-1.2.5.tgz#82244a2190c4e419f8371f5aa34daa3420e2abde" - -invariant@^2.1.1, invariant@^2.2.2, invariant@^2.2.4: +invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: @@ -7834,7 +7814,7 @@ prompts@^0.1.9: kleur "^2.0.1" sisteransi "^0.1.1" -prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0, prop-types@^15.6.2: +prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0, prop-types@^15.6.2: version "15.6.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" dependencies: @@ -8100,15 +8080,6 @@ react-inspector@^2.3.0: babel-runtime "^6.26.0" is-dom "^1.0.9" -react-intl@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.4.0.tgz#66c14dc9df9a73b2fbbfbd6021726e80a613eb15" - dependencies: - intl-format-cache "^2.0.5" - intl-messageformat "^2.1.0" - intl-relativeformat "^2.0.0" - invariant "^2.1.1" - react-is@^16.4.2, react-is@^16.5.2: version "16.5.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.5.2.tgz#e2a7b7c3f5d48062eb769fcb123505eb928722e3" @@ -9098,12 +9069,6 @@ stealthy-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" -storybook-addon-intl@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/storybook-addon-intl/-/storybook-addon-intl-2.3.0.tgz#94711276cefeb0b9ef13b1e70a96516c1fe0cfd2" - dependencies: - prop-types "^15.5.0" - stream-browserify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" diff --git a/config/initializers/react_on_rails.rb b/config/initializers/react_on_rails.rb index 0d02f07bba..d4636baac2 100644 --- a/config/initializers/react_on_rails.rb +++ b/config/initializers/react_on_rails.rb @@ -65,8 +65,7 @@ ################################################################################ # I18N OPTIONS ################################################################################ - # Replace the following line to the location where you keep translation.js & default.js for use - # by the npm packages react-intl. Be sure this directory exists! + # Replace the following line to the location where you keep translation.js & default.js for use by client config.i18n_dir = Rails.root.join("client", "app", "libs", "i18n") # # Replace the following line to the location where you keep your client i18n yml files diff --git a/config/locales/en.yml b/config/locales/en.yml index cc32023770..6162cd0065 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -11,6 +11,10 @@ en: click_here: "click here" less: " [Less]" language: 'Language' + true: 'yes' + false: 'no' + expand_menu: 'expand menu' + close: 'close' languages: en: English diff --git a/config/locales/es.yml b/config/locales/es.yml index 19b669d213..05cbe46801 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -11,6 +11,10 @@ es: click_here: "da click aquí" less: " [Menos]" language: 'Idioma' + true: 'si' + false: 'no' + expand_menu: 'ampliar menú' + close: 'cerca' languages: en: English diff --git a/config/locales/it.yml b/config/locales/it.yml index b5122a671f..7a02414673 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -11,6 +11,10 @@ it: click_here: "clicca qui" less: " [Di meno]" language: 'Lingua' + true: 'sì' + false: 'no' + expand_menu: 'espandere il menu' + close: 'vicino' languages: en: English diff --git a/config/locales/nb.yml b/config/locales/nb.yml index 874e4b9cb7..e8be58cad7 100644 --- a/config/locales/nb.yml +++ b/config/locales/nb.yml @@ -11,6 +11,10 @@ nb: click_here: "klikk her" less: " [Mindre]" language: 'Språk' + true: 'ja' + false: 'nei' + expand_menu: 'utvide menyen' + close: 'lukk' languages: en: English diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 1e47604768..735278a507 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -11,6 +11,10 @@ nl: click_here: "klik hier" less: " [Minder]" language: 'Taal' + true: 'ja' + false: 'nee' + expand_menu: 'vouw menu uit' + close: 'dichtbij' languages: en: English diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 7fb41a0194..b9762d3760 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -11,6 +11,10 @@ pt-BR: click_here: "clique aqui" less: " [Menos]" language: 'Idioma' + true: 'sim' + false: 'não' + expand_menu: 'expandir menu' + close: 'fechar' languages: en: English diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 1fefe3c62f..eed7064385 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -11,6 +11,10 @@ sv: click_here: "Klicka här" less: " [Mindre]" language: 'Språk' + true: 'ja' + false: 'nej' + expand_menu: 'expandera menyn' + close: 'stänga' languages: en: English diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 4aef03ef74..d4a1f2ea8a 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -11,6 +11,10 @@ vi: click_here: "nhấn vào đây" less: " [Ít hơn]" language: 'Ngôn ngữ' + true: 'có' + false: 'không' + expand_menu: 'mở rộng menu' + close: 'gần' languages: en: English diff --git a/spec/features/user_creates_a_draft_moment_spec.rb b/spec/features/user_creates_a_draft_moment_spec.rb index 64d377853a..71a25a7ba6 100644 --- a/spec/features/user_creates_a_draft_moment_spec.rb +++ b/spec/features/user_creates_a_draft_moment_spec.rb @@ -84,7 +84,7 @@ find('.accordion').click end - find('#moment_comment').click + find('#moment_comment_switch').click find('#submit').click # VIEWING AS OWNER @@ -114,8 +114,6 @@ # EDITING find('.storyActionsEdit').click expect(find('.pageTitle')).to have_content 'Edit My New Moment' - expect(page).to have_field('moment_comment', checked: true) - expect(page).to have_field('moment_publishing', checked: true) within('#moment_category_accordion') do find('.accordion').click @@ -148,7 +146,7 @@ fill_in_textarea(moment_why_text, '#moment_why') # PUBLISH - find('#moment_publishing').click + find('#moment_publishing_switch').click find('#submit').click # VIEWING AFTER EDITING diff --git a/spec/features/user_creates_a_draft_strategy_spec.rb b/spec/features/user_creates_a_draft_strategy_spec.rb index c0b76bc0ef..4399637dc2 100644 --- a/spec/features/user_creates_a_draft_strategy_spec.rb +++ b/spec/features/user_creates_a_draft_strategy_spec.rb @@ -53,7 +53,7 @@ find('.accordion').click end - find('#strategy_comment').click + find('#strategy_comment_switch').click find('#strategy_perform_strategy_reminder').click find('#submit').click @@ -79,8 +79,6 @@ visit back find('.storyActionsEdit').click expect(find('.pageTitle')).to have_content 'Edit My New Strategy' - expect(page).to have_field('strategy_comment', checked: true) - expect(page).to have_field('strategy_publishing', checked: true) within('#strategy_category_accordion') do find('.accordion').click @@ -99,7 +97,7 @@ fill_in_textarea(strategy_description_text, '#strategy_description') # PUBLISH - find('#strategy_publishing').click + find('#strategy_publishing_switch').click find('#submit').click # VIEWING AFTER EDITING diff --git a/spec/features/user_creates_a_published_moment_spec.rb b/spec/features/user_creates_a_published_moment_spec.rb index 0683ed892d..a7d2c4b61a 100644 --- a/spec/features/user_creates_a_published_moment_spec.rb +++ b/spec/features/user_creates_a_published_moment_spec.rb @@ -10,7 +10,7 @@ it 'is not successful' do login_as user visit new_moment_path - find('#moment_publishing').click + find('#moment_publishing_switch').click find('#submit').click expect(page).to have_content('New Moment') expect(page).to have_css('.labelError') @@ -89,8 +89,8 @@ find('.accordion').click end - find('#moment_comment').click - find('#moment_publishing').click + find('#moment_comment_switch').click + find('#moment_publishing_switch').click find('#submit').click # VIEWING @@ -114,8 +114,6 @@ # EDITING find('.storyActionsEdit').click expect(find('.pageTitle')).to have_content 'Edit My New Moment' - expect(page).to have_field('moment_comment', checked: true) - expect(page).to have_field('moment_publishing', checked: false) within('#moment_category_accordion') do find('.accordion').click diff --git a/spec/features/user_creates_a_published_strategy_spec.rb b/spec/features/user_creates_a_published_strategy_spec.rb index 7c33ee0b36..9f5b114cca 100644 --- a/spec/features/user_creates_a_published_strategy_spec.rb +++ b/spec/features/user_creates_a_published_strategy_spec.rb @@ -8,7 +8,7 @@ it 'is not successful' do login_as user visit new_strategy_path - find('#strategy_publishing').click + find('#strategy_publishing_switch').click find('#submit').click expect(page).to have_content('New Strategy') expect(page).to have_css('.labelError') @@ -57,9 +57,9 @@ find('.accordion').click end - find('#strategy_comment').click + find('#strategy_comment_switch').click find('#strategy_perform_strategy_reminder').click - find('#strategy_publishing').click + find('#strategy_publishing_switch').click find('#submit').click # VIEWING @@ -76,8 +76,6 @@ # EDITING find('.storyActionsEdit').click expect(find('.pageTitle')).to have_content 'Edit My New Strategy' - expect(page).to have_field('strategy_comment', checked: true) - expect(page).to have_field('strategy_publishing', checked: false) within('#strategy_category_accordion') do find('.accordion').click
- -
{I18n.t('draft')}