Layout improvements for modals and other areas
This commit is contained in:
parent
f55d777c55
commit
c3414f1754
17 changed files with 1177 additions and 117 deletions
6
app/assets/stylesheets/common/_focusable.scss
Normal file
6
app/assets/stylesheets/common/_focusable.scss
Normal file
|
@ -0,0 +1,6 @@
|
|||
/* License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later */
|
||||
.focusable_item {
|
||||
&:focus {
|
||||
outline: 1px dotted black
|
||||
}
|
||||
}
|
|
@ -43,3 +43,5 @@
|
|||
@import 'common/z_indices';
|
||||
@import 'common/ios_hack';
|
||||
@import 'common/minimal';
|
||||
|
||||
@import 'common/focusable'
|
16
javascripts/src/components/common/BootstrapWrapper.tsx
Normal file
16
javascripts/src/components/common/BootstrapWrapper.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
class BootstrapWrapper extends React.Component<{}, {}> {
|
||||
render() {
|
||||
return <div className={"tw-bs"}>
|
||||
{this.props.children}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default observer(BootstrapWrapper)
|
||||
|
||||
|
||||
|
115
javascripts/src/components/common/DefaultCloseButton.tsx
Normal file
115
javascripts/src/components/common/DefaultCloseButton.tsx
Normal file
|
@ -0,0 +1,115 @@
|
|||
import React = require("react");
|
||||
import { action, observable } from "mobx";
|
||||
import { Transition } from "react-transition-group";
|
||||
import { CloseButton } from "./svg/CloseButton";
|
||||
import color = require("color");
|
||||
import { observer } from "mobx-react";
|
||||
import ScreenReaderOnlyText from "./ScreenReaderOnlyText";
|
||||
|
||||
interface DefaultCloseButtonProps {
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
const mainColor = '#969696'
|
||||
const darkenedColor = color(mainColor).darken(0.1).hex()
|
||||
const defaultStyles = {
|
||||
foreground: {
|
||||
fill: mainColor
|
||||
},
|
||||
background: {
|
||||
stroke: mainColor,
|
||||
fill: '#FFFFFF'
|
||||
}
|
||||
}
|
||||
|
||||
const states: { [state: string]: any } = {
|
||||
entering: {
|
||||
foreground: {
|
||||
fill: darkenedColor,
|
||||
transition: 'fill 250ms ease-in-out'
|
||||
},
|
||||
background: {
|
||||
stroke: darkenedColor,
|
||||
transition: 'stroke 250ms ease-in-out'
|
||||
}
|
||||
},
|
||||
entered: {
|
||||
foreground: {
|
||||
fill: darkenedColor,
|
||||
},
|
||||
background: {
|
||||
stroke: darkenedColor,
|
||||
}
|
||||
},
|
||||
exiting: {
|
||||
foreground: {
|
||||
fill: mainColor,
|
||||
transition: 'fill 250ms ease-in-out'
|
||||
},
|
||||
background: {
|
||||
stroke: mainColor,
|
||||
transition: 'stroke 250ms ease-in-out'
|
||||
}
|
||||
},
|
||||
exited: {
|
||||
foreground: {
|
||||
fill: mainColor,
|
||||
},
|
||||
background: {
|
||||
stroke: mainColor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export class DefaultCloseButton extends React.Component<DefaultCloseButtonProps, {}> {
|
||||
@observable
|
||||
hovering: boolean
|
||||
|
||||
@observable
|
||||
focusing: boolean
|
||||
|
||||
@action.bound
|
||||
mouseEnter() {
|
||||
this.hovering = true;
|
||||
}
|
||||
|
||||
@action.bound
|
||||
mouseLeave() {
|
||||
this.hovering = false;
|
||||
}
|
||||
|
||||
@action.bound
|
||||
keyDown(event: React.KeyboardEvent<HTMLAnchorElement>) {
|
||||
if (event.key == 'Enter') {
|
||||
event.preventDefault();
|
||||
this.props.onClick();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Transition in={this.hovering} timeout={250}>
|
||||
{(hoverState) => {
|
||||
const backgroundStyle = {
|
||||
...defaultStyles.background,
|
||||
...((states[hoverState] && states[hoverState].background) || {})
|
||||
}
|
||||
|
||||
const foregroundStyle =
|
||||
{
|
||||
...defaultStyles.foreground,
|
||||
...((states[hoverState] && states[hoverState].foreground) || {})
|
||||
}
|
||||
|
||||
return <a onMouseEnter={this.mouseEnter} onMouseLeave={this.mouseLeave} onClick={this.props.onClick} onKeyDown={this.keyDown} tabIndex={0} className={'focusable_item'}>
|
||||
<CloseButton backgroundCircleStyle={backgroundStyle}
|
||||
foregroundCircleStyle={foregroundStyle}
|
||||
/><ScreenReaderOnlyText>Close modal</ScreenReaderOnlyText>
|
||||
</a>
|
||||
|
||||
}
|
||||
}
|
||||
</Transition>
|
||||
|
||||
}
|
||||
}
|
|
@ -2,9 +2,9 @@
|
|||
import * as React from 'react';
|
||||
import 'jest';
|
||||
import Modal, {ModalProps} from './Modal'
|
||||
import {shallow} from "enzyme";
|
||||
import {toJS} from "mobx";
|
||||
import {shallow, mount, ReactWrapper} from "enzyme";
|
||||
import toJson from "enzyme-to-json";
|
||||
import { DefaultCloseButton } from './DefaultCloseButton';
|
||||
|
||||
describe('Modal', () => {
|
||||
test('nothing displayed if inactive', () => {
|
||||
|
@ -13,16 +13,47 @@ describe('Modal', () => {
|
|||
expect(toJson(modal)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('active modal displays', () => {
|
||||
describe('active modal displays', () => {
|
||||
let onCloseWasCalled = false
|
||||
let modal = shallow(<Modal titleText={"title text"}
|
||||
let modal:ReactWrapper
|
||||
beforeEach(() => {
|
||||
onCloseWasCalled = false;
|
||||
modal = mount(<Modal titleText={"title text"}
|
||||
focusDialog={true}
|
||||
modalActive={true}
|
||||
onClose={() => { onCloseWasCalled = true}}
|
||||
childGenerator={() => <div/>}/>)
|
||||
})
|
||||
|
||||
it('matches snapshot', () => {
|
||||
expect(toJson(modal)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('closes on modal component close', () => {
|
||||
let modalComponent = modal.instance() as React.Component<ModalProps, {}> //casting to modal didn't work for reasons?
|
||||
modalComponent.props.onClose()
|
||||
expect(onCloseWasCalled).toBeTruthy()
|
||||
})
|
||||
|
||||
it('closes on closeButtonClick', () => {
|
||||
let closeButton = modal.find('DefaultCloseButton')
|
||||
let instanceButton = closeButton.instance() as DefaultCloseButton
|
||||
instanceButton.props.onClick();
|
||||
expect(onCloseWasCalled).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it('doesnt have a close button if we ask for none', () => {
|
||||
let onCloseWasCalled = false
|
||||
let modal = mount(<Modal titleText={"title text"}
|
||||
focusDialog={true}
|
||||
modalActive={true}
|
||||
showCloseButton={false}
|
||||
onClose={() => { onCloseWasCalled = true}}
|
||||
childGenerator={() => <div/>}/>)
|
||||
expect(toJson(modal)).toMatchSnapshot()
|
||||
let modalComponent = modal.instance() as React.Component<ModalProps, {}> //casting to modal didn't work for reasons?
|
||||
modalComponent.props.onClose()
|
||||
expect(onCloseWasCalled).toBeTruthy()
|
||||
childGenerator={() => <div/>}
|
||||
/>)
|
||||
expect(modal.find('DefaultCloseButton').exists()).toBeFalsy()
|
||||
})
|
||||
|
||||
})
|
|
@ -5,6 +5,9 @@ import AriaModal = require('react-aria-modal');
|
|||
import { VelocityTransitionGroup } from 'velocity-react';
|
||||
import 'velocity-animate';
|
||||
import 'velocity-animate/velocity.ui';
|
||||
import { DefaultCloseButton } from './DefaultCloseButton';
|
||||
import BootstrapWrapper from './BootstrapWrapper';
|
||||
import { Row, Column } from './layout';
|
||||
|
||||
export interface ModalProps {
|
||||
onClose?: () => void // if you want your modal to close, this needs to set modalActive to false
|
||||
|
@ -12,25 +15,44 @@ export interface ModalProps {
|
|||
titleText?: string
|
||||
focusDialog?: boolean
|
||||
dialogStyle?: any
|
||||
showCloseButton?: boolean
|
||||
childGenerator: () => any
|
||||
}
|
||||
|
||||
class Modal extends React.Component<ModalProps, {}> {
|
||||
|
||||
static defaultProps = {
|
||||
dialogStyle: { minWidth: '768px' }
|
||||
dialogStyle: { minWidth: '768px' },
|
||||
showCloseButton: true
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const innerModal = this.props.modalActive ? <AriaModal mounted={this.props.modalActive} titleText={this.props.titleText} focusDialog={this.props.focusDialog}
|
||||
onExit={this.props.onClose} dialogStyle={this.props.dialogStyle}>
|
||||
<header className='modal-header'>
|
||||
<h4 className='modal-header-title'>{this.props.titleText}</h4>
|
||||
</header>
|
||||
<div className="modal-body">
|
||||
{this.props.childGenerator()}
|
||||
</div>
|
||||
<BootstrapWrapper>
|
||||
<header className='modal-header' style={{
|
||||
position: 'relative',
|
||||
padding: '12px 10px 12px 20px'
|
||||
}}>
|
||||
<Row>
|
||||
<Column colSpan={11} breakSize={'xs'}>
|
||||
<h3 className='modal-header-title' style={{ margin: 0 }}>{this.props.titleText}</h3>
|
||||
</Column>
|
||||
{this.props.showCloseButton ?
|
||||
<Column colSpan={1} breakSize={'xs'}>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<DefaultCloseButton onClick={() => this.props.onClose()} />
|
||||
</div>
|
||||
</Column> : false
|
||||
}
|
||||
</Row>
|
||||
</header>
|
||||
<div className="modal-body">
|
||||
<div style={{ position: 'relative' }}>
|
||||
{this.props.childGenerator()}
|
||||
</div>
|
||||
</div>
|
||||
</BootstrapWrapper>
|
||||
</AriaModal> : false
|
||||
|
||||
const modal =
|
||||
|
@ -61,7 +83,7 @@ class Modal extends React.Component<ModalProps, {}> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
runOnMount={true}>
|
||||
{innerModal}
|
||||
</VelocityTransitionGroup>;
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import * as React from 'react';
|
||||
import 'jest';
|
||||
import ScreenReaderOnlyText from './ScreenReaderOnlyText'
|
||||
import toJson from 'enzyme-to-json';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
describe('ScreenReaderOnlyText', () => {
|
||||
it('renders properly', () => {
|
||||
let text = shallow(<ScreenReaderOnlyText>Test</ScreenReaderOnlyText>)
|
||||
expect(toJson(text)).toMatchSnapshot()
|
||||
})
|
||||
})
|
29
javascripts/src/components/common/ScreenReaderOnlyText.tsx
Normal file
29
javascripts/src/components/common/ScreenReaderOnlyText.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import * as React from 'react';
|
||||
|
||||
export interface ScreenReaderOnlyTextProps
|
||||
{
|
||||
}
|
||||
|
||||
class ScreenReaderOnlyText extends React.Component<ScreenReaderOnlyTextProps, {}> {
|
||||
|
||||
|
||||
render() {
|
||||
const style:React.CSSProperties = {
|
||||
position: 'absolute',
|
||||
width: '1px',
|
||||
height: '1px',
|
||||
padding: 0,
|
||||
margin: '-1px',
|
||||
overflow: 'hidden',
|
||||
clip: 'rect(0,0,0,0)',
|
||||
border: 0
|
||||
}
|
||||
return <span style={style}>{this.props.children}</span>
|
||||
}
|
||||
}
|
||||
|
||||
export default ScreenReaderOnlyText;
|
||||
|
||||
|
||||
|
|
@ -1,68 +1,418 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Modal active modal displays 1`] = `
|
||||
<VelocityTransitionGroup
|
||||
enter={
|
||||
exports[`Modal active modal displays matches snapshot 1`] = `
|
||||
<Modal
|
||||
childGenerator={[Function]}
|
||||
dialogStyle={
|
||||
Object {
|
||||
"animation": "fadeIn",
|
||||
"style": Object {
|
||||
"left": "0px",
|
||||
"position": "fixed",
|
||||
"top": "0px",
|
||||
"zIndex": "5000",
|
||||
},
|
||||
"minWidth": "768px",
|
||||
}
|
||||
}
|
||||
enterHideStyle={
|
||||
Object {
|
||||
"display": "none",
|
||||
}
|
||||
}
|
||||
enterShowStyle={
|
||||
Object {
|
||||
"display": "",
|
||||
}
|
||||
}
|
||||
leave={
|
||||
Object {
|
||||
"animation": "fadeOut",
|
||||
"style": Object {
|
||||
"left": "0px",
|
||||
"position": "fixed",
|
||||
"top": "0px",
|
||||
"zIndex": "5000",
|
||||
},
|
||||
}
|
||||
}
|
||||
runOnMount={true}
|
||||
focusDialog={true}
|
||||
modalActive={true}
|
||||
onClose={[Function]}
|
||||
showCloseButton={true}
|
||||
titleText="title text"
|
||||
>
|
||||
<Displaced
|
||||
dialogStyle={
|
||||
<VelocityTransitionGroup
|
||||
enter={
|
||||
Object {
|
||||
"minWidth": "768px",
|
||||
"animation": "fadeIn",
|
||||
"style": Object {
|
||||
"left": "0px",
|
||||
"position": "fixed",
|
||||
"top": "0px",
|
||||
"zIndex": "5000",
|
||||
},
|
||||
}
|
||||
}
|
||||
focusDialog={true}
|
||||
mounted={true}
|
||||
onExit={[Function]}
|
||||
titleText="title text"
|
||||
enterHideStyle={
|
||||
Object {
|
||||
"display": "none",
|
||||
}
|
||||
}
|
||||
enterShowStyle={
|
||||
Object {
|
||||
"display": "",
|
||||
}
|
||||
}
|
||||
leave={
|
||||
Object {
|
||||
"animation": "fadeOut",
|
||||
"style": Object {
|
||||
"left": "0px",
|
||||
"position": "fixed",
|
||||
"top": "0px",
|
||||
"zIndex": "5000",
|
||||
},
|
||||
}
|
||||
}
|
||||
runOnMount={true}
|
||||
>
|
||||
<header
|
||||
className="modal-header"
|
||||
<TransitionGroup
|
||||
childFactory={[Function]}
|
||||
component="div"
|
||||
>
|
||||
<h4
|
||||
className="modal-header-title"
|
||||
>
|
||||
title text
|
||||
</h4>
|
||||
</header>
|
||||
<div
|
||||
className="modal-body"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</Displaced>
|
||||
</VelocityTransitionGroup>
|
||||
<div>
|
||||
<Displaced
|
||||
dialogStyle={
|
||||
Object {
|
||||
"minWidth": "768px",
|
||||
}
|
||||
}
|
||||
focusDialog={true}
|
||||
in={true}
|
||||
key=".0"
|
||||
mounted={true}
|
||||
onExit={[Function]}
|
||||
onExited={[Function]}
|
||||
titleText="title text"
|
||||
>
|
||||
<Portal
|
||||
containerInfo={
|
||||
<div>
|
||||
<div>
|
||||
<div
|
||||
style="position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; z-index: 1050; overflow-x: hidden; overflow-y: auto; text-align: center; background: rgba(0, 0, 0, 0.5); cursor: pointer;"
|
||||
>
|
||||
<div
|
||||
aria-label="title text"
|
||||
id="react-aria-modal-dialog"
|
||||
role="dialog"
|
||||
style="display: inline-block; text-align: left; top: 0px; max-width: 100%; cursor: default; outline: 0; min-width: 768px;"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="tw-bs"
|
||||
>
|
||||
<header
|
||||
class="modal-header"
|
||||
style="position: relative; padding: 12px 10px 12px 20px;"
|
||||
>
|
||||
<div
|
||||
class="row"
|
||||
>
|
||||
<h3
|
||||
class="col-xs-11 modal-header-title"
|
||||
style="margin: 0px;"
|
||||
>
|
||||
title text
|
||||
</h3>
|
||||
<div
|
||||
class="col-xs-1 "
|
||||
style="text-align: right;"
|
||||
>
|
||||
<a
|
||||
class="focusable_item"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
height="24"
|
||||
version="1.1"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10.65625"
|
||||
style="stroke: #969696; fill: #FFFFFF;"
|
||||
/>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="9.25"
|
||||
style="fill: #969696;"
|
||||
/>
|
||||
<path
|
||||
d="M 7.130438,16.869562 16.869562,7.130438"
|
||||
style="stroke: #ffffff; stroke-width: 1.375; stroke-linecap: square;"
|
||||
/>
|
||||
<path
|
||||
d="M 16.869562,16.869562 7.130438,7.130438"
|
||||
style="stroke: #ffffff; stroke-width: 1.375; stroke-linecap: square;"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
style="position: absolute; width: 1px; height: 1px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); border: 0px;"
|
||||
>
|
||||
Close modal
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div
|
||||
class="modal-body"
|
||||
>
|
||||
<div
|
||||
style="position: relative;"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Modal
|
||||
dialogId="react-aria-modal-dialog"
|
||||
dialogStyle={
|
||||
Object {
|
||||
"minWidth": "768px",
|
||||
}
|
||||
}
|
||||
escapeExits={true}
|
||||
focusDialog={true}
|
||||
focusTrapPaused={false}
|
||||
in={true}
|
||||
includeDefaultStyles={true}
|
||||
mounted={true}
|
||||
onExit={[Function]}
|
||||
onExited={[Function]}
|
||||
scrollDisabled={true}
|
||||
titleText="title text"
|
||||
underlayClickExits={true}
|
||||
underlayColor="rgba(0,0,0,0.5)"
|
||||
underlayProps={Object {}}
|
||||
>
|
||||
<FocusTrap
|
||||
_createFocusTrap={[Function]}
|
||||
active={true}
|
||||
focusTrapOptions={
|
||||
Object {
|
||||
"escapeDeactivates": true,
|
||||
"initialFocus": "#react-aria-modal-dialog",
|
||||
}
|
||||
}
|
||||
paused={false}
|
||||
tag="div"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
onMouseDown={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"WebkitOverflowScrolling": "touch",
|
||||
"background": "rgba(0,0,0,0.5)",
|
||||
"cursor": "pointer",
|
||||
"height": "100%",
|
||||
"left": 0,
|
||||
"overflowX": "hidden",
|
||||
"overflowY": "auto",
|
||||
"position": "fixed",
|
||||
"textAlign": "center",
|
||||
"top": 0,
|
||||
"width": "100%",
|
||||
"zIndex": 1050,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
aria-label="title text"
|
||||
id="react-aria-modal-dialog"
|
||||
key="b"
|
||||
role="dialog"
|
||||
style={
|
||||
Object {
|
||||
"cursor": "default",
|
||||
"display": "inline-block",
|
||||
"maxWidth": "100%",
|
||||
"minWidth": "768px",
|
||||
"outline": 0,
|
||||
"textAlign": "left",
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
tabIndex="-1"
|
||||
>
|
||||
<BootstrapWrapper>
|
||||
<div
|
||||
className="tw-bs"
|
||||
>
|
||||
<header
|
||||
className="modal-header"
|
||||
style={
|
||||
Object {
|
||||
"padding": "12px 10px 12px 20px",
|
||||
"position": "relative",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Row>
|
||||
<div
|
||||
className="row"
|
||||
>
|
||||
<Column
|
||||
breakSize="xs"
|
||||
colSpan={11}
|
||||
>
|
||||
<h3
|
||||
className="col-xs-11 modal-header-title"
|
||||
style={
|
||||
Object {
|
||||
"margin": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
title text
|
||||
</h3>
|
||||
</Column>
|
||||
<Column
|
||||
breakSize="xs"
|
||||
colSpan={1}
|
||||
>
|
||||
<div
|
||||
className="col-xs-1 "
|
||||
style={
|
||||
Object {
|
||||
"textAlign": "right",
|
||||
}
|
||||
}
|
||||
>
|
||||
<DefaultCloseButton
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Transition
|
||||
appear={false}
|
||||
enter={true}
|
||||
exit={true}
|
||||
in={false}
|
||||
mountOnEnter={false}
|
||||
onEnter={[Function]}
|
||||
onEntered={[Function]}
|
||||
onEntering={[Function]}
|
||||
onExit={[Function]}
|
||||
onExited={[Function]}
|
||||
onExiting={[Function]}
|
||||
timeout={250}
|
||||
unmountOnExit={false}
|
||||
>
|
||||
<a
|
||||
className="focusable_item"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
tabIndex={0}
|
||||
>
|
||||
<Component
|
||||
backgroundCircleStyle={
|
||||
Object {
|
||||
"fill": "#FFFFFF",
|
||||
"stroke": "#969696",
|
||||
}
|
||||
}
|
||||
foregroundCircleStyle={
|
||||
Object {
|
||||
"fill": "#969696",
|
||||
}
|
||||
}
|
||||
>
|
||||
<svg
|
||||
height="24"
|
||||
version="1.1"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10.65625"
|
||||
style={
|
||||
Object {
|
||||
"fill": "#FFFFFF",
|
||||
"stroke": "#969696",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="9.25"
|
||||
style={
|
||||
Object {
|
||||
"fill": "#969696",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<path
|
||||
d="M 7.130438,16.869562 16.869562,7.130438"
|
||||
style={
|
||||
Object {
|
||||
"stroke": "#ffffff",
|
||||
"strokeLinecap": "square",
|
||||
"strokeWidth": 1.375,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<path
|
||||
d="M 16.869562,16.869562 7.130438,7.130438"
|
||||
style={
|
||||
Object {
|
||||
"stroke": "#ffffff",
|
||||
"strokeLinecap": "square",
|
||||
"strokeWidth": 1.375,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</svg>
|
||||
</Component>
|
||||
<ScreenReaderOnlyText>
|
||||
<span
|
||||
style={
|
||||
Object {
|
||||
"border": 0,
|
||||
"clip": "rect(0,0,0,0)",
|
||||
"height": "1px",
|
||||
"margin": "-1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "absolute",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Close modal
|
||||
</span>
|
||||
</ScreenReaderOnlyText>
|
||||
</a>
|
||||
</Transition>
|
||||
</DefaultCloseButton>
|
||||
</div>
|
||||
</Column>
|
||||
</div>
|
||||
</Row>
|
||||
</header>
|
||||
<div
|
||||
className="modal-body"
|
||||
>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"position": "relative",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BootstrapWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FocusTrap>
|
||||
</Modal>
|
||||
</Portal>
|
||||
</Displaced>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</VelocityTransitionGroup>
|
||||
</Modal>
|
||||
`;
|
||||
|
||||
exports[`Modal nothing displayed if inactive 1`] = `
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ScreenReaderOnlyText renders properly 1`] = `
|
||||
<span
|
||||
style={
|
||||
Object {
|
||||
"border": 0,
|
||||
"clip": "rect(0,0,0,0)",
|
||||
"height": "1px",
|
||||
"margin": "-1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "absolute",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Test
|
||||
</span>
|
||||
`;
|
|
@ -0,0 +1,142 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`layout Row renders multiple children properly 1`] = `
|
||||
<Row>
|
||||
<div
|
||||
className="row"
|
||||
>
|
||||
<SimpleCheckerComponent>
|
||||
<div />
|
||||
</SimpleCheckerComponent>
|
||||
<SimpleCheckerComponent>
|
||||
<div />
|
||||
</SimpleCheckerComponent>
|
||||
</div>
|
||||
</Row>
|
||||
`;
|
||||
|
||||
exports[`layout Row renders single child properly 1`] = `
|
||||
<Row>
|
||||
<div
|
||||
className="row"
|
||||
>
|
||||
<SimpleCheckerComponent>
|
||||
<div />
|
||||
</SimpleCheckerComponent>
|
||||
</div>
|
||||
</Row>
|
||||
`;
|
||||
|
||||
exports[`layout ThreeColumnFields renders multiple children properly 1`] = `
|
||||
<ThreeColumnFields>
|
||||
<Row>
|
||||
<div
|
||||
className="row"
|
||||
>
|
||||
<Column
|
||||
breakSize="sm"
|
||||
colSpan={4}
|
||||
>
|
||||
<SimpleCheckerComponent
|
||||
className="col-sm-4 u-paddingRight--10"
|
||||
>
|
||||
<div />
|
||||
</SimpleCheckerComponent>
|
||||
</Column>
|
||||
<Column
|
||||
breakSize="sm"
|
||||
colSpan={4}
|
||||
>
|
||||
<SimpleCheckerComponent
|
||||
className="col-sm-4 u-paddingRight--10"
|
||||
>
|
||||
<div />
|
||||
</SimpleCheckerComponent>
|
||||
</Column>
|
||||
<Column
|
||||
breakSize="sm"
|
||||
colSpan={4}
|
||||
>
|
||||
<SimpleCheckerComponent
|
||||
className="col-sm-4 "
|
||||
>
|
||||
<div />
|
||||
</SimpleCheckerComponent>
|
||||
</Column>
|
||||
</div>
|
||||
</Row>
|
||||
</ThreeColumnFields>
|
||||
`;
|
||||
|
||||
exports[`layout ThreeColumnFields renders single child properly 1`] = `
|
||||
<ThreeColumnFields>
|
||||
<Row>
|
||||
<div
|
||||
className="row"
|
||||
>
|
||||
<Column
|
||||
breakSize="sm"
|
||||
colSpan={4}
|
||||
>
|
||||
<SimpleCheckerComponent
|
||||
className="col-sm-4 "
|
||||
>
|
||||
<div />
|
||||
</SimpleCheckerComponent>
|
||||
</Column>
|
||||
</div>
|
||||
</Row>
|
||||
</ThreeColumnFields>
|
||||
`;
|
||||
|
||||
exports[`layout TwoColumnFields renders multiple children properly 1`] = `
|
||||
<TwoColumnFields>
|
||||
<Row>
|
||||
<div
|
||||
className="row"
|
||||
>
|
||||
<Column
|
||||
breakSize="sm"
|
||||
colSpan={6}
|
||||
>
|
||||
<SimpleCheckerComponent
|
||||
className="col-sm-6 u-paddingRight--10"
|
||||
>
|
||||
<div />
|
||||
</SimpleCheckerComponent>
|
||||
</Column>
|
||||
<Column
|
||||
breakSize="sm"
|
||||
colSpan={6}
|
||||
>
|
||||
<SimpleCheckerComponent
|
||||
className="col-sm-6 "
|
||||
>
|
||||
<div />
|
||||
</SimpleCheckerComponent>
|
||||
</Column>
|
||||
</div>
|
||||
</Row>
|
||||
</TwoColumnFields>
|
||||
`;
|
||||
|
||||
exports[`layout TwoColumnFields renders single child properly 1`] = `
|
||||
<TwoColumnFields>
|
||||
<Row>
|
||||
<div
|
||||
className="row"
|
||||
>
|
||||
<Column
|
||||
breakSize="sm"
|
||||
colSpan={6}
|
||||
>
|
||||
<SimpleCheckerComponent
|
||||
className="col-sm-6 "
|
||||
>
|
||||
<div />
|
||||
</SimpleCheckerComponent>
|
||||
</Column>
|
||||
</div>
|
||||
</Row>
|
||||
</TwoColumnFields>
|
||||
`;
|
107
javascripts/src/components/common/layout.spec.tsx
Normal file
107
javascripts/src/components/common/layout.spec.tsx
Normal file
|
@ -0,0 +1,107 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import * as React from 'react';
|
||||
import 'jest';
|
||||
import Modal, { ModalProps } from './Modal'
|
||||
import { shallow, mount, ReactWrapper } from "enzyme";
|
||||
import toJson from "enzyme-to-json";
|
||||
import { DefaultCloseButton } from './DefaultCloseButton';
|
||||
import { Column, Row, ThreeColumnFields, TwoColumnFields } from './layout';
|
||||
|
||||
const SimpleCheckerComponent = (props: { className?: string }) => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
describe('layout', () => {
|
||||
describe('Column', () => {
|
||||
it('sets proper class without none passed in', () => {
|
||||
const column = shallow(<Column colSpan={12} breakSize={'sm'}>
|
||||
<SimpleCheckerComponent />
|
||||
</Column>)
|
||||
|
||||
expect(column.find(SimpleCheckerComponent).props()['className']).toBe('col-sm-12 ')
|
||||
})
|
||||
|
||||
it('sets proper class with one passed in', () => {
|
||||
const column = shallow(<Column colSpan={1} breakSize={'lg'}>
|
||||
<SimpleCheckerComponent className="another_class" />
|
||||
</Column>)
|
||||
|
||||
expect(column.find(SimpleCheckerComponent).props()['className'])
|
||||
.toBe('col-lg-1 another_class')
|
||||
})
|
||||
|
||||
it('sets proper display name', () => {
|
||||
const column = shallow(<Column colSpan={1} breakSize={'lg'}>
|
||||
<SimpleCheckerComponent className="another_class" />
|
||||
</Column>)
|
||||
expect(Column.displayName).toBe('Column')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Row', () => {
|
||||
it('renders single child properly', () => {
|
||||
const row = mount(<Row>
|
||||
<SimpleCheckerComponent/>
|
||||
</Row>)
|
||||
|
||||
expect(toJson(row)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
|
||||
it('renders multiple children properly', () => {
|
||||
const row = mount(<Row>
|
||||
<SimpleCheckerComponent/>
|
||||
<SimpleCheckerComponent/>
|
||||
</Row>)
|
||||
|
||||
expect(toJson(row)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('has correct display name', () => {
|
||||
const row = mount(<Row>
|
||||
<SimpleCheckerComponent/>
|
||||
</Row>)
|
||||
|
||||
expect(Row.displayName).toBe('Row')
|
||||
})
|
||||
})
|
||||
|
||||
describe('ThreeColumnFields',() => {
|
||||
it('renders single child properly', () => {
|
||||
const fields = mount(<ThreeColumnFields>
|
||||
<SimpleCheckerComponent/>
|
||||
</ThreeColumnFields>)
|
||||
|
||||
expect(toJson(fields)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('renders multiple children properly', () => {
|
||||
const fields = mount(<ThreeColumnFields>
|
||||
<SimpleCheckerComponent/>
|
||||
<SimpleCheckerComponent/>
|
||||
<SimpleCheckerComponent/>
|
||||
</ThreeColumnFields>)
|
||||
|
||||
expect(toJson(fields)).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('TwoColumnFields',() => {
|
||||
it('renders single child properly', () => {
|
||||
const fields = mount(<TwoColumnFields>
|
||||
<SimpleCheckerComponent/>
|
||||
</TwoColumnFields>)
|
||||
|
||||
expect(toJson(fields)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('renders multiple children properly', () => {
|
||||
const fields = mount(<TwoColumnFields>
|
||||
<SimpleCheckerComponent/>
|
||||
<SimpleCheckerComponent/>
|
||||
</TwoColumnFields>)
|
||||
|
||||
expect(toJson(fields)).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
|
@ -3,36 +3,78 @@ import * as React from 'react';
|
|||
import {observer} from "mobx-react";
|
||||
import * as _ from 'lodash'
|
||||
|
||||
export const TwoColumnFields = observer((props:{children:Array<React.ReactElement<any>>}) => {
|
||||
return <div className="row">
|
||||
function arrayify<T>(items: Array<T>|T){
|
||||
return items instanceof Array ? items : [items]
|
||||
}
|
||||
|
||||
export const TwoColumnFields: React.StatelessComponent<{}> = (props:{children:Array<React.ReactElement<any>>|React.ReactElement<any>}) => {
|
||||
const children = arrayify(props.children)
|
||||
return <Row>
|
||||
{
|
||||
_.take(props.children, 2).map((i:React.ReactElement<any>) => {
|
||||
let className = "col-sm-6"
|
||||
if (_.last(props.children) !== i){
|
||||
children.map((i:React.ReactElement<any>) => {
|
||||
let className = ""
|
||||
if (_.last(children) !== i){
|
||||
className += " u-paddingRight--10"
|
||||
}
|
||||
if (i.props['className']){
|
||||
className += i.props['className']
|
||||
}
|
||||
|
||||
return React.cloneElement(i, {wrapperClassName: className})
|
||||
return <Column colSpan={6} breakSize={'sm'}>
|
||||
{React.cloneElement(i, {className: className})}
|
||||
</Column>
|
||||
})}
|
||||
</div>
|
||||
})
|
||||
</Row>
|
||||
}
|
||||
|
||||
export const ThreeColumnFields = observer((props:{children:React.ReactElement<any>[]}) => {
|
||||
return <div className="row">
|
||||
TwoColumnFields.displayName = 'TwoColumnFields'
|
||||
|
||||
export const ThreeColumnFields: React.StatelessComponent<{}> = (props:{children:Array<React.ReactElement<any>>|React.ReactElement<any>}) => {
|
||||
const children = arrayify(props.children)
|
||||
return <Row>
|
||||
{
|
||||
_.take(props.children, 3).map((i:React.ReactElement<any>) => {
|
||||
let className = "col-sm-4"
|
||||
if (_.last(props.children) !== i){
|
||||
children.map((i:React.ReactElement<any>) => {
|
||||
let className = ""
|
||||
if (_.last(children) !== i){
|
||||
className += " u-paddingRight--10"
|
||||
}
|
||||
if (i.props['className']){
|
||||
className += i.props['className']
|
||||
}
|
||||
|
||||
return React.cloneElement(i, {wrapperClassName: className})
|
||||
return <Column colSpan={4} breakSize={'sm'}>
|
||||
{React.cloneElement(i, {className: className})}
|
||||
</Column>
|
||||
})}
|
||||
</Row>
|
||||
}
|
||||
|
||||
ThreeColumnFields.displayName = 'ThreeColumnFields'
|
||||
|
||||
|
||||
export const Row: React.StatelessComponent<{}> = (props:{children:Array<React.ReactElement<any>>|React.ReactElement<any>}) => {
|
||||
return <div className="row">
|
||||
{props.children}
|
||||
</div>
|
||||
})
|
||||
}
|
||||
|
||||
Row.displayName = 'Row'
|
||||
|
||||
type ColumnBreakSize = 'xs'| 'sm'|'md'|'lg'
|
||||
type ColumnSpan = 1|2|3|4|5|6|7|8|9|10|11|12
|
||||
|
||||
interface ColumnProps {
|
||||
children:React.ReactElement<any>
|
||||
colSpan:ColumnSpan,
|
||||
breakSize:ColumnBreakSize
|
||||
}
|
||||
|
||||
export const Column: React.StatelessComponent<ColumnProps> = (props) => {
|
||||
let className = `col-${props.breakSize}-${props.colSpan} `
|
||||
props.children.props
|
||||
if (props.children.props.className){
|
||||
className += props.children.props['className']
|
||||
}
|
||||
|
||||
return React.cloneElement(props.children, {className: className})
|
||||
}
|
||||
|
||||
Column.displayName = 'Column'
|
||||
|
|
39
javascripts/src/components/common/svg/CloseButton.tsx
Normal file
39
javascripts/src/components/common/svg/CloseButton.tsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import React = require("react");
|
||||
interface CloseButtonProps {
|
||||
backgroundCircleStyle:React.CSSProperties
|
||||
foregroundCircleStyle:React.CSSProperties
|
||||
}
|
||||
|
||||
export const CloseButton = (props: CloseButtonProps) => {
|
||||
return <svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1">
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10.65625"
|
||||
style={props.backgroundCircleStyle} />
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="9.25"
|
||||
style={props.foregroundCircleStyle} />
|
||||
<path
|
||||
d="M 7.130438,16.869562 16.869562,7.130438"
|
||||
style={{
|
||||
stroke: '#ffffff',
|
||||
strokeWidth:1.375,
|
||||
strokeLinecap:'square'
|
||||
}} />
|
||||
<path
|
||||
d="M 16.869562,16.869562 7.130438,7.130438"
|
||||
style={{
|
||||
stroke: '#ffffff',
|
||||
strokeWidth:1.375,
|
||||
strokeLinecap:'square'
|
||||
}} />
|
||||
</svg>
|
||||
}
|
6
javascripts/src/components/common/svg/checkbox.tsx
Normal file
6
javascripts/src/components/common/svg/checkbox.tsx
Normal file
|
@ -0,0 +1,6 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import * as React from 'react';
|
||||
|
||||
export const Checkbox = (props:React.SVGProps<SVGPathElement>) => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="10px" width="10px"><path d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z" {...props}/></svg>
|
||||
}
|
171
package-lock.json
generated
171
package-lock.json
generated
|
@ -204,17 +204,17 @@
|
|||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.1.tgz",
|
||||
"integrity": "sha512-7jGW8ppV0ant637pIqAcFfQDDH1orEPGJb8aXfUozuCU3QqX7rX4DA8iwrbPrR1hcH0FTTHz47yQnk+bl5xHQA==",
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz",
|
||||
"integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.12.0"
|
||||
"regenerator-runtime": "^0.13.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"regenerator-runtime": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
|
||||
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
|
||||
"version": "0.13.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
|
||||
"integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -4974,6 +4974,30 @@
|
|||
"integrity": "sha512-+T9qBbqe/jXtTjzVddArZExahoPPmt8eq3O1ZuCKZXjBVxf/ciUYNXrIDZJEVgYvpELnv6VlPRCfLzufRxpAag==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/color": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.0.tgz",
|
||||
"integrity": "sha512-5qqtNia+m2I0/85+pd2YzAXaTyKO8j+svirO5aN+XaQJ5+eZ8nx0jPtEWZLxCi50xwYsX10xUHetFzfb1WEs4Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/color-convert": "*"
|
||||
}
|
||||
},
|
||||
"@types/color-convert": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-1.9.0.tgz",
|
||||
"integrity": "sha512-OKGEfULrvSL2VRbkl/gnjjgbbF7ycIlpSsX7Nkab4MOWi5XxmgBYvuiQ7lcCFY5cPDz7MUNaKgxte2VRmtr4Fg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/color-name": "*"
|
||||
}
|
||||
},
|
||||
"@types/color-name": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/enzyme": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.1.10.tgz",
|
||||
|
@ -5104,6 +5128,15 @@
|
|||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-transition-group": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.9.0.tgz",
|
||||
"integrity": "sha512-hP7vUaZMVSWKxo133P8U51U6UZ7+pbY+eAQb8+p6SZ2rB1rj3mOTDgTzhhi+R2SCB4S+sWekAAGoxdiZPG0ReQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/sinon": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-4.3.3.tgz",
|
||||
|
@ -7402,7 +7435,8 @@
|
|||
"clone": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
|
||||
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
|
||||
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
|
||||
"dev": true
|
||||
},
|
||||
"clone-deep": {
|
||||
"version": "2.0.2",
|
||||
|
@ -7448,21 +7482,29 @@
|
|||
}
|
||||
},
|
||||
"color": {
|
||||
"version": "0.11.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-0.11.3.tgz",
|
||||
"integrity": "sha1-S60dDVJJndANvW8IaEQkZ+STlOY=",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-3.1.0.tgz",
|
||||
"integrity": "sha512-CwyopLkuRYO5ei2EpzpIh6LqJMt6Mt+jZhO5VI5f/wJLZriXQE32/SSqzmrh+QB+AZT81Cj8yv+7zwToW8ahZg==",
|
||||
"requires": {
|
||||
"clone": "^1.0.2",
|
||||
"color-convert": "^1.3.0",
|
||||
"color-string": "^0.3.0"
|
||||
"color-convert": "^1.9.1",
|
||||
"color-string": "^1.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"color-convert": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
|
||||
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"requires": {
|
||||
"color-name": "^1.1.1"
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-string": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
|
||||
"integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
|
||||
"requires": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7481,6 +7523,7 @@
|
|||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz",
|
||||
"integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "^1.0.0"
|
||||
}
|
||||
|
@ -7494,6 +7537,28 @@
|
|||
"color": "^0.11.0",
|
||||
"css-color-names": "0.0.4",
|
||||
"has": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"color": {
|
||||
"version": "0.11.4",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz",
|
||||
"integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"clone": "^1.0.2",
|
||||
"color-convert": "^1.3.0",
|
||||
"color-string": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"colors": {
|
||||
|
@ -7853,6 +7918,26 @@
|
|||
"integrity": "sha1-tQS9BYabOSWd0MXvw12EMXbczEo=",
|
||||
"dev": true
|
||||
},
|
||||
"color": {
|
||||
"version": "0.11.4",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz",
|
||||
"integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"clone": "^1.0.2",
|
||||
"color-convert": "^1.3.0",
|
||||
"color-string": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
|
@ -15239,6 +15324,28 @@
|
|||
"postcss": "^5.0.4",
|
||||
"postcss-message-helpers": "^2.0.0",
|
||||
"reduce-function-call": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"color": {
|
||||
"version": "0.11.4",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz",
|
||||
"integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"clone": "^1.0.2",
|
||||
"color-convert": "^1.3.0",
|
||||
"color-string": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"postcss-color-hex-alpha": {
|
||||
|
@ -16732,11 +16839,11 @@
|
|||
}
|
||||
},
|
||||
"react-transition-group": {
|
||||
"version": "2.5.3",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.3.tgz",
|
||||
"integrity": "sha512-2DGFck6h99kLNr8pOFk+z4Soq3iISydwOFeeEVPjTN6+Y01CmvbWmnN02VuTWyFdnRtIDPe+wy2q6Ui8snBPZg==",
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
|
||||
"integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
|
||||
"requires": {
|
||||
"dom-helpers": "^3.3.1",
|
||||
"dom-helpers": "^3.4.0",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-lifecycles-compat": "^3.0.4"
|
||||
|
@ -16759,11 +16866,6 @@
|
|||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.8.1"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.8.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.1.tgz",
|
||||
"integrity": "sha512-ioMCzVDWvCvKD8eeT+iukyWrBGrA3DiFYkXfBsVYIRdaREZuBjENG+KjrikavCLasozqRWTwFUagU/O4vPpRMA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -17507,6 +17609,21 @@
|
|||
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
|
||||
"dev": true
|
||||
},
|
||||
"simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
|
||||
"requires": {
|
||||
"is-arrayish": "^0.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-arrayish": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"sinon": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-5.0.7.tgz",
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
"generate-api-js": "rm -rf javascripts/api && npm run get-codegen-cli && npm run generate-openapi && java -jar .bin/swagger-codegen-cli.jar generate -l 'typescript-jquery' -i tmp/openapi.json -o javascripts/api --config 'swagger.json' -t lib/swagger-typescript-jquery"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/color": "^3.0.0",
|
||||
"@types/enzyme": "^3.1.9",
|
||||
"@types/enzyme-to-json": "^1.5.1",
|
||||
"@types/es6-promise": "^3.3.0",
|
||||
|
@ -32,6 +33,7 @@
|
|||
"@types/react-intl": "^2.3.7",
|
||||
"@types/react-test-renderer": "^16.0.1",
|
||||
"@types/react-text-mask": "^5.4.2",
|
||||
"@types/react-transition-group": "^2.9.0",
|
||||
"@types/sinon": "^4.3.3",
|
||||
"@types/validator": "^9.4.1",
|
||||
"@types/velocity-animate": "^1.2.33",
|
||||
|
@ -87,7 +89,7 @@
|
|||
"attr-binder": "0.3.1",
|
||||
"aws-sdk": "^2.402.0",
|
||||
"chart.js": "2.1.4",
|
||||
"color": "0.11.3",
|
||||
"color": "^3.1.0",
|
||||
"commons.css": "0.1.8",
|
||||
"cookie": "0.3.1",
|
||||
"cropperjs": "1.0.0-beta.2",
|
||||
|
@ -138,6 +140,7 @@
|
|||
"react-dom": "^16.3.1",
|
||||
"react-intl": "^2.4.0",
|
||||
"react-text-mask": "^5.3.0",
|
||||
"react-transition-group": "^2.9.0",
|
||||
"shuffle-array": "1.0.1",
|
||||
"snabbdom": "0.3.0",
|
||||
"snake-case": "2.1.0",
|
||||
|
|
Loading…
Reference in a new issue