├── .gitignore
├── Actions
└── index.js
├── Animations
├── CommonAnimationProps.js
├── CommonOpacityProps.js
├── Fade
│ ├── Down.js
│ ├── DownBig.js
│ ├── Left.js
│ ├── LeftBig.js
│ ├── Right.js
│ ├── RightBig.js
│ ├── Up.js
│ ├── UpBig.js
│ └── index.js
├── Fly.js
├── Rotate
│ ├── ClockLeft.js
│ ├── ClockUp.js
│ ├── CounterClock.js
│ ├── CounterClockRight.js
│ ├── CounterClockUp.js
│ └── index.js
├── Zoom.js
└── index.js
├── Architecture
└── index.js
├── CONTRIBUTING.md
├── Components
├── FormattedInput.js
├── NestedValue.js
├── PatternedInput.js
├── ValidatingForm.js
└── ValidatingInput.js
├── LICENSE
├── README.md
├── React
├── CSSTransitionGroup.js
├── LinkedStateMixin.js
├── Perf.js
├── TestUtils.js
├── TransitionGroup.js
├── batchedUpdates.js
├── classSet.js
├── cloneWithProps.js
├── index.js
└── update.js
├── Router
├── DefaultRoute.js
├── HashLocation.js
├── History.js
├── HistoryLocation.js
├── ImitateBrowserBehavior.js
├── Link.js
├── Navigation.js
├── NotFoundRoute.js
├── Redirect.js
├── RefreshLocation.js
├── Route.js
├── RouteHandler.js
├── ScrollToBehavior.js
├── State.js
├── create.js
└── run.js
├── STYLE-GUIDE.md
├── Stores
├── ActionStores.js
└── index.js
├── Tuxedo.js
├── Tuxx-full-logo.png
├── Tuxx-icon.png
├── __tests__
└── TuxxModules-test.js
├── docs
├── ApiMenu.json
├── Getting-Started.md
├── Overview.md
├── Tuxedo.md
├── TuxxActionStores.md
├── TuxxActions.md
├── TuxxAnimations.md
├── TuxxArchitecture.md
├── TuxxModularity.md
├── TuxxMutableClass.md
├── TuxxOwneeClass.md
├── TuxxOwnerClass.md
├── TuxxReact.md
├── TuxxRouter.md
└── TuxxStores.md
├── jest-preprocessor.js
├── package.json
└── src
├── Dispatcher.js
├── TuxxActionStore.js
├── TuxxActions.js
├── TuxxAnimationEasings.js
├── TuxxAnimations.js
├── TuxxArchitectStores.js
├── TuxxDeepSearch.js
├── TuxxGetOwnerPropsMixin.js
├── TuxxInvariant.js
├── TuxxMutableClass.js
├── TuxxMutableRenderMixin.js
├── TuxxOwneeClass.js
├── TuxxOwnerClass.js
├── TuxxPropTypeCheckerMixin.js
├── TuxxStore.js
├── TuxxStoreMixinGenerator.js
└── __tests__
├── TuxxActionStore-test.js
├── TuxxActions-test.js
├── TuxxAnimations-test.js
├── TuxxArchitectStores-test.js
├── TuxxDeepSearch-test.js
├── TuxxGetOwnerPropsMixin-test.js
├── TuxxMutableClass-test.js
├── TuxxMutableRenderMixin-test.js
├── TuxxOwneeClass-test.js
├── TuxxOwnerClass-test.js
├── TuxxPropTypeCheckerMixin-test.js
├── TuxxStore-test.js
├── TuxxStoreMixinGenerator-test.js
└── testComponents.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.log
3 | build/config.gypi
4 |
--------------------------------------------------------------------------------
/Actions/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('tuxx/src/TuxxActions');
2 |
--------------------------------------------------------------------------------
/Animations/CommonAnimationProps.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //Holds CSS properties which are shared among all animations
4 | var CommonAnimationProps = {
5 | enter: {
6 | 'transition-duration': '.5s',
7 | 'transition-timing-function': 'linear'
8 | },
9 | leave: {
10 | 'transition-duration': '.5s',
11 | 'transition-timing-function': 'linear'
12 | }
13 | };
14 |
15 | module.exports = CommonAnimationProps;
16 |
--------------------------------------------------------------------------------
/Animations/CommonOpacityProps.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var assign = require('object-assign');
4 | var CommonAnimationProps = require('./CommonAnimationProps');
5 |
6 | //Holds CSS properties which are shared among all Fade animations
7 | var CommonOpacityProps = {
8 | enter: assign({}, CommonAnimationProps.enter, {
9 | 'opacity': '0.01'
10 | }),
11 | 'enter-active': {
12 | 'opacity': '1'
13 | },
14 | leave: assign({}, CommonAnimationProps.leave, {
15 | 'opacity': '1'
16 | }),
17 | 'leave-active': {
18 | 'opacity': '0.01'
19 | }
20 | };
21 |
22 | module.exports = CommonOpacityProps;
23 |
--------------------------------------------------------------------------------
/Animations/Fade/Down.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 | //Default FadeDown animation component
7 | var FadeDown = {
8 | //Class name given to the animation component once mounted
9 | className: 'fadeDown',
10 | //CSS for wrapped component on entry
11 | enter: assign({}, CommonOpacityProps.enter, {
12 | 'transform': 'translateY(-20px)'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transform': 'translateY(0)'
17 | }),
18 | //CSS for wrapped component on leave
19 | leave: assign({}, CommonOpacityProps.leave, {
20 | 'transform': 'translateY(0)'
21 | }),
22 | //CSS for wrapped component when leave animation completes
23 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
24 | 'transform': 'translateY(-20px)'
25 | })
26 | };
27 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
28 | module.exports = createAnimation(FadeDown);
29 |
--------------------------------------------------------------------------------
/Animations/Fade/DownBig.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 | //Default FadeDownBig animation component
7 | var FadeDownBig = {
8 | //Class name given to the animation component once mounted
9 | className: 'fadeDownBig',
10 | //CSS for wrapped component on entry
11 | enter: assign({}, CommonOpacityProps.enter, {
12 | 'transform': 'translateY(-200px)'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transform': 'translateY(0)'
17 | }),
18 | //CSS for wrapped component on leave
19 | leave: assign({}, CommonOpacityProps.leave, {
20 | 'transform': 'translateY(0)'
21 | }),
22 | //CSS for wrapped component when leave animation completes
23 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
24 | 'transform': 'translateY(-200px)'
25 | })
26 | };
27 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
28 | module.exports = createAnimation(FadeDownBig);
29 |
--------------------------------------------------------------------------------
/Animations/Fade/Left.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 | //Default FadeLeft animation component
7 | var FadeLeft = {
8 | //Class name given to the animation component once mounted
9 | className: 'fadeLeft',
10 | //CSS for wrapped component on entry
11 | enter: assign({}, CommonOpacityProps.enter, {
12 | 'transform': 'translateX(-20px)'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transform': 'translateX(0)'
17 | }),
18 | //CSS for wrapped component on leave
19 | leave: assign({}, CommonOpacityProps.leave, {
20 | 'transform': 'translateX(0)'
21 | }),
22 | //CSS for wrapped component when leave animation completes
23 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
24 | 'transform': 'translateX(-20px)'
25 | })
26 | };
27 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
28 | module.exports = createAnimation(FadeLeft);
29 |
--------------------------------------------------------------------------------
/Animations/Fade/LeftBig.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 | //Default FadeLeftBig animation component
7 | var FadeLeftBig = {
8 | //Class name given to the animation component once mounted
9 | className: 'fadeLeftBig',
10 | //CSS for wrapped component on entry
11 | enter: assign({}, CommonOpacityProps.enter, {
12 | 'transform': 'translateX(-200px)'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transform': 'translateX(0)'
17 | }),
18 | //CSS for wrapped component on leave
19 | leave: assign({}, CommonOpacityProps.leave, {
20 | 'transform': 'translateX(0)'
21 | }),
22 | //CSS for wrapped component when leave animation completes
23 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
24 | 'transform': 'translateX(-200px)'
25 | })
26 | };
27 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
28 | module.exports = createAnimation(FadeLeftBig);
29 |
--------------------------------------------------------------------------------
/Animations/Fade/Right.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 | //Default FadeRight animation component
7 | var FadeRight = {
8 | //Class name given to the animation component once mounted
9 | className: 'fadeRight',
10 | //CSS for wrapped component on entry
11 | enter: assign({}, CommonOpacityProps.enter, {
12 | 'transform': 'translateX(20px)'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transform': 'translateX(0)'
17 | }),
18 | //CSS for wrapped component on leave
19 | leave: assign({}, CommonOpacityProps.leave, {
20 | 'transform': 'translateX(0)'
21 | }),
22 | //CSS for wrapped component when leave animation completes
23 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
24 | 'transform': 'translateX(20px)'
25 | })
26 | };
27 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
28 | module.exports = createAnimation(FadeRight);
29 |
--------------------------------------------------------------------------------
/Animations/Fade/RightBig.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 | //Default FadeRight animation component
7 | var FadeRightBig = {
8 | //Class name given to the animation component once mounted
9 | className: 'fadeRightBig',
10 | //CSS for wrapped component on entry
11 | enter: assign({}, CommonOpacityProps.enter, {
12 | 'transform': 'translateX(200px)'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transform': 'translateX(0)'
17 | }),
18 | //CSS for wrapped component on leave
19 | leave: assign({}, CommonOpacityProps.leave, {
20 | 'transform': 'translateX(0)'
21 | }),
22 | //CSS for wrapped component when leave animation completes
23 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
24 | 'transform': 'translateX(200px)'
25 | })
26 | };
27 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
28 | module.exports = createAnimation(FadeRightBig);
29 |
--------------------------------------------------------------------------------
/Animations/Fade/Up.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 | //Default FadeUp animation component
7 | var FadeUp = {
8 | //Class name given to the animation component once mounted
9 | className: 'fadeUp',
10 | //CSS for wrapped component on entry
11 | enter: assign({}, CommonOpacityProps.enter, {
12 | 'transform': 'translateY(20px)'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transform': 'translateY(0)'
17 | }),
18 | //CSS for wrapped component on leave
19 | leave: assign({}, CommonOpacityProps.leave, {
20 | 'transform': 'translateY(0)'
21 | }),
22 | //CSS for wrapped component when leave animation completes
23 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
24 | 'transform': 'translateY(20px)'
25 | })
26 | };
27 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
28 | module.exports = createAnimation(FadeUp);
29 |
--------------------------------------------------------------------------------
/Animations/Fade/UpBig.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 | //Default FadeUpBig animation component
7 | var FadeUpBig = {
8 | //Class name given to the animation component once mounted
9 | className: 'fadeUpBig',
10 | //CSS for wrapped component on entry
11 | enter: assign({}, CommonOpacityProps.enter, {
12 | 'transform': 'translateY(200px)'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transform': 'translateY(0)'
17 | }),
18 | //CSS for wrapped component on leave
19 | leave: assign({}, CommonOpacityProps.leave, {
20 | 'transform': 'translateY(0)'
21 | }),
22 | //CSS for wrapped component when leave animation completes
23 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
24 | 'transform': 'translateY(200px)'
25 | })
26 | };
27 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
28 | module.exports = createAnimation(FadeUpBig);
29 |
--------------------------------------------------------------------------------
/Animations/Fade/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 | //Default Fade animation component
7 | var Fade = {
8 | //Class name given to the animation component once mounted
9 | className: 'fade',
10 | //CSS for wrapped component on entry
11 | enter: CommonOpacityProps.enter,
12 | //CSS for wrapped component when entry animation completes
13 | 'enter-active': CommonOpacityProps['enter-active'],
14 | //CSS for wrapped component on leave
15 | leave: CommonOpacityProps.leave,
16 | //CSS for wrapped component when leave animation completes
17 | 'leave-active': CommonOpacityProps['leave-active']
18 | };
19 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
20 | module.exports = createAnimation(Fade);
21 |
--------------------------------------------------------------------------------
/Animations/Fly.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 | //Default Fly animation component
7 | var Fly = {
8 | //Class name given to the animation component once mounted
9 | className: 'fly',
10 | //CSS for wrapped component on entry
11 | enter: assign({}, CommonOpacityProps.enter, {
12 | 'transform': 'translateY(-2000px)'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transform': 'translateY(0px)'
17 | }),
18 | //CSS for wrapped component on leave
19 | leave: assign({}, CommonOpacityProps.leave, {
20 | 'transform': 'translateY(0px)'
21 | }),
22 | //CSS for wrapped component when leave animation completes
23 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
24 | 'transform': 'translateY(-2000px)'
25 | })
26 | };
27 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
28 | module.exports = createAnimation(Fly);
29 |
--------------------------------------------------------------------------------
/Animations/Rotate/ClockLeft.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 |
7 | var ClockLeft = {
8 | className: 'clockLeft',
9 | //CSS for wrapped component on entry
10 | enter: assign({}, CommonOpacityProps.enter, {
11 | 'transform': 'rotate(-90deg)',
12 | 'transformOrigin': 'left bottom'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transformOrigin': 'left bottom',
17 | 'transform': 'rotate(0)'
18 | }),
19 | //CSS for wrapped component on leave
20 | leave: assign({}, CommonOpacityProps.leave, {
21 | 'transformOrigin': 'left bottom',
22 | 'transform': 'rotate(0)'
23 | }),
24 | //CSS for wrapped component when leave animation completes
25 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
26 | 'transform': 'rotate(-90deg)',
27 | 'transformOrigin': 'left bottom'
28 | })
29 | };
30 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
31 | module.exports = createAnimation(ClockLeft);
32 |
--------------------------------------------------------------------------------
/Animations/Rotate/ClockUp.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 |
7 | var ClockUp = {
8 | className: 'ClockUp',
9 | //CSS for wrapped component on entry
10 | enter: assign({}, CommonOpacityProps.enter, {
11 | 'transform': 'rotate(-90deg)',
12 | 'transformOrigin': 'right bottom'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transformOrigin': 'right bottom',
17 | 'transform': 'rotate(0)'
18 | }),
19 | //CSS for wrapped component on leave
20 | leave: assign({}, CommonOpacityProps.leave, {
21 | 'transformOrigin': 'right bottom',
22 | 'transform': 'rotate(0)'
23 | }),
24 | //CSS for wrapped component when leave animation completes
25 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
26 | 'transform': 'rotate(-90deg)',
27 | 'transformOrigin': 'right bottom'
28 | })
29 | };
30 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
31 | module.exports = createAnimation(ClockUp);
32 |
--------------------------------------------------------------------------------
/Animations/Rotate/CounterClock.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 |
7 | var CounterClock = {
8 | className: 'counterClock',
9 | //CSS for wrapped component on entry
10 | enter: assign({}, CommonOpacityProps.enter, {
11 | 'transform': 'rotate(-200deg)',
12 | 'transformOrigin': 'center center'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transformOrigin': 'center center',
17 | 'transform': 'rotate(0)'
18 | }),
19 | //CSS for wrapped component on leave
20 | leave: assign({}, CommonOpacityProps.leave, {
21 | 'transformOrigin': 'center center',
22 | 'transform': 'rotate(0)'
23 | }),
24 | //CSS for wrapped component when leave animation completes
25 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
26 | 'transform': 'rotate(-200deg)',
27 | 'transformOrigin': 'center center'
28 | })
29 | };
30 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
31 | module.exports = createAnimation(CounterClock);
32 |
--------------------------------------------------------------------------------
/Animations/Rotate/CounterClockRight.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 |
7 | var CounterClockRight = {
8 | className: 'counterClockRight',
9 | //CSS for wrapped component on entry
10 | enter: assign({}, CommonOpacityProps.enter, {
11 | 'transform': 'rotate(90deg)',
12 | 'transformOrigin': 'right bottom'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transformOrigin': 'right bottom',
17 | 'transform': 'rotate(0)'
18 | }),
19 | //CSS for wrapped component on leave
20 | leave: assign({}, CommonOpacityProps.leave, {
21 | 'transformOrigin': 'right bottom',
22 | 'transform': 'rotate(0)'
23 | }),
24 | //CSS for wrapped component when leave animation completes
25 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
26 | 'transform': 'rotate(90deg)',
27 | 'transformOrigin': 'right bottom'
28 | })
29 | };
30 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
31 | module.exports = createAnimation(CounterClockRight);
32 |
--------------------------------------------------------------------------------
/Animations/Rotate/CounterClockUp.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 |
7 | var CounterClockUp = {
8 | className: 'counterClockUp',
9 | //CSS for wrapped component on entry
10 | enter: assign({}, CommonOpacityProps.enter, {
11 | 'transform': 'rotate(90deg)',
12 | 'transformOrigin': 'left bottom'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transformOrigin': 'left bottom',
17 | 'transform': 'rotate(0)'
18 | }),
19 | //CSS for wrapped component on leave
20 | leave: assign({}, CommonOpacityProps.leave, {
21 | 'transformOrigin': 'left bottom',
22 | 'transform': 'rotate(0)'
23 | }),
24 | //CSS for wrapped component when leave animation completes
25 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
26 | 'transform': 'rotate(90deg)',
27 | 'transformOrigin': 'left bottom'
28 | })
29 | };
30 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
31 | module.exports = createAnimation(CounterClockUp);
32 |
--------------------------------------------------------------------------------
/Animations/Rotate/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 |
7 | var Clock = {
8 | className: 'rotate',
9 | //CSS for wrapped component on entry
10 | enter: assign({}, CommonOpacityProps.enter, {
11 | 'transform': 'rotate(200deg)',
12 | 'transformOrigin': 'center center'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transformOrigin': 'center center',
17 | 'transform': 'rotate(0)'
18 | }),
19 | //CSS for wrapped component on leave
20 | leave: assign({}, CommonOpacityProps.leave, {
21 | 'transformOrigin': 'center center',
22 | 'transform': 'rotate(0)'
23 | }),
24 | //CSS for wrapped component when leave animation completes
25 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
26 | 'transform': 'rotate(200deg)',
27 | 'transformOrigin': 'center center'
28 | })
29 | };
30 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
31 | module.exports = createAnimation(Clock);
32 |
--------------------------------------------------------------------------------
/Animations/Zoom.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createAnimation = require('tuxx/Animations').createAnimation;
4 | var assign = require('object-assign');
5 | var CommonOpacityProps = require('tuxx/Animations/CommonOpacityProps');
6 | //Default Zoom animation component
7 | var Zoom = {
8 | //Class name given to the animation component once mounted
9 | className: 'zoom',
10 | //CSS for wrapped component on entry
11 | enter: assign({}, CommonOpacityProps.enter, {
12 | 'transform': 'scale(.1)'
13 | }),
14 | //CSS for wrapped component when entry animation completes
15 | 'enter-active': assign({}, CommonOpacityProps['enter-active'], {
16 | 'transform': 'scale(1)'
17 | }),
18 | //CSS for wrapped component on leave
19 | leave: assign({}, CommonOpacityProps.leave, {
20 | 'transform': 'scale(1)'
21 | }),
22 | //CSS for wrapped component when leave animation completes
23 | 'leave-active': assign({}, CommonOpacityProps['leave-active'], {
24 | 'transform': 'scale(.1)'
25 | })
26 | };
27 | //Use createAnimation function from main Tuxx Animation module to create wrapping animation component and pass in the default params
28 | module.exports = createAnimation(Zoom);
29 |
--------------------------------------------------------------------------------
/Animations/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | createAnimation: require('tuxx/src/TuxxAnimations')
3 | };
4 |
--------------------------------------------------------------------------------
/Architecture/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | architect: require('tuxx/src/TuxxArchitectStores')
3 | };
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## General Workflow
4 |
5 | 1. Fork the repo.
6 | 1. Cut a namespaced feature branch from master.
7 | - bug/...
8 | - feat/...
9 | - test/...
10 | - doc/...
11 | - refactor/...
12 | 1. Make commits to your feature branch. Try to keep the number of commits to a minimum. Follow the existing pattern for commit naming.
13 | 1. When you've finished with your fix or feature, Rebase upstream changes into your branch. Submit a [pull request](https://github.com/TuxedoJS/TuxedoJS.git) directly to master. Include a description of your changes.
14 | 1. Your pull request will be reviewed by another maintainer. The point of code reviews is to help keep the codebase clean and of high quality and, equally as important, to help you grow as a programmer. If your code reviewer requests you make a change you don't understand, ask them why.
15 | 1. Fix any issues raised by your code reviewer, and push your fixes as a single new commit.
16 | 1. Once the pull request has been reviewed, it will be merged by another member of the team. Do not merge your own commits. **EVER**
17 |
18 | ## Detailed Workflow
19 |
20 | ### Fork the repo
21 |
22 | Use github’s interface to make a fork of the repo, then add that repo as an upstream remote:
23 |
24 | ```
25 | git remote add upstream https://github.com/TuxedoJS/TuxedoJS.git
26 | ```
27 |
28 | ### Cut a namespaced feature branch from master
29 |
30 | Your branch should follow this naming convention:
31 | - bug/...
32 | - feat/...
33 | - test/...
34 | - doc/...
35 | - refactor/...
36 |
37 | These commands will help you do this:
38 |
39 | ``` bash
40 |
41 | # Creates your branch and brings you there
42 | git checkout -b `your-branch-name`
43 | ```
44 |
45 | ### Make commits to your feature branch.
46 |
47 | Prefix each commit like so
48 | - (feat) Add a new feature
49 | - (fix) Fix inconsistent tests [Fixes #0]
50 | - (refactor) ...
51 | - (cleanup) ...
52 | - (test) ...
53 | - (doc) ...
54 |
55 | Make changes and commits on your branch, and make sure that you only make changes that are relevant to this branch. If you find yourself making unrelated changes, make a new branch for those changes.
56 |
57 | #### Commit Message Guidelines
58 |
59 | - Commit messages should be written in the present tense; e.g. "Fix continuous integration script." and not "Fixes continuous integration script."
60 | - The first line of your commit message should be a brief summary of what the commit changes. Aim for about 70 characters max. Remember: This is a summary, not a detailed description of everything that changed.
61 | - If you want to explain the commit in more depth, following the first line should be a blank line and then a more detailed description of the commit. This can be as detailed as you want, so dig into details here and keep the first line short.
62 | - Always end your commit message with a period.
63 |
64 | ### Rebase upstream changes into your branch
65 |
66 | Once you are done making changes, you can begin the process of getting your code merged into the main repo. Step 1 is to rebase upstream changes to the master branch into yours by running this command from your branch:
67 |
68 | ```bash
69 | git pull --rebase upstream master
70 | ```
71 |
72 | This will start the rebase process. You must commit all of your changes before doing this. If there are no conflicts, this should just roll all of your changes back on top of the changes from upstream, leading to a nice, clean, linear commit history.
73 |
74 | If there are conflicting changes, git will start yelling at you part way through the rebasing process. Git will pause rebasing to allow you to sort out the conflicts. You do this the same way you solve merge conflicts, by checking all of the files git says have been changed in both histories and picking the versions you want. Be aware that these changes will show up in your pull request, so try and incorporate upstream changes as much as possible.
75 |
76 | You pick a file by `git add`ing it - you do not make commits during a rebase.
77 |
78 | Once you are done fixing conflicts for a specific commit, run:
79 |
80 | ```bash
81 | git rebase --continue
82 | ```
83 |
84 | This will continue the rebasing process. Once you are done fixing all conflicts you should run the existing tests to make sure you didn’t break anything, then run your new tests (there are new tests, right?) and make sure they work also.
85 |
86 | If rebasing broke anything, fix it, then repeat the above process until you get here again and nothing is broken and all the tests pass.
87 |
88 | ### Make a pull request
89 |
90 | Make a clear pull request from your fork and branch to the upstream master branch, detailing exactly what changes you made and what feature this should add. The clearer your pull request is the faster you can get your changes incorporated into this repo.
91 |
92 | At least one other person MUST give your changes a code review, and once they are satisfied they will merge your changes into upstream. Alternatively, they may have some requested changes. You should make more commits to your branch to fix these, then follow this process again from rebasing onwards.
93 |
94 | Once you get back here, make a comment requesting further review and someone will look at your code again. If they like it, it will get merged, else, just repeat again.
95 |
96 | Thanks for contributing!
97 |
98 | ### Guidelines
99 |
100 | 1. Uphold the current code standard:
101 | - Keep your code DRY.
102 | - Apply the boy scout rule.
103 | - Follow STYLE-GUIDE.md.
104 | 1. Run the tests before submitting a pull request.
105 | 1. Tests are very, very important. Submit tests if your pull request contains new, testable behavior.
106 | 1. Your pull request is comprised of a single (squashed) commit.
107 |
108 | ## Checklist:
109 |
110 | This is just to help you organize your process
111 |
112 | - [ ] Did I cut my work branch off of master (don't cut new branches from existing feature branches)?
113 | - [ ] Did I follow the correct naming convention for my branch?
114 | - [ ] Is my branch focused on a single main change?
115 | - [ ] Do all of my changes directly relate to this change?
116 | - [ ] Did I rebase the upstream master branch after I finished all my work?
117 | - [ ] Did I write a clear pull request message detailing what changes I made?
118 | - [ ] Did I get a code review?
119 | - [ ] Did I make any requested changes from that code review?
120 |
121 | If you follow all of these guidelines and make good changes, you should have no problem getting your changes merged into the project.
122 |
--------------------------------------------------------------------------------
/Components/FormattedInput.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-validating-form/src/formatted_input');
2 |
--------------------------------------------------------------------------------
/Components/NestedValue.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-validating-form/src/nested_value');
2 |
--------------------------------------------------------------------------------
/Components/PatternedInput.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-validating-form/src/patterned_input');
2 |
--------------------------------------------------------------------------------
/Components/ValidatingForm.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-validating-form/src/validating_form');
2 |
--------------------------------------------------------------------------------
/Components/ValidatingInput.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-validating-form/src/validating_input');
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2015 TuxedoJS.
2 |
3 | The following license applies to all parts of this software except as
4 | documented below:
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
24 | ====
25 |
26 | All files located in the node_modules and external directories are
27 | externally maintained libraries used by this software which have their
28 | own licenses; we recommend you read them, as their terms may differ from
29 | the terms above.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | [](https://semaphoreapp.com/cheerazar/tuxedojs)
3 |
4 | > A Front-End JavaScript Framework built on Facebook's powerful React view layer and Flux architecture.
5 |
6 | [React](https://github.com/facebook/react) and [Flux](https://github.com/facebook/flux) are two novel and exciting approaches to front-end development. Lots of people use `React` as the V in MVC, since it makes no assumptions about the rest of your technology stack and it uses a *virtual DOM* diff implementation for ultra-high performance.
7 |
8 | `Flux` is an application architecture for `React` that remedies the problems associated with predicting changes in applications that use two-way directional data flows, instead utilizing a *unidirectional data flow* which makes it easier to understand and modify an application as it becomes more complicated. In `Flux`, the `Dispatcher` is a singleton that directs the flow of data and ensures that updates do not cascade. As an application grows, the `Dispatcher` becomes more vital, as it can also manage dependencies between stores by invoking the registered callbacks in a specific order. See this blog for more on [Flux](http://facebook.github.io/react/blog/2014/05/06/flux.html).
9 |
10 | `TuxedoJS` capitalizes on the performance benefits of `React` and the simplified application architecture of `Flux`. It abstracts away unnecessary complexity and implements a more accessible and semantic interface for working with `Flux` and augmented `React` components in various aspects of the view logic.
11 |
12 | ## Table of Contents
13 |
27 |
28 |
29 | ## Features and Examples [#](#Features-and-Examples)
30 |
31 | `Tuxx` abstracts away the complexity of `Flux` with powerful `Actions` syntax:
32 |
33 | ```javascript
34 | var Actions = require('tuxx/Actions');
35 |
36 | var todoActions = Actions.createActionCategory({
37 | category: 'todos',
38 | source: 'todo views',
39 | actions: ['add', 'remove']
40 | });
41 |
42 | module.exports = todoActions;
43 | ```
44 |
45 | `Tuxx` provides all of the glue code needed to build stores and register them with the `TuxxActions` dispatcher:
46 |
47 | ```javascript
48 | var TodoActions = require('./TodoActions')
49 | var ActionStores = require('tuxx/Stores/ActionStores');
50 |
51 | var todoStore = ActionStores.createStore({
52 | _todos: [],
53 |
54 | getAll: function () {
55 | return this._todos;
56 | },
57 |
58 | onAdd: function (todo) {/*handle creation of Todo*/},
59 |
60 | onRemove: function (todo) {/*handle removal of Todo*/},
61 |
62 | //semantic method allows us to register our store with actions
63 | register: function () {
64 | return {
65 | todos: {
66 | add: this.onAdd,
67 | remove: this.onRemove
68 | }
69 | };
70 | }
71 | });
72 |
73 | module.exports = todoStore;
74 | ```
75 |
76 | `Tuxx` provides powerful opinionated `React` classes that make connecting with your stores, sharing methods with child components, and building high performance components a synch.
77 |
78 | A high performance component:
79 |
80 | ```javascript
81 | var React = require('tuxx/React');
82 |
83 | var Todo = React.createMutableClass({
84 | mutableTraits: {
85 | props: 'text'
86 | },
87 |
88 | //tuxx provides tools for automatically sharing static properties and methods between components via nearestOwnerProps
89 | handleRemove: function (e) {
90 | e.preventDefault();
91 | this.nearestOwnerProps.remove(this.props.todo);
92 | },
93 |
94 | render: function () {
95 | return (
96 |
{ this.props.todo.text }
97 |
98 | );
99 | }
100 | });
101 |
102 | module.exports = Todo;
103 | ```
104 |
105 | A standard `Tuxx` component:
106 |
107 | ```javascript
108 | var TodoCreateForm = React.createOwneeClass({
109 | //you can perform propType checking on nearestOwnerProps too
110 | nearestOwnerPropTypes: {
111 | add: React.PropTypes.func.isRequired
112 | },
113 |
114 | //again, using automatically shared static methods here via 'nearestOwnerProps'
115 | handleSubmit: function (e) {
116 | e.preventDefault();
117 | var todoTextInput = this.refs.textInput.getDOMNode();
118 | this.nearestOwnerProps.add({text: todoTextInput.value});
119 | },
120 |
121 | render: function(){
122 | return (
123 |
127 | );
128 | }
129 | });
130 |
131 | module.exports = TodoCreateForm;
132 | ```
133 |
134 | A `Tuxx` class designed to manage state and pass down properties/methods:
135 |
136 | ```javascript
137 | var todoStore = require('./todoStore');
138 | var todoActions = require('./todoActions');
139 | var Todo = require('./Todo');
140 | var TodoCreateForm = require('./TodoCreateForm');
141 |
142 | var TodoViewOwner = React.createOwnerClass({
143 | getInitialState: function () {/*get Todos from store*/},
144 |
145 | connectOwnerToStore: function () {
146 | return {
147 | store: todoStore,
148 | listener: function () {
149 | this.setState({ todos: todoStore.getAll() });
150 | }
151 | };
152 | },
153 |
154 | registerOwnerProps: function () {
155 | return {
156 | add: todoActions.add,
157 | remove: todoActions.remove
158 | };
159 | },
160 |
161 | render: function () {
162 | var Todos = this.state.todos.map(function(todo) {
163 | return ();
164 | });
165 |
166 | return (
167 |
168 |
169 | {Todos}
170 |
171 | );
172 | }
173 | });
174 | ```
175 |
176 | `Tuxx` provides an entire library of semantic plug-and-play animations.
177 |
178 | ```javascript
179 | var React = require('tuxx/React');
180 | var Fly = require('tuxx/Animations/Fly');
181 | var FadeUp = require('tuxx/Animations/Fade/Up');
182 |
183 | var Home = React.createClass({
184 | render: function () {
185 | return (
186 |
187 |
188 |
Hello World
189 |
190 |
191 |
Hello, Classier World
192 |
193 |
194 | );
195 | }
196 | });
197 | ```
198 |
199 | See our [TuxedoJS Doc Site](https://tuxedojs.org) for a full list of `Tuxx` features and functionality.
200 |
201 | ***
202 |
203 | ## Graceful Degradation [#](#Graceful-Degradation)
204 |
205 | > Tuxx allows you to be as classy as you want.
206 |
207 | An integral facet of the `Tuxx` architecture is that you can use as much or as little of it as you want. `Tuxx` does absolutely no modifying of the underlying `React` and `Flux` components it is built upon, but rather extends their core functionality and provides more intuitive interfaces for leveraging their power.
208 |
209 | Furthermore, `Tuxx` was designed to be as modular as possible, allowing you to only use the specific parts you need. It is for this very reason that we don't pollute the global namespace with one large `Tuxx` object that holds unncessary JavaScript.
210 |
211 | Thus, feel free to fall back to `React` or `Flux` conventions as much or as little as you desire. We hope you enjoy the flexibility.
212 |
213 | ***
214 |
215 | ## Requirements [#](#Requirements)
216 |
217 | - Node 0.10.x
218 |
219 | ***
220 |
221 | ## Development [#](#Development)
222 |
223 | ### Installing TuxedoJS [#](#Installing-TuxedoJS)
224 |
225 | Install `TuxedoJS` through npm:
226 |
227 | npm install tuxx
228 |
229 | ***
230 |
231 | ### Tasks and Dependencies [#](#Tasks-and-Dependencies)
232 |
233 | Tuxx is built with CommonJS and thus you will need a compiler such as `Browserify` or `webpack`. In our case, we use `Browserify` for compiling, `Reactify` for compiling JSX, `Envify` for accessing NODE_ENV variables in the browser (great for automatically turning dev tools on and off), and `Watchify` for automatic compiling.
234 |
235 | npm install --save browserify
236 | npm install --save envify
237 | npm install --save reactify
238 | npm install --save watchify
239 |
240 | To use these modules in development, we recommend adding the following lines to the `scripts` key of your `package.json` file to set up automatic JSX compiling and configure testing:
241 |
242 | "scripts": {
243 | "start": "watchify -d [YOUR MAIN.JS OR SIMILAR FILE] -o [YOUR OUTPUT BUNDLE.JS] -v",
244 | "build": "NODE_ENV=production browserify [YOUR MAIN.JS OR SIMILAR FILE] | uglifyjs -cm > [YOUR OUTPUT BUNDLE.JS]"
245 | }
246 |
247 | Include this `browserify` transform in your `package.json` file as well:
248 |
249 | "browserify": {
250 | "transform": [
251 | [
252 | "reactify",
253 | {
254 | "es6": true
255 | }
256 | ],
257 | "envify"
258 | ]
259 | }
260 |
261 | The `browserify` transform will cause `watchify` to use `reactify` to compile your JSX and `es6` syntax like the `Spread` syntax, and allow you to use `envify` for node environment variables in the browser.
262 |
263 | After adding this code, run
264 |
265 | npm start
266 |
267 | during development to automatically compile all JSX and JavaScript syntax into one `bundle.js` which you can then directly link to your `index.html` file. Use
268 |
269 | npm run build
270 |
271 | to compile a production ready bundle of your code.
272 |
273 | ***
274 |
275 | ### Roadmap [#](#Roadmap)
276 |
277 | View the project roadmap [here](https://github.com/TuxedoJS/TuxedoJS/issues).
278 |
279 | ***
280 |
281 | ## Credits and Tech Stack [#](#Credits-and-Tech-Stack)
282 |
283 | - [React](https://github.com/facebook/react)
284 | - [React-Tools](https://www.npmjs.com/package/react-tools)
285 | - [React-Router](https://github.com/rackt/react-router)
286 | - [Flux](https://github.com/facebook/flux)
287 | - [Jest](https://github.com/facebook/jest)
288 | - [Jest-cli](https://www.npmjs.com/package/jest-cli)
289 | - [CommonJS](https://github.com/commonjs/commonjs)
290 | - [Browserify](http://browserify.org)
291 | - [Reactify](https://github.com/andreypopp/reactify)
292 | - [Watchify](https://github.com/substack/watchify)
293 | - [Envify](https://github.com/hughsk/envify)
294 | - [object-assign](https://github.com/sindresorhus/object-assign)
295 |
296 | ***
297 |
298 | ## Interested in Contributing? [#](#Interested-in-Contributing)
299 |
300 | Please review [CONTRIBUTING.md](CONTRIBUTING.md).
301 |
302 | ***
303 |
304 | ## Team [#](#Team)
305 |
306 | - __Team Lead__: [Dmitri Rabinowitz](https://github.com/drabinowitz)
307 | - __Scrum Master__: [Gunnari Auvinen](https://github.com/Cheerazar)
308 | - __Product Owner__: [Spencer Stebbins](https://github.com/sjstebbins)
309 | - __Software Engineer__: [Pat Lauer](https://github.com/plauer)
310 |
--------------------------------------------------------------------------------
/React/CSSTransitionGroup.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react/lib/ReactCSSTransitionGroup');
2 |
--------------------------------------------------------------------------------
/React/LinkedStateMixin.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react/lib/LinkedStateMixin');
2 |
--------------------------------------------------------------------------------
/React/Perf.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react/lib/ReactDefaultPerf');
2 |
--------------------------------------------------------------------------------
/React/TestUtils.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react/lib/ReactTestUtils');
2 |
--------------------------------------------------------------------------------
/React/TransitionGroup.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react/lib/ReactTransitionGroup');
2 |
--------------------------------------------------------------------------------
/React/batchedUpdates.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react/lib/ReactUpdates').batchedUpdates;
2 |
--------------------------------------------------------------------------------
/React/classSet.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react/lib/cx');
2 |
--------------------------------------------------------------------------------
/React/cloneWithProps.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react/lib/cloneWithProps');
2 |
--------------------------------------------------------------------------------
/React/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react');
2 | module.exports.createOwnerClass = require('tuxx/src/TuxxOwnerClass');
3 | module.exports.createOwneeClass = require('tuxx/src/TuxxOwneeClass');
4 | module.exports.createMutableClass = require('tuxx/src/TuxxMutableClass');
5 |
--------------------------------------------------------------------------------
/React/update.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react/lib/update');
2 |
--------------------------------------------------------------------------------
/Router/DefaultRoute.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/components/DefaultRoute');
2 |
--------------------------------------------------------------------------------
/Router/HashLocation.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/locations/HashLocation');
2 |
--------------------------------------------------------------------------------
/Router/History.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/utils/History');
2 |
--------------------------------------------------------------------------------
/Router/HistoryLocation.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/locations/HistoryLocation');
2 |
--------------------------------------------------------------------------------
/Router/ImitateBrowserBehavior.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/behaviors/ImitateBrowserBehavior');
2 |
--------------------------------------------------------------------------------
/Router/Link.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/components/Link');
2 |
--------------------------------------------------------------------------------
/Router/Navigation.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/mixins/Navigation');
2 |
--------------------------------------------------------------------------------
/Router/NotFoundRoute.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/components/NotFoundRoute');
2 |
--------------------------------------------------------------------------------
/Router/Redirect.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/components/Redirect');
2 |
--------------------------------------------------------------------------------
/Router/RefreshLocation.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/locations/RefreshLocation');
2 |
--------------------------------------------------------------------------------
/Router/Route.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/components/Route');
2 |
--------------------------------------------------------------------------------
/Router/RouteHandler.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/components/RouteHandler');
2 |
--------------------------------------------------------------------------------
/Router/ScrollToBehavior.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/behaviors/ScrollToTopBehavior');
2 |
--------------------------------------------------------------------------------
/Router/State.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/mixins/State');
2 |
--------------------------------------------------------------------------------
/Router/create.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/utils/createRouter');
2 |
--------------------------------------------------------------------------------
/Router/run.js:
--------------------------------------------------------------------------------
1 | module.exports = require('react-router/modules/utils/runRouter');
2 |
--------------------------------------------------------------------------------
/STYLE-GUIDE.md:
--------------------------------------------------------------------------------
1 | ### Indentation
2 |
3 | When writing any block of code that is logically subordinate to the line immediately before and after it, that block should be indented two spaces more than the surrounding lines
4 |
5 | * Do not put any tab characters anywhere in your code. You would do best to stop pressing the tab key entirely.
6 | * Increase the indent level for all blocks by two extra spaces
7 | * When a line opens a block, the next line starts 2 spaces further in than the line that opened
8 |
9 | ```javascript
10 | // good:
11 | if (condition) {
12 | action();
13 | }
14 |
15 | // bad:
16 | if (condition) {
17 | action();
18 | }
19 | ```
20 |
21 | * When a line closes a block, that line starts at the same level as the line that opened the block
22 | ```javascript
23 | // good:
24 | if (condition) {
25 | action();
26 | }
27 |
28 | // bad:
29 | if (condition) {
30 | action();
31 | }
32 | ```
33 |
34 | * No two lines should ever have more or less than 2 spaces difference in their indentation. Any number of mistakes in the above rules could lead to this, but one example would be:
35 |
36 | ```javascript
37 | // bad:
38 | transmogrify({
39 | a: {
40 | b: function(){
41 | }
42 | }});
43 | ```
44 |
45 | * Use Sublime's arrow collapsing as a guide. Do the collapsing lines seem like they should be 'contained' by the line with an arrow on it?
46 |
47 | ### Language constructs
48 |
49 | * Do not use `for...in` statements with the intent of iterating over a list of numeric keys. Use a for-with-semicolons statement in stead.
50 |
51 | ```javascript
52 | // good:
53 | // declare iterator variables and length before entering the loop
54 | var i, listLength;
55 | var list = ['a', 'b', 'c'];
56 | listLength = list.length;
57 |
58 | for (i = 0; i < listLength; i++) {
59 | alert(list[i]);
60 | }
61 |
62 | // bad:
63 | var list = ['a', 'b', 'c'];
64 | for (var i in list) {
65 | alert(list[i]);
66 | }
67 | ```
68 |
69 | * Never omit braces for statement blocks (although they are technically optional).
70 | ```javascript
71 | // good:
72 | for (key in object) {
73 | alert(key);
74 | }
75 |
76 | // bad:
77 | for (key in object)
78 | alert(key);
79 | ```
80 |
81 | * Always use `===` and `!==`, since `==` and `!=` will automatically convert types in ways you're unlikely to expect.
82 |
83 | ```javascript
84 | // good:
85 |
86 | // this comparison evaluates to false, because the number zero is not the same as the empty string.
87 | if (0 === '') {
88 | alert('looks like they\'re equal');
89 | }
90 |
91 | // bad:
92 |
93 | // This comparison evaluates to true, because after type coercion, zero and the empty string are equal.
94 | if (0 == '') {
95 | alert('looks like they\'re equal');
96 | }
97 | ```
98 |
99 | ### Semicolons
100 |
101 | * Don't forget semicolons at the end of lines.
102 |
103 | ```javascript
104 | // good:
105 | alert('hi');
106 |
107 | // bad:
108 | alert('hi')
109 | ```
110 |
111 | * Semicolons are not required at the end of statements that include a block--i.e. `if`, `for`, `while`, etc.
112 |
113 | ```javascript
114 | // good:
115 | if (condition) {
116 | response();
117 | }
118 |
119 | // bad:
120 | if (condition) {
121 | response();
122 | };
123 | ```
124 |
125 | * Misleadingly, a function may be used at the end of a normal assignment statement, and would require a semicolon (even though it looks rather like the end of some statement block).
126 |
127 | ```javascript
128 | // good:
129 | var greet = function () {
130 | alert('hi');
131 | };
132 |
133 | // bad:
134 | var greet = function () {
135 | alert('hi');
136 | }
137 | ```
138 |
139 | # Supplemental reading
140 |
141 | ### Comments
142 |
143 | * Provide comments any time you are confident it will make reading your code easier.
144 | * Be aware that comments come at some cost. They make a file longer and can drift out of sync with the code they annotate.
145 | * Comment on what code is attempting to do, not how it will achieve it.
146 | * A good comment is often less effective than a good variable name.
147 |
148 |
149 | ### Padding & additional whitespace
150 | TuxedoJS has a consistent convention for white space. There is a space after an `if`, `while`, `for`, `function`, etc, and then there is a space after the close parenthesis of the conditional, input arguments, etc. Maintaining this convention creates consistent code that is easy to review the content of without getting hung up on the formatting.
151 |
152 | Always end your file with a newline.
153 |
154 | ```javascript
155 | // good:
156 | if (x === 0) {
157 | console.log('This is done correctly');
158 | }
159 |
160 | var timesTwo = function (x) {
161 | return x * 2;
162 | };
163 |
164 | for (var i = 0; i < 10; i++) {
165 | console.log('i is: ' + i);
166 | }
167 |
168 | // bad:
169 | if(x !== 1){
170 | console.log('This is not following our convention');
171 | }
172 |
173 | var timesThree = function(x){
174 | return x * 3;
175 | };
176 | ```
177 |
178 | * You are not allowed to align two similar lines in TuxedoJS. This pattern usually leads to unnecessary edits of many lines in your code every time you change a variable name.
179 |
180 | ```javascript
181 | // not allowed:
182 | var firstItem = getFirst ();
183 | var secondItem = getSecond();
184 | ```
185 |
186 | * Put `else` and `else if` statements on the same line as the ending curly brace for the preceding `if` block
187 | ```javascript
188 | // good:
189 | if (condition) {
190 | response();
191 | } else {
192 | otherResponse();
193 | }
194 |
195 | // bad:
196 | if (condition) {
197 | response();
198 | }
199 | else {
200 | otherResponse();
201 | }
202 | ```
203 |
204 | ### Opening or closing too many blocks at once
205 |
206 | * The more blocks you open on a single line, the more your reader needs to remember about the context of what they are reading. Try to resolve your blocks early, and refactor. A good rule is to avoid closing more than two blocks on a single line--three in a pinch.
207 |
208 | ```javascript
209 | // avoid:
210 | _.ajax(url, {success: function(){
211 | // ...
212 | }});
213 |
214 | // prefer:
215 | _.ajax(url, {
216 | success: function () {
217 | // ...
218 | }
219 | });
220 | ```
221 |
222 | ### Variable declaration
223 |
224 | * Use a new var statement for each line you declare a variable on.
225 | * Do not break variable declarations onto multiple lines.
226 | * Use a new line for each variable declaration.
227 | * See http://benalman.com/news/2012/05/multiple-var-statements-javascript/ for more details
228 |
229 | ```javascript
230 | // good:
231 | var ape;
232 | var bat;
233 |
234 | // bad:
235 | var cat,
236 | dog
237 |
238 | // use sparingly. Tux tends to use it specifically when declaring variables that are reused:
239 | var eel, fly;
240 | ```
241 |
242 | ### Capital letters in variable names
243 |
244 | * TuxedoJS follows standard camelCase variable naming. If you are creating a constructor function that will be invoked using new or a similar object creation pattern, the first letter of the variable name should be capitalized as well.
245 |
246 | ### Minutia
247 |
248 | * For lists, put commas at the end of each newline, not at the beginning of each item in a list
249 |
250 | ```javascript
251 | // good:
252 | var animals = [
253 | 'ape',
254 | 'bat',
255 | 'cat'
256 | ];
257 |
258 | // bad:
259 | var animals = [
260 | 'ape'
261 | , 'bat'
262 | , 'cat'
263 | ];
264 | ```
265 |
266 | * Avoid use of `switch` statements altogether. They are hard to outdent using the standard whitespace rules above, and are prone to error due to missing `break` statements. See [this article](http://ericleads.com/2012/12/switch-case-considered-harmful/) for more detail.
267 |
268 | * Prefer single quotes around JavaScript strings, rather than double quotes. Having a standard of any sort is preferable to a mix-and-match approach, and single quotes allow for easy embedding of HTML, which prefers double quotes around tag attributes.
269 |
270 | ```javascript
271 | // good:
272 | var dog = 'dog';
273 | var cat = 'cat';
274 |
275 | // acceptable:
276 | var dog = "dog";
277 | var cat = "cat";
278 |
279 | // bad:
280 | var dog = 'dog';
281 | var cat = "cat";
282 | ```
283 |
--------------------------------------------------------------------------------
/Stores/ActionStores.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | createStore: require('tuxx/src/TuxxActionStore')
3 | };
4 |
--------------------------------------------------------------------------------
/Stores/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | createStore: require('tuxx/src/TuxxStore')
3 | };
4 |
--------------------------------------------------------------------------------
/Tuxedo.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //NOTE: please avoid using this object if possible as it requires in all tuxx modules, even ones you might not use. Please review the documentation on this subject and only require in your desired tux modules
4 | var Tuxx = {};
5 |
6 | //React object provides react module as well as opinionated tuxx react classes: createOwnerClass, createOwneeClass
7 | //recommend accessing this object via require('tuxx/React')
8 | Tuxx.React = require('./React');
9 |
10 | //Stores object provides createStore method for creating stores
11 | //recommend accessing this object via require('tuxx/Stores')
12 | Tuxx.Stores = require('./Stores');
13 |
14 | //Architecture object provides architect method for defining store relationships
15 | //recommend accessing this object via require('tuxx/Architecture')
16 | Tuxx.Architecture = require('./Architecture');
17 |
18 | //Actions object provides means for creating and listening to actions
19 | //recommend accessing this object via require('tuxx/Actions')
20 | Tuxx.Actions = require('./Actions');
21 |
22 | //Route object provides routing components
23 | //recommend accessing a specific routing component via require('tuxx/Router/DESIRED ROUTING COMPONENT')
24 | Tuxx.Router = require('react-router');
25 |
26 | //Components object provides prebuilt react components
27 | //recommend accessing a specific prebuilt component via require('tuxx/Components/DESIRED PREBUILT COMPONENT')
28 | Tuxx.Components = {};
29 |
30 | //Form
31 | Tuxx.Components.Form = require('react-validating-form');
32 |
33 | //Animations object provides react animation components and createAnimation method for creating custom animations
34 | //recommend accessing a specific animation component via require('tuxx/Animations/DESIRED ANIMATION COMPONENT')
35 | //recommend accessing a specific animation sub-component via require('tuxx/Animations/ANIMATION COMPONENT/ANIMATION SUB-COMPONENT')
36 | //recommend accessing the createAnimation method via require('tuxx/Animations')
37 | Tuxx.Animations = require('./Animations');
38 |
39 | //Fade
40 | Tuxx.Animations.Fade = require('./Animations/Fade');
41 | Tuxx.Animations.Fade.Down = require('./Animations/Fade/Down');
42 | Tuxx.Animations.Fade.DownBig = require('./Animations/Fade/DownBig');
43 | Tuxx.Animations.Fade.Left = require('./Animations/Fade/Left');
44 | Tuxx.Animations.Fade.LeftBig = require('./Animations/Fade/LeftBig');
45 | Tuxx.Animations.Fade.Right = require('./Animations/Fade/Right');
46 | Tuxx.Animations.Fade.RightBig = require('./Animations/Fade/RightBig');
47 | Tuxx.Animations.Fade.Up = require('./Animations/Fade/Up');
48 | Tuxx.Animations.Fade.UpBig = require('./Animations/Fade/UpBig');
49 |
50 | //Zoom
51 | Tuxx.Animations.Zoom = require('./Animations/Zoom');
52 |
53 | //Fly
54 | Tuxx.Animations.Fly = require('./Animations/Fly');
55 |
56 | //Rotate
57 | Tuxx.Animations.Rotate = require('./Animations/Rotate');
58 | Tuxx.Animations.Rotate.ClockLeft = require('./Animations/Rotate/ClockLeft');
59 | Tuxx.Animations.Rotate.ClockUp = require('./Animations/Rotate/ClockUp');
60 | Tuxx.Animations.Rotate.CounterClock = require('./Animations/Rotate/CounterClock');
61 | Tuxx.Animations.Rotate.CounterClockRight = require('./Animations/Rotate/CounterClockRight');
62 | Tuxx.Animations.Rotate.CounterClockUp = require('./Animations/Rotate/CounterClockUp');
63 |
64 | //warn user not to use this object in console
65 | console.warn('Using the Tuxx object directly is not recommended as it will mean loading all modules even those you don\'t intend to use. Please review the TuxedoJS documents for requiring only the individual pieces of TuxedoJS that you will need.');
66 |
67 | module.exports = Tuxx;
68 |
--------------------------------------------------------------------------------
/Tuxx-full-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drabinowitz/TuxedoJS/91afa0cedbb61d39cff9f7718bdfb5a60fcc3e05/Tuxx-full-logo.png
--------------------------------------------------------------------------------
/Tuxx-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drabinowitz/TuxedoJS/91afa0cedbb61d39cff9f7718bdfb5a60fcc3e05/Tuxx-icon.png
--------------------------------------------------------------------------------
/__tests__/TuxxModules-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //since these tests are for module structure and are based on requiring modules, those modules should not be mocked out in order to make sure that the comparisons are valid
4 | jest.autoMockOff();
5 | describe('Individual Tuxx Modules', function () {
6 | describe('Actions', function () {
7 | it('should expose the module in tuxx/src/TuxxActions.js', function () {
8 | expect(require('tuxx/Actions')).toBe(require('tuxx/src/TuxxActions'));
9 | });
10 | });
11 |
12 | describe('Architecture', function () {
13 | it('should expose the module in tuxx/src/TuxxArchitectStores.js under the method architect', function () {
14 | expect(require('tuxx/Architecture').architect).toBe(require('tuxx/src/TuxxArchitectStores'));
15 | });
16 | });
17 |
18 | describe('Components', function () {
19 | it('should expose only the specific component that the user requires in', function () {
20 | expect(require('tuxx/Components/FormattedInput')).toBe(require('react-validating-form').FormattedInput);
21 | });
22 | });
23 |
24 | describe('React', function () {
25 | it('should expose the react module', function () {
26 | expect(require('tuxx/React')).toBe(require('react'));
27 | });
28 |
29 | it('should expose the module in tuxx/src/TuxxOwnerClass under the method createOwnerClass', function () {
30 | expect(require('tuxx/React').createOwnerClass).toBe(require('tuxx/src/TuxxOwnerClass'));
31 | });
32 |
33 | it('should expose the module in tuxx/src/TuxxOwneeClass under the method createOwneeClass', function () {
34 | expect(require('tuxx/React').createOwneeClass).toBe(require('tuxx/src/TuxxOwneeClass'));
35 | });
36 |
37 | it('should expose the module in tuxx/src/TuxxMutableClass under the method createMutableClass', function () {
38 | expect(require('tuxx/React').createMutableClass).toBe(require('tuxx/src/TuxxMutableClass'));
39 | });
40 |
41 | it('should expose the specific React addon that the user requires in', function () {
42 | expect(require('tuxx/React/LinkedStateMixin')).toBe(require('react/lib/LinkedStateMixin'));
43 | expect(require('tuxx/React/Perf')).toBe(require('react/lib/ReactDefaultPerf'));
44 | });
45 | });
46 |
47 | describe('Router', function () {
48 | it('should expose only the specific router component that the user requires in', function () {
49 | expect(require('tuxx/Router/Route')).toBe(require('react-router').Route);
50 | });
51 | });
52 |
53 | describe('Stores', function () {
54 | it('should expose the module in src/TuxxStore under the method createStore', function () {
55 | expect(require('tuxx/Stores').createStore).toBe(require('tuxx/src/TuxxStore'));
56 | });
57 |
58 | describe('ActionStores', function () {
59 | it('should expose the module in src/TuxxActionStore under the method createStore', function () {
60 | expect(require('tuxx/Stores/ActionStores').createStore).toBe(require('tuxx/src/TuxxActionStore'));
61 | });
62 | });
63 | });
64 |
65 | describe('Animations', function () {
66 | it('should expose the TuxxAnimations module under the createAnimation method', function () {
67 | expect(require('tuxx/Animations').createAnimation).toBe(require('tuxx/src/TuxxAnimations'));
68 | });
69 |
70 | it('should expose only the specific animation component that the user requires in', function () {
71 | expect(require('tuxx/Animations/Fly')).toBeDefined();
72 | });
73 |
74 | it('should expose only the specific sub animation component that the user requires in', function () {
75 | expect(require('tuxx/Animations/Fade/DownBig')).toBeDefined();
76 | });
77 | });
78 |
79 | describe('Tuxedo', function () {
80 | it('should log a warning if used directly', function () {
81 | console.warn = jest.genMockFunction();
82 | require('tuxx');
83 | expect(console.warn.mock.calls[0][0]).toBe('Using the Tuxx object directly is not recommended as it will mean loading all modules even those you don\'t intend to use. Please review the TuxedoJS documents for requiring only the individual pieces of TuxedoJS that you will need.');
84 | });
85 | });
86 | });
87 |
--------------------------------------------------------------------------------
/docs/ApiMenu.json:
--------------------------------------------------------------------------------
1 | {
2 | "data": [
3 | {
4 | "section": "TuxxActions",
5 | "docs": []
6 | },
7 | {
8 | "section": "TuxxAnimations",
9 | "docs": []
10 | },
11 | {
12 | "section": "TuxxArchitecture",
13 | "docs": []
14 | },
15 | {
16 | "section": "TuxxModularity",
17 | "docs": []
18 | },
19 | {
20 | "section": "TuxxReact",
21 | "docs": ["TuxxOwneeClass", "TuxxOwnerClass", "TuxxMutableClass"]
22 | },
23 | {
24 | "section": "TuxxRouter",
25 | "docs": []
26 | },
27 | {
28 | "section": "TuxxStores",
29 | "docs": ["TuxxActionStores"]
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/docs/Tuxedo.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drabinowitz/TuxedoJS/91afa0cedbb61d39cff9f7718bdfb5a60fcc3e05/docs/Tuxedo.md
--------------------------------------------------------------------------------
/docs/TuxxActionStores.md:
--------------------------------------------------------------------------------
1 | # Tuxx ActionStores
2 | ## Table of Contents
3 |
4 |
22 |
23 |
24 |
25 | ## Context [#](#Context)
26 | Recall that [Tuxx Actions](https://tuxedojs.org/docs/TuxxActions) allow you to create, listen, and dispatch actions as per the `Tuxx` architecture.
27 |
28 | ```javascript
29 | var Actions = require('tuxx/Actions');
30 |
31 | var messageActions = Actions.createActionCategory({
32 | category: 'messages',
33 | source: 'message views',
34 | actions: ['get', 'create', 'delete']
35 | });
36 | ```
37 |
38 | Well, with `ActionStores`, we provide an alternative approach to registering your stores with your actions.
39 |
40 | ***
41 |
42 | ## Premise [#](#Premise)
43 | >`TuxxActionStores` is a module providing the interface for creating stores that are pre-packaged with a `register` convenience method for automatically registering the store with actions.
44 |
45 | ***
46 |
47 | ## Implementation [#](#Implementation)
48 | ### 1) Requiring ActionStores [#](#Requiring-ActionStores)
49 | The interface for creating `ActionStores` is exposed via a `createStore` constructor function on the `ActionStores` object.
50 |
51 | ```javascript
52 | var ActionStores = require('tuxx/Stores/ActionStores');
53 | ```
54 |
55 | ***
56 |
57 | ### 2) Creating ActionStores [#](#Creating-ActionStores)
58 | `ActionStores` are an implementation on top of the basic `TuxxStore`. Thus, they have all of the same properties. The only difference is that `ActionStores` expect to have a `register` method passed in. If this `register` key is defined, then the `ActionStore` will invoke the method with the store as the context and use the object the method returns to register the store with `TuxxActions`. Let's take a look at an example
59 |
60 | ```javascript
61 | var actionStore = ActionStores.createStore({
62 | _messages: [],
63 |
64 | onCreate: function (message) {
65 | this._messages.push(message);
66 | this.emitChange();
67 | },
68 |
69 | register: function () {
70 | return {
71 | messages: {
72 | create: this.onCreate
73 | }
74 | };
75 | }
76 | });
77 | ```
78 |
79 | Below is the API documentation for this method.
80 |
81 | ***
82 |
83 |
84 | ### 3) ActionStores.createStore [#](#ActionStores-createStore)
85 | Returns an `actionStore` instance based on the passed in `storeProps`.
86 |
87 | ```javascript
88 | var actionStore = ActionStores.createStore(storeProps);
89 | ```
90 |
91 | ***
92 |
93 | #### 3.1) Parameter - `storeProps` - type: OBJECT - required [#](#storeProps)
94 | `StoreProps` is an object containing actions that will encapsulate the store's data, with an optional `register` method which, if provided, will automatically register all the actions with the newly created store. Let's see what this looks like:
95 |
96 | ```javascript
97 | var messageStoreProperties = {
98 | _messages: [],
99 |
100 | onCreate: function (message) {
101 | this._messages.push(message);
102 | this.emitChange();
103 | },
104 |
105 | register: function () {
106 | return {
107 | messages: {
108 | create: this.onCreate
109 | }
110 | };
111 | }
112 | };
113 | ```
114 |
115 | ***
116 |
117 | ##### 3.1.1) Method - `storeProps.register` - type: FUNCTION - optional [#](#storeProps-register)
118 | The `register` method is an optional key on the `storeProps` parameter which, if defined, will invoke `Actions.register`, passing in the store as the first argument and the object returned by this `storeProps.register` method as the second argument. Here is what the `register` method performs on the backend:
119 |
120 | ```javascript
121 | Actions.register(actionStore, actionStore.register.call(actionStore));
122 | ```
123 |
124 | Now let's look at an example of `storeProps.register`:
125 |
126 | ```javascript
127 | storeProps.register = function () {
128 | return {
129 | messages: {
130 | create: this.onCreate;
131 | }
132 | };
133 | };
134 | ```
135 | Note that `this` in the above example refers to the store being created.
136 |
137 | Furthermore, this syntax abstracts away even more complexity by allowing you to register listeners with one or multiple actions and one or multiple categories simultaneously.
138 |
139 | ```javascript
140 | {
141 | CATEGORY1: {
142 | CATEGORY1_ACTION1: this.ONTHISACTION,
143 | CATEGORY1_ACTION2: this.ONANOTHERACTION,
144 | },
145 | CATEGORY2: {
146 | ACTION1: this.ONFINALACTION
147 | }
148 | }
149 | ```
150 |
151 | #### Return - `actionStore` - type: OBJECT [#](#createStore-actionStore)
152 | Returns a `TuxxStore` instance.
153 |
154 | ***
155 |
156 | ## TuxxActionStores Complete Example [#](#TuxxActionStores-Complete-Example)
157 | Taking everything we have learned, let's look at a full example.
158 |
159 | ```javascript
160 | var ActionStores = require('tuxx/Stores/ActionStores');
161 | var Actions = require('tuxx/Actions');
162 |
163 | var messageActions = Actions.createActionCategory({
164 | category: 'messages',
165 | source: 'message views',
166 | actions: ['create']
167 | });
168 |
169 | var messageStoreProperties = {
170 | _messages: [],
171 |
172 | onCreate: function (message) {
173 | this._messages.push(message);
174 | this.emitChange();
175 | },
176 |
177 | register: function () {
178 | return {
179 | messages: {
180 | create: this.onCreate;
181 | }
182 | };
183 | }
184 | };
185 |
186 | var messageActionStore = ActionStores.createActionStore(messageStoreProperties);
187 | ```
188 |
189 | Hopefully this shows the power of the `ActionStore`. It allows us to easily and semantically describe the action categories and verbs that our store is listening for and the callbacks it will invoke in response to those actions.
190 |
--------------------------------------------------------------------------------
/docs/TuxxArchitecture.md:
--------------------------------------------------------------------------------
1 | # Tuxx/Architecture
2 |
3 | ## Table of Contents
4 |
5 |
23 |
24 |
25 | ## Premise [#](#Premise)
26 | >TuxedoJS provides a novel approach for describing the dependencies between stores. In Flux, store dependencies are defined via the `Dispatcher.waitFor` syntax. This allows stores to define the order at which they will receive events from the dispatcher. While this syntax is workable, we consider it to be flawed due to the fact that it requires each store to separately manage the stores it needs to waitFor. This can result in distributed app architectures that are difficult to reason about. For an engineer to understand all the store relationships in an app, he/she would need to step through every store file and draw out a map of the stores that it is dependent on. TuxedoJS tackles this problem by centralizing waitFor statements and providing a more semantic interface for building waitFor relationships.
27 |
28 | ***
29 |
30 | ## Implementation [#](#Implementation)
31 | TuxedoJS provides two major innovations for defining store dependencies:
32 |
33 | 1. It allows you to write all of your store dependencies in a single location. This means that **one** file is responsible for managing your app's architecture.
34 | 2. It allows you to describe your store dependencies in terms of the inputs to and outputs from each of your stores.
35 |
36 | ### 1) Requiring Architecture [#](#Requiring-Architecture)
37 | The Architecture interface is exposed via:
38 |
39 | ```javascript
40 | var Architecture = require('tuxx/Architecture');
41 | ```
42 |
43 | ***
44 |
45 | ### 2) Architecting Store Dependencies [#](#Architecting-Store-Dependencies)
46 | Let's take a look at an example. Imagine we want to describe the following network of store dependencies, where each store is dependent on the store it points to: [#](#Architecting-Store-Dependencies-Example)
47 |
48 |
49 | ```javascript
50 | userStore <---privateMessageStore
51 | ^ ^
52 | | |
53 | roomStore <---------|
54 | ^ |
55 | | |
56 | threadStore <---unreadStore
57 | ^ |
58 | | |
59 | messageStore <------|
60 | ```
61 |
62 | In Flux we would need to write these dependencies spread out across six different files, while on the other hand, in TuxedoJS, we can accomplish all of this in just one file:
63 |
64 | ```javascript
65 | var userStore = require('../stores/userStore');
66 | var roomStore = require('../stores/roomStore');
67 | var threadStore = require('../stores/threadStore');
68 | var messageStore = require('../stores/messageStore');
69 | var privateMessageStore = require('../stores/privateMessageStore');
70 | var unreadStore = require('../stores/unreadStore');
71 |
72 | var architect = require('tuxx/Architecture').architect;
73 |
74 | architect(userStore).itOutputs('users');
75 | architect(roomStore).itNeeds('users').itOutputs('rooms', 'unreadCount');
76 | architect(threadStore).itNeeds('rooms').itOutputs('threads', 'unreadCount');
77 | architect(messageStore).itNeeds('threads').itOutputs('messages', 'unreadCount');
78 | architect(privateMessageStore).itNeeds('users').itOutputs('privateMessages', 'unreadCount');
79 | architect(unreadStore).itNeeds('unreadCount');
80 | ```
81 |
82 | TuxxArchitecture also supports arrays of inputs for `itNeeds` and `itOutputs` as well as the ability to chain invocations of these functions via the `and` method (it copies the last method invoked). For example, we could rewrite the `architect(roomStore)` call from the previous example as:
83 |
84 | ```javascript
85 | architect(roomStore).itNeeds('users').itOutputs(['rooms', 'unreadCount']); //OR
86 | architect(roomStore).itNeeds('users').itOutputs('rooms').and('unreadCount');
87 | ```
88 |
89 | If we decided that the `unreadStore` was also dependent on the `userStore` we could write:
90 |
91 | ```javascript
92 | architect(unreadStore).itNeeds('unreadCount', 'users'); //OR
93 | architect(unreadStore).itNeeds(['unreadCount', 'users']); //OR
94 | architect(unreadStore).itNeeds('unreadCount').and('users');
95 | ```
96 |
97 | Lastly, you have the option to directly need stores if you do not want to use this string based syntax:
98 |
99 | ```javascript
100 | architect(roomStore).itNeeds(userStore);
101 | architect(threadStore).itNeeds(userStore, roomStore);
102 | architect(messageStore).itNeeds(userStore, threadStore);
103 | architect(privateMessageStore).itNeeds(userStore);
104 | architect(unreadStore).itNeeds(roomStore, threadStore, messageStore, privateMessageStore);
105 | ```
106 |
107 | While this approach is shorter it can be advantageous to use the string approach, as it allows you to write your dependencies in terms of the actual outputs of your stores and thus provides self-documenting code that can make it easier to reason about your application architecture down the line.
108 |
109 | Below is the full API documentation for the `architect` method.
110 |
111 | ***
112 |
113 | #### 2.1) Architecture.architect [#](#Architecture-architect)
114 | Accepts a store and returns an `architectureChain` instance to define the store's inputs and outputs.
115 |
116 | ```javascript
117 | var architectureChain = Architecture.architect(storeToArchitect);
118 | ```
119 |
120 | ***
121 |
122 | ##### 2.1.1) Parameter - `storeToArchitect` - type: OBJECT - required [#](#architect-storeToArchitect)
123 | Store to architect. The returned architectureChain instance will be able to add dependencies to this store, and define the outputs that other stores can use to add a dependency for this store.
124 |
125 | ```javascript
126 | var storeToArchitect = require('../stores/userStore');
127 | ```
128 |
129 | ##### Return - `architectureChain` - type: OBJECT [#](#architect-architectureChain)
130 | Object to chain architecture methods onto. It is defined below.
131 |
132 | ***
133 |
134 | #### 2.2) architectureChain - type: OBJECT [#](#architectureChain)
135 | The `architectureChain` instance returned from the `Architecture.architect` method. It possesses the following methods, each of which return this same architectureChain instance in order to allow method chaining:
136 |
137 | ***
138 |
139 | ##### 2.2.1) Method - `architectureChain.itNeeds` - type: FUNCTION [#](#architectureChain-itNeeds)
140 | Accepts any number of inputs: stores, output strings, or arrays of stores and/or output strings. Adds a dependency to the `storeToArchitect` for each store mapped to the passed in inputs. Returns this `architectureChain` instance for continued method chaining.
141 |
142 | ```javascript
143 | architectureChain.itNeeds(storeOrInput, additionalStoresOrInputs...);
144 | ```
145 |
146 | ###### Parameter - `storeOrInput` - type: STRING, OBJECT, ARRAY of STRINGs and/or OBJECTs - required [#](#itNeeds-storeOrInput)
147 | Adds a dependency for the store or stores that map to the passed in string. If a store is passed in as an input, it adds a dependency for the passed in store. If an array is passed in is an input, it adds a dependency for each element in the passed in array. This method will do this for each input.
148 |
149 | ```javascript
150 | storeOrInput = 'users'; //OR
151 | storeOrInput = userStore;
152 | ```
153 |
154 | The following examples demonstrate passing in multiple inputs to `itNeeds`.
155 |
156 | ```javascript
157 | storeOrInput = ['users', roomStore]; //OR
158 |
159 | storeOrInput = 'users';
160 | additionalStoreOrInput = roomStore;
161 | ```
162 |
163 | ###### Return - `architectureChain` - type: OBJECT [#](#itNeeds-architectureChain)
164 | Returns this `architectureChain` instance to allow for continued method chaining.
165 |
166 | ```javascript
167 | console.assert(architectureChain === architectureChain.itNeeds('users'));
168 | ```
169 |
170 | ***
171 |
172 | ##### 2.2.2) Method - `architectureChain.itOutputs` - type: FUNCTION [#](#architectureChain-itOutputs)
173 | Accepts any number of inputs: strings to output or arrays of strings to output. Maps this `storeToArchitect` to the passed in output(s) so other stores can add dependencies for this store. If another store is already mapped to this output than stores that need the output will become dependent on both of these stores. Returns this `architectureChain` instance for continued method chaining.
174 |
175 | ```javascript
176 | architectureChain.itOutputs(output, additionalOutputs...);
177 | ```
178 |
179 | ###### Parameter - `output` - type: STRING, ARRAY of STRINGs - required [#](#itOutputs-output)
180 | Registers the store to the passed in `output` string such that other stores that need this `output` string will be dependent on this store. If an array is passed in the method registers the store with each `output` string in the passed in array. This method will perform this operation for each input. Multiple stores can have the same outputs, in which case a store that needs the string will be dependent on every store that outputs it.
181 |
182 | ```javascript
183 | output = 'rooms';
184 | ```
185 |
186 | The following examples demonstrate passing in multiple inputs to `itOutputs`.
187 |
188 | ```javascript
189 | output = ['rooms', 'unreadCount']; //OR
190 | output = 'rooms'; additionalOutput = 'unreadCount';
191 | ```
192 |
193 | ###### Return - `architectureChain` - type: OBJECT [#](#itOutputs-architectureChain)
194 | Returns this `architectureChain` instance to allow for continued method chaining.
195 |
196 | ```javascript
197 | console.assert(architectureChain === architectureChain.itOutputs('users'));
198 | ```
199 |
200 | ***
201 |
202 | ##### 2.2.3) Method - `architectureChain.and` - type: FUNCTION [#](#architectureChain-and)
203 | Clones the last invoked method for conjunctive method chaining. Thus, `and` will become the `itNeeds` method after `itNeeds` is invoked. After `itOuputs` is invoked `and` becomes the `itOutputs` method.
204 |
205 | ```javascript
206 | architectureChain.itOutputs('rooms');
207 | console.assert(architectureChain.and === architectureChain.itOutputs);
208 |
209 | architectureChain.itNeeds('users');
210 | console.assert(architectureChain.and === architectureChain.itNeeds);
211 | ```
212 |
213 | TuxxArchitecture is a very simple module but its ramifications are dramatic. It turns the process of writing out store dependencies from tedious, confusing, and error-prone to easy, fun, and self-documenting.
214 |
--------------------------------------------------------------------------------
/docs/TuxxModularity.md:
--------------------------------------------------------------------------------
1 | # Tuxx Modularity
2 | ## Table of Contents
3 |
4 |
6 |
7 | ## Premise [#](#Premise)
8 | >Tuxx is a completely modular framework. This means that you can use as much or as little of Tuxx as you want in your own development projects. Whereas, most other frameworks have your require the whole framework every time you want to use a feature of it, with Tuxx you only have to require the Tuxx module you need and not the entire framework.
9 |
10 | ***
11 |
12 | ## Implementation [#](#Implementation)
13 | Tuxx's modular nature allows you to only require specifics modules you need. For example, if you wanted to require React and the default Fade animation component in your particular project, you need only:
14 |
15 | ```javascript
16 | var React = require('tuxx/React');
17 | var Fade = require('tuxx/Animations/Fade');
18 | ```
19 |
20 | Requiring specific Tuxx modules is simple. All you need to do is follow a basic folder hierarchy path in your require statements. Every Tuxx require statement starts with a **lowercase** `tuxx` followed by a `/`, the **capitalized** sub directory you want to require and another `/` and **capitalized** module name for each level of specificity within that folder.
21 |
22 | **Note:** When requiring a module like `('tuxx/Animations')`, it will look at the index.js file within that module by default. In this case, the index.js within Animations exposes the `createAnimation` function by requiring TuxxAnimations from Tuxx.
23 |
--------------------------------------------------------------------------------
/docs/TuxxMutableClass.md:
--------------------------------------------------------------------------------
1 | # TuxxMutableClass
2 |
3 | ## Table of Contents
4 |
5 |
22 |
23 |
24 | ## Premise [#](#Premise)
25 | >The `TuxxMutableClass` is an opinionated type of [React](http://facebook.github.io/react/) class designed compare specified mutable traits to determine if a component should re-render.
26 |
27 | ***
28 |
29 | ## Implementation [#](#Implementation)
30 | `Tuxx` determines if a mutable class component should re-render by taking advantage of the `componentWillMount` and `shouldComponentUpdate` lifecycle events. Specifically, in the `componentWillMount` lifecycle, the component will do a deep search of its `state` and `props` to create paths to the specified `mutableTraits`. The paths are then attached to the constructor's prototype, such that a deep search to create the paths only needs to be done once for a particular component class.
31 |
32 | These `mutableTraits` paths are then used in the `shouldComponentUpdate` lifecycle event to see if those properties have changed when comparing the current state and props to the next props and state of the component. If those state and/or props have not changed then `shouldComponentUpdate` will return false which indicates to React that it does not need to update the component in the DOM. By performing this check you can prevent the unneeded re-rendering of components within the virtual DOM each time a potential re-render is triggered.
33 |
34 | The mutable class in `Tuxx` is an extension of the `OwneeClass`, which is an implementation on top of React's `createClass` method. In laymen's terms this means that it has access to all of the same [lifecycle](http://facebook.github.io/react/docs/component-specs.html) methods as a normal React class. Additionally, it requires a `render` method just like any other React component.
35 |
36 | ***
37 |
38 | ### 1) Requiring TuxxMutableClass [#](#Requiring-TuxxMutableClass)
39 | The `TuxxMutableClass` method `createMutableClass` is exposed by requiring the `Tuxx` React module, which can be required via:
40 |
41 | ```javascript
42 | var React = require('tuxx/React');
43 | ```
44 |
45 | **Note that at this time for `Tuxx` to be compiled by the JSX transformer, the `Tuxx` module must be assigned to the variable name `React`**
46 |
47 | ***
48 |
49 | ### 2) Creating Mutable Class Components [#](#creating-mutable-class-components)
50 | The `Tuxx` extended `React` object allows users to create mutable class components via invoking [React.createMutableClass](#createMutableClass). Pass in an object to the `createMutableClass` method and it will return a `TuxxMutableClass` which is an extension of a generic React Class.
51 |
52 | Let's take a look at an example: [#](#create-mutable-class-example)
53 |
54 |
55 | ```javascript
56 | var Message = React.createMutableClass({
57 | // the traits we want to watch to see if a re-render is required
58 | mutableTraits: {
59 | props: ['text', 'timestamp'],
60 | state: 'editing'
61 | },
62 |
63 | render: function () {
64 | return (
65 |
66 |
{this.props.message.text}
67 |
68 | );
69 | }
70 | });
71 | ```
72 |
73 | Below is the api documentation for this method.
74 |
75 | ***
76 |
77 | ### 3) React.createMutableClass [#](#React-createMutableClass)
78 |
79 | ```javascript
80 | var Message = React.createMutableClass(mutableClassProps);
81 | ```
82 |
83 | ***
84 |
85 | #### 3.1) Parameter - `mutableClassProps` - type: OBJECT - required [#](#createMutableClass-mutableClassProps)
86 | Methods and properties to extend the returned `mutableClass` object with. Expected keys:
87 |
88 | ##### Property - `mutableClassProps.mutableTraits` - type: OBJECT - optional [#](#mutableClassProps-mutableTraits)
89 | The traits that will be registered onto the mutable class.
90 |
91 | **NOTE: If the `mutableTraits` property is not included in the `mutableClassProps` object, instead of applying the TuxxMutableRenderMixin to the return object, the PureRenderMixin will be applied instead. The PureRenderMixin implements `shouldComponentUpdate`, but it only does a shallow compare of the top-most keys in props and state.**
92 |
93 | Expected keys:
94 |
95 | ###### Property - `mutableTraits.props` - type: STRING, ARRAY of STRINGs, ARRAY of ARRAYs of STRINGs - optional [#](#mutableTraits-props)
96 | A trait, or array of traits, or an array of arrays of traits, within the components props object to create a path for. An example will follow the `mutableTraits.state` explanation.
97 |
98 | ###### Property - `mutableTraits.state` - type: STRING, ARRAY of STRINGs, ARRAY of ARRAYs of STRINGs - optional [#](#mutableTraits-state)
99 | A trait, or array of traits, or an array of arrays of traits, within the components state object to create a path for.
100 |
101 | ```javascript
102 | var mutableClassProps = {};
103 | mutableClassProps.mutableTraits = {
104 | props: 'text',
105 | state: 'editing'
106 | };
107 |
108 | // OR
109 | mutableClassProps.mutableTraits = {
110 | props: ['text', 'timestamp'],
111 | state: 'editing'
112 | };
113 |
114 | // OR it is possible to specify the hierarchy of keys to search through to improve performance of the one time deep search/provide greater specificity regarding the required property
115 | mutableClassProps.mutableTraits = {
116 | props: [
117 | ['message', 'text'],
118 | ['message', 'timestamp']
119 | ],
120 | state: 'editing'
121 | };
122 | ```
123 |
124 | ***
125 |
126 | #### 3.2) Parameter - `mutableClassProps.render` - type: FUNCTION - required [#](#mutableClassProps-render)
127 | As with any other `React` class, a [render](#http://facebook.github.io/react/docs/component-specs.html#render) method is required. This method is responsible for returning a `React Element` which can be rendered onto the DOM.
128 |
129 | ```javascript
130 | mutableClassProps.render = function () {
131 | return (
132 |
133 |
hello world
134 |
135 | );
136 | };
137 | ```
138 |
139 | ***
140 |
141 | #### 3.3) Methods/Properties - `mutableClassProps.LIFECYCLE_AND_SPECS` - type: FUNCTIONs - optional [#](#mutableClassProps-LIFECYCLE_AND_SPECS)
142 | As with any other `React` class, the `mutableClassProps` can optionally include `React` [lifecycle methods and specs](#http://facebook.github.io/react/docs/component-specs.html).
143 |
144 | ```javascript
145 | mutableClassProps.componentDidMount = function () {
146 | roomActions.get();
147 | };
148 | ```
149 |
150 | #### Return - `mutableClass` - type: OBJECT [#](#createMutableClass-mutableClass)
151 | Returns a React class augmented with special `Tuxx` mixins.
152 |
153 | ***
154 |
155 | ## TuxxMutableClass Guidelines [#](#TuxxMutableClass-Guidelines)
156 | We recommend using the mutable class in situations where you are rendering many components of the same class, and only some of the state and props in those components will ever update.
157 |
158 | A specific example is if you have tens or hundreds of message components rendering on the screen. The mutable class would be beneficial at that point because most of those message components can update, but will not necessarily update with great frequency. Therefore having components that will check to see if they should re-render in the virtual DOM can result in a performance boost with the more messages that are on the screen. If someone edits the text of a message, instead of re-rendering all of the other messages in the virtual DOM, the mutable class will only re-render the message that has to update.
159 |
160 | ***
161 |
162 | ## TuxxMutableClass Complete Example [#](#TuxxMutableClass-Complete-Example)
163 | Taking all that we have learned so far let's take a look at a more realistic example.
164 |
165 | ```javascript
166 | var React = require('tuxx/React');
167 |
168 | var Message = React.createMutableClass({
169 | mutableTraits: {
170 | props: [
171 | ['message', 'text']
172 | ],
173 | state: 'editing'
174 | },
175 |
176 | getInitialState: function () {
177 | return {
178 | editing: false
179 | };
180 | },
181 |
182 | deleteMessage: function(e) {
183 | e.preventDefault();
184 | this.nearestOwnerProps.deleteMessage(this.props.message.id); // the mutableClass is a child of an ownerClass component
185 | },
186 |
187 | updateMessage: function (e) {
188 | e.preventDefault();
189 | var messageNode = this.refs.editMessage.getDOMNode();
190 | this.nearestOwnerProps.updateMessage(message.value, this.props.message.id);
191 | messageNode.value = '';
192 | this.edit();
193 | },
194 |
195 | edit: function(e) {
196 | e.preventDefault();
197 | if (this.isMounted()) {
198 | this.setState({ editing: !this.state.editing });
199 | }
200 | },
201 |
202 | render: function () {
203 | var editForm;
204 | var message = this.props.message;
205 |
206 | if (this.state.editing) {
207 | editForm = (
208 |
222 | );
223 | }
224 | });
225 | ```
226 |
227 | In this example the Message component takes advantage of the mutable class by registering the `this.state.editing` and `this.props.message.text` as the mutable traits of this component using the `mutableTraits` property. Whenever this component is triggered to update it will check to see if `this.state.editing` and/or `this.props.message.text` has updated. If neither has then `shouldComponentUpdate` will return false and the component will not need to re-render in the virtual DOM.
228 |
--------------------------------------------------------------------------------
/docs/TuxxRouter.md:
--------------------------------------------------------------------------------
1 | # Tuxx/Router
2 |
3 | ## Table of Contents
4 |
5 |
26 |
27 |
28 | ## Premise [#](#Premise)
29 | >Tuxx Stores provides an interface for abstracting away common store methods in the Flux architecture.
30 |
31 | ***
32 |
33 | ## Implementation [#](#Implementation)
34 | ### 1) Requiring Stores [#](#Requiring-Stores)
35 | Tuxx uses Facebook's [Flux](https://facebook.github.io/flux/) architecture and provides an interface for creating stores. This interface is exposed via:
36 |
37 | ```javascript
38 | var Stores = require('tuxx/Stores')
39 | ```
40 |
41 | ***
42 |
43 | ### 2) Creating Stores [#](#Creating-Stores)
44 | The `Stores` object allows the user to create stores via invoking [Stores.createStore](#createStores). Stores are responsible for managing data and alerting views to when that data has changed in TuxedoJS. Pass in an object to the `createStore` method and it will return a new object that has been extended with the `emitChange`, `addChangeListener`, and `removeChangeListener` methods that are common to Flux stores. The Tuxx methods are added to the object being extended first, so if you want to overwrite Tuxx's implementation of one or all of those methods, you just need to provide your methods with those keys on the input object to `createStore`. Let's take a look at an example: [#](#create-store-example)
45 |
46 |
47 | ```javascript
48 | var messageStore = Stores.createStore({
49 | _messages: {},
50 | getAll: function () {
51 | return Object.keys(this._messages);
52 | }
53 | });
54 | ```
55 |
56 | Below is the API documentation for this method.
57 |
58 | ***
59 |
60 | ### 3) Stores.createStore [#](#Stores-createStore)
61 |
62 | ```javascript
63 | var store = Stores.createStore(methods);
64 | ```
65 |
66 | ***
67 |
68 | #### 3.1) Parameter - `methods` - type: OBJECT - optional [#](#createStore-methods)
69 | Methods and properties to extend the created store with. This object is optional and there are no expected keys. However normally, as in the case of the example above, a store will have properties associated with storing its data and methods for exposing that data to view components.
70 |
71 | #### Return - `store` - type: OBJECT [#](#createStore-store)
72 | The `Stores.createStore` method returns a `store` instance which is described below.
73 |
74 | ***
75 |
76 | ### 4) store - type: OBJECT [#](#store)
77 | The store has the following methods:
78 |
79 | ***
80 |
81 | #### 4.1) Method `store.emitChange` - type: FUNCTION [#](#store-emitChange)
82 | Normally, it is common to invoke `emitChange` after data in the store is updated to alert the views that are listening to this store to request the new data. The change event can be either the default event string `CHANGE` or one provided by the user.
83 |
84 | ```javascript
85 | store.emitChange(EVENT_TYPE); // OR
86 | store.emitChange(); // will emit the default change event type string: 'CHANGE'
87 | ```
88 |
89 | ##### Parameter - `EVENT_TYPE` - type: STRING - optional [#](#emitChange-EVENT_TYPE)
90 | Change event type string that will be broadcast by the store. Will trigger callbacks that are listening for this event type on the store. If no `EVENT_TYPE` is specified the default value of `CHANGE` will be used.
91 |
92 | ```javascript
93 | var newEventType = 'NEW_EVENT_TYPE';
94 | store.emitChange(newEventType);
95 | ```
96 |
97 | ***
98 |
99 | ### 5) Method `store.addChangeListener` - type: FUNCTION [#](#store-addChangeListener)
100 | The `addChangListener` method will invoke the provided callback function when the specified event type string is emitted. The change event can be either the default event string `CHANGE` or one provided by the user.
101 |
102 | ```
103 | store.addChangeListener(callback, EVENT_TYPE); // OR
104 | store.addChangeListener(callback); // this will add a listener for the default CHANGE event
105 | ```
106 |
107 | ***
108 |
109 | ##### 5.1) Parameter - `callback` - type: FUNCTION - required [#](#addChangeListener-callback)
110 | The callback to invoke when the corresponding event is triggered.
111 |
112 | ```javascript
113 | var listenerCallback = function () {
114 | this.setState({ messages: MessageStore.all() });
115 | };
116 |
117 | store.addChangeListener(listenerCallback);
118 | ```
119 |
120 | ***
121 |
122 | ##### 5.2) Parameter - `EVENT_TYPE` - type: STRING - optional [#](#addChangeListener-EVENT_TYPE)
123 | Event string to add a listener callback to. If no event is passed in than the listener will be added to the default `CHANGE` event.
124 |
125 | ```javascript
126 | var listenerCallback = function () {
127 | this.setState({ messages: MessageStore.all() });
128 | };
129 |
130 | var newEventType = 'NEW_EVENT_TYPE';
131 | store.addChangeListener(listenerCallback, newEventType);
132 | ```
133 |
134 | ***
135 |
136 | ### 6) Method `store.removeChangeListener` - type: FUNCTION [#](#store-removeChangeListener)
137 | Removes the passed in listener callback from the passed in event string. If no event is specified than the default `CHANGE` event is used.
138 |
139 | ```javascript
140 | store.removeChangeListener(callback, EVENT_TYPE);
141 | ```
142 |
143 | ***
144 |
145 | ##### 6.1) Parameter - `callback` - type: FUNCTION - required [#](#removeChangeListener-callback)
146 | The callback function that will be removed from the store's set of listeners.
147 |
148 | ```javascript
149 | var listenerCallback = function () {
150 | this.setState({ messages: MessageStore.all() });
151 | };
152 |
153 | store.addChangeListener(listenerCallback);
154 | store.removeChangeListener(listenerCallback);
155 | ```
156 |
157 | ***
158 |
159 | ##### 6.2) Parameter - `EVENT_TYPE` - type: STRING - optional [#](#removeChangeListener-EVENT_TYPE)
160 | Store event type string that the listener will be removed from. If no `EVENT_TYPE` is specified the default value of `CHANGE` will be used.
161 |
162 | ```javascript
163 | var listenerCallback = function () {
164 | this.setState({ messages: MessageStore.all() });
165 | };
166 |
167 | var newEventType = 'NEW_EVENT_TYPE';
168 | store.addChangeListener(listenerCallback, newEventType);
169 | store.removeChangeListener(listenerCallback, newEventType);
170 | ```
171 |
172 | ***
173 |
174 | ## Store Event And Method Guidelines [#](#store-event-and-method-guidelines)
175 | While it is possible to emit any kind of event you want from your stores in `Tuxx`. We strongly recommend that you stick with emitting just the default `CHANGE` event. If you are concerned about rendering components unnecessarily (a view responding to a `CHANGE` event even when the relevant data for the view hasn't updated), then use the `shouldComponentUpdate` life-cycle method to let the view decide for itself if it needs to update.
176 |
177 | Next, we strongly recommend you equip the store with a variety of methods to expose exactly the kind of data needed by the views. If you come across a new view that needs a new piece of data in a store, add a method to the store to expose that new kind of data.
178 |
179 | Why are these approaches better? For event emitting, tracking down bugs that pop up when an `EVENT_TYPE` string is misspelled can be a real pain and `React` is really, really fast (especially with `shouldComponentUpdate`), which means there is never anything to be gained by making sure only some components go and get new data from the store. Additionally, a store should never be making decisions about who needs to know what information. It simply needs to be responsible for tracking its data, emitting updates, and exposing its data.
180 |
181 | For the methods, it allows you to program declaratively since you are asking for precisely the data you need from the store and it also significantly reduces the likelihood of bugs popping up in your app since you won't have data manipulations scattered across your different views.
182 |
183 | ```javascript
184 | //BAD
185 | var messageStore = Stores.createStore({
186 | _messages: [],
187 |
188 | get: function () {
189 | return this._messages;
190 | },
191 |
192 | onFirstMessageChange: function (newMessage) {
193 | this._messages.unshift(newMessage);
194 | this.emitChange('NEW_FIRST_MESSAGE');
195 | }
196 | });
197 |
198 | var getNewFirstMessageFromStore = function () {
199 | return messageStore.get()[0];
200 | };
201 |
202 | //Any mistake in our string spelling and this will silently fail
203 | messageStore.addChangeListener(getNewFirstMessageFromStore, 'NEW_FIRST_MESSAGE');
204 |
205 | //GOOD
206 | var messageStore = Stores.createStore({
207 | _messages: [],
208 |
209 | //provide declarative methods for requesting the data needed from the store
210 | getFirst: function () {
211 | return this._messages[0];
212 | },
213 |
214 | //always emit the default change event
215 | onFirstMessageChange: function (newMessage) {
216 | this._messages.unshift(newMessage);
217 | this.emitChange();
218 | }
219 | });
220 |
221 | var getNewFirstMessageFromStore = function () {
222 | return messageStore.getFirst();
223 | };
224 |
225 | messageStore.addChangeListener(getNewFirstMessageFromStore);
226 | ```
227 |
228 | ***
229 |
230 | ## TuxxStores Complete Example [#](#TuxxStores-Complete-Example)
231 | Taking everything we have learned let's look at a more realistic example of using TuxxStores.
232 |
233 | ```javascript
234 | var Stores = require('tuxx/Stores');
235 |
236 | var messageStore = Stores.createStore({
237 | _messages: [],
238 |
239 | allMessages: function () {
240 | return this._messages;
241 | },
242 |
243 | onGet: function (data) {
244 | this._messages = data.messages;
245 | this.emitChange();
246 | },
247 |
248 | onCreate: function (message) {
249 | this._messages.push(message);
250 | this.emitChange();
251 | },
252 |
253 | onDelete: function (message) {
254 | this._messages.splice(message.id, 1);
255 | this.emitChange();
256 | }
257 | });
258 |
259 | console.assert(messageStore.hasOwnProperty('emitChange'));
260 | console.assert(messageStore.hasOwnProperty('addChangeListener'));
261 | console.assert(messageStore.hasOwnProperty('removeChangeListener'));
262 |
263 | // In Tuxx we have better and more semantic methods for performing the below set of operations but let's use standard React syntax for the moment
264 |
265 | var React = require('tuxx/React');
266 | var Messages = require('./Messages.jsx'); // This component isn't built out here as it isn't explicitly needed to illustrate this example
267 |
268 | var MessageView = React.createOwnerClass({
269 | getInitialState: function () {
270 | return {
271 | messages: messageStore.getAll()
272 | };
273 | },
274 |
275 | listenerCallback: function () {
276 | this.setState({
277 | messages: messageStore.getAll()
278 | });
279 | },
280 |
281 | componentDidMount: function () {
282 | this.listenerCallback = this.listenerCallback.bind(this); //need to do this so we make sure listenerCallback is invoked with the proper context while it is in scope. If we do not do this, then we won't have the proper function when we go to remove it in the componentWillUnmount method
283 | messageStore.addChangeListener(this.listenerCallback);
284 | },
285 |
286 | componentWillUnmount: function () {
287 | messageStore.removeChangeListener(this.listenerCallback);
288 | },
289 |
290 | render: function () {
291 | return (
292 |
293 | );
294 | }
295 | };
296 | ```
297 |
298 | Hopefully this example helps to demonstrate some of the benefits of using the `createStore` method as it allows you to significantly reduce boilerplate when creating stores in your application.
299 |
--------------------------------------------------------------------------------
/jest-preprocessor.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var ReactTools = require('react-tools');
4 |
5 | module.exports = {
6 | process: function(src) {
7 | return ReactTools.transform(src, {
8 | harmony: true
9 | });
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tuxx",
3 | "version": "0.1.0",
4 | "description": "Framework built on React and Flux",
5 | "main": "Tuxedo.js",
6 | "scripts": {
7 | "test": "jest",
8 | "start": "watchify --extension=.jsx -d Tuxedo.js -o bundle.js -v"
9 | },
10 | "browserify": {
11 | "transform": [
12 | [
13 | "reactify",
14 | {
15 | "es6": true
16 | }
17 | ],
18 | "envify"
19 | ]
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "https://github.com/TuxedoJS/TuxedoJS"
24 | },
25 | "keywords": [
26 | "TuxedoJs",
27 | "Tuxx",
28 | "tuxx",
29 | "tuxedoJS",
30 | "tuxedo",
31 | "Tuxedo"
32 | ],
33 | "author": "drabinowitz, cheerazar, plauer, sjstebbins",
34 | "license": "MIT",
35 | "bugs": {
36 | "url": "https://github.com/TuxedoJS/TuxedoJS/issues"
37 | },
38 | "homepage": "https://github.com/TuxedoJS/TuxedoJS",
39 | "dependencies": {
40 | "arrival": "^1.0.0",
41 | "browserify": "^8.0.2",
42 | "envify": "^3.2.0",
43 | "events": "^1.0.2",
44 | "flux": "^2.0.1",
45 | "object-assign": "^2.0.0",
46 | "react": "^0.12.2",
47 | "react-router": "^0.11.6",
48 | "react-validating-form": "^0.1.1",
49 | "reactify": "^0.17.1",
50 | "watchify": "^2.2.1"
51 | },
52 | "devDependencies": {
53 | "jest": "^0.1.37",
54 | "jest-cli": "^0.2.1",
55 | "react-tools": "^0.12.2"
56 | },
57 | "jest": {
58 | "scriptPreprocessor": "jest-preprocessor.js",
59 | "moduleFileExtensions": [
60 | "js",
61 | "json",
62 | "jsx"
63 | ],
64 | "unmockedModulePathPatterns": [
65 | "src/TuxxInvariant",
66 | "src/TuxxDeepSearch",
67 | "node_modules/react/",
68 | "node_modules/object-assign/",
69 | "Animations/Common"
70 | ]
71 | },
72 | "directories": {
73 | "doc": "docs"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Dispatcher.js:
--------------------------------------------------------------------------------
1 | var Flux = require('flux');
2 |
3 | //create new instance of dispatcher
4 | module.exports = new Flux.Dispatcher();
5 |
--------------------------------------------------------------------------------
/src/TuxxActionStore.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var createStore = require('tuxx/Stores').createStore;
4 | var Actions = require('tuxx/Actions');
5 |
6 | //createActionStore FUNCTION: creates a tuxxStore with 'register' convenience method for automatically registering the store with Actions.
7 | //@param methods OBJECT: key value pairs that will be added to the store being created
8 | //additional keys:
9 | // register FUNCTION: convenience method for invoking Actions.register. Actions.register will be invoked with the newly created tuxxStore and the return of the method at the register key. Thus, the register method should return an OBJECT following the normal requirements for the second input to Actions.register. The method will be invoked with the context of the tuxxStore.
10 | var createActionStore = function (methods) {
11 | var tuxxStore = createStore(methods);
12 | //if the tuxxStore has a register method than register it to tuxx/Actions
13 | var registerToActions = methods.register;
14 | if (registerToActions) {
15 | //invoke the Actions.register with our tuxxStore and the result of our TuxxStore.register method invoked with the context of the tuxxStore
16 | Actions.register(tuxxStore, registerToActions.call(tuxxStore));
17 | }
18 | //return our tuxxStore
19 | return tuxxStore;
20 | };
21 |
22 | module.exports = createActionStore;
23 |
--------------------------------------------------------------------------------
/src/TuxxActions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var invariant = require('tuxx/src/TuxxInvariant');
4 | //require in instance of Flux Dispatcher
5 | var Dispatcher = require('tuxx/src/Dispatcher.js');
6 |
7 | //Actions OBJECT
8 | var Actions = {};
9 |
10 | //expose Dispatcher in case user needs it
11 | Actions.__Dispatcher__ = Dispatcher;
12 |
13 | //Actions.register FUNCTION
14 | //@param storeToRegister OBJECT: TuxxStore for which actions will be registered, needed for waitFor syntax [ALTERNATE FUNCTION: function will be directly registered to dispatcher]
15 | //@param categoriesToRegister OBJECT
16 | //expected keys:
17 | //ACTION:CATEGORY OBJECT: key will be an action category created with the function defined below, value will be an object
18 | //expected keys:
19 | //ACTION:VERBS FUNCTION: key wil be an action verb within the category of actions, value is function to call when associated action is dispatched. function receives action payload OBJECT as input
20 | Actions.register = function (storeToRegister, categoriesToRegister) {
21 | //if function is passed in, register that instead
22 | if (typeof storeToRegister === 'function') {
23 | return Dispatcher.register(storeToRegister);
24 | }
25 | //registers actions to dispatcher callback under object, invoked when corresponding action is passed in
26 | //define listener
27 | var listener = {};
28 |
29 | //get categories
30 | var categories = Object.keys(categoriesToRegister);
31 | var categoriesLength = categories.length;
32 |
33 | //declare variables
34 | var category, categoryToRegister, action, actions, actionsLength, i, j, createdCategory, createdAction;
35 |
36 | //loop through categories
37 | for (i = 0; i < categoriesLength; i++) {
38 | //loop through actions in category
39 | category = categories[i];
40 | //get category object to register
41 | categoryToRegister = categoriesToRegister[category];
42 | //get actions in category to register
43 | actions = Object.keys(categoryToRegister);
44 | actionsLength = actions.length;
45 |
46 | //loop through actions
47 | for (j = 0; j < actionsLength; j++) {
48 | action = actions[j];
49 |
50 | //get action string from Actions[category][action] store key in listener with value of callback
51 | //throw errors if category has not been created or does not have desired actions
52 | //get category
53 | createdCategory = Actions[category];
54 | //if category does not exist throw error
55 | invariant(createdCategory, '"%s" category has not been created yet', category);
56 | //get action in category
57 | createdAction = createdCategory[action];
58 | //if category does not have action throw error
59 | invariant(createdAction, '"%s" category does not have action "%s"', category, action);
60 | //register listener at key of action.type with value of callback
61 | listener[createdAction.type] = categoryToRegister[action];
62 | }
63 | }
64 | //register to Dispatcher and add registration key to store
65 | storeToRegister.__registerId__ = Dispatcher.register(function (payload) {
66 | //look for store architecture
67 | var tuxxArchitecture = storeToRegister.__tuxxArchitecture__;
68 | //if found invoke waitFor with registerested architecture for store
69 | if (tuxxArchitecture) {
70 | Dispatcher.waitFor(tuxxArchitecture);
71 | }
72 | //look for actionType in listener
73 | var registeredAction = listener[payload.action.actionType];
74 | //if found invoke callback CONTEXT: store, ARGUMENTS: body, [payload if needed]
75 | if (registeredAction) {
76 | return registeredAction.call(storeToRegister, payload.action.body, payload);
77 | }
78 | });
79 | };
80 |
81 | //Actions.createActionCategory FUNCTION
82 | //@param actionCategoryProps OBJECT
83 | //expected keys:
84 | //category STRING: type of data actions will refer to, for example: 'messages', 'todos'
85 | //source STRING: source of action, for example: 'view_component', 'server_api'
86 | //actions ARRAY: verbs that will act on the category, for example: 'read', 'create', 'update', 'destroy'
87 | Actions.createActionCategory = function (actionCategoryProps) {
88 | //creates object under Actions at the key of the category
89 | var category = actionCategoryProps.category;
90 |
91 | //throw error if category has already been defined
92 | invariant(!Actions[category], 'Action Category "%s" is already defined', category);
93 |
94 | //assign actionCategory to Actions and return it
95 | var actionCategory = Actions[category] = new ActionCategory(actionCategoryProps);
96 | return actionCategory;
97 | };
98 |
99 | //ActionCategory CONSTRUCTOR
100 | //@param props OBJECT
101 | //expected keys:
102 | //category STRING
103 | //source STRING
104 | //actions STRING or ARRAY
105 | var ActionCategory = function (props) {
106 | //grab props for use in function
107 | var category = props.category;
108 | var source = props.source;
109 |
110 | //assign passed in properties to actionCategory instance
111 | this.__category__ = category;
112 | this.__source__ = source;
113 |
114 | //loop through actions and bind a separate function for each
115 | var action, actionType;
116 | var propActions = props.actions;
117 | var propActionsLength = propActions.length;
118 | for (var i = 0; i < propActionsLength; i++) {
119 | action = propActions[i];
120 |
121 | //construct full action name from CATEGORY_ACTION
122 | actionType = category + '_' + action;
123 |
124 | //bind actionType and assign actionType to action at key type
125 | //this is done to allow for semantically invoking actions directly
126 | var thisAction = this[action] = dispatchAction.bind(this, actionType, source);
127 | //store actionType on our function for reference in dispatching
128 | thisAction.type = actionType;
129 | }
130 | };
131 |
132 | //ActionCategory.before FUNCTION: replaces the current method at the passed in actionVerb with the callbackToInvokeBeforeDispatch and binds the previous actionVerb method to the first input of the callbackToInvokeBeforeDispatch
133 | //@param actionVerb STRING: action verb to replace with callbackToInvokeBeforeDispatch, callbackToInvokeBeforeDispatch will be invoked before dispatching or before the previous 'before' callback on this action verb [ALTERNATE ARRAY: array of string action verbs to add callback onto, callback will be invoked before dispatching or before the previous 'before' callback on each action verb in the array]
134 | //@param callbackToInvokeBeforeDispatch FUNCTION: callback to invoke before dispatching the action. Callback will receive two inputs:
135 | //1st input FUNCTION: the next callback to invoke in the callback chain. Pass an object into the function to invoke it with an actionBody
136 | //2nd input OBJECT: the actionBody. This will either be from the first invocation of the action or from the previous callback in the chain
137 | ActionCategory.prototype.before = function (actionVerb, callbackToInvokeBeforeDispatch) {
138 | //if the actionverb is an array than invoke the before method with each element in the array and the callbackToInvokeBeforeDispatch
139 | if (Array.isArray(actionVerb)) {
140 | var actionVerbLength = actionVerb.length;
141 | for (var i = 0; i < actionVerbLength; i++) {
142 | this.before(actionVerb[i], callbackToInvokeBeforeDispatch);
143 | }
144 | } else {
145 | //if the actionVerb does not correspond to any action in this category throw an error
146 | var thisAction = this[actionVerb];
147 | invariant(thisAction, 'could not find action: "%s" within category: "%s" when attempting to register the before callback', actionVerb, this.__category__);
148 |
149 | //bind the current action method to the first input of the callbackToInvokeBeforeDispatch and store that as the new action method
150 | this[actionVerb] = callbackToInvokeBeforeDispatch.bind(this, thisAction);
151 |
152 | //reassign the type property which is needed for registering listeners
153 | this[actionVerb].type = thisAction.type;
154 | }
155 | };
156 |
157 | //ActionCategory.register FUNCTION: registers store with Actions and maps action verbs within this action category to callbacks
158 | //@param storeToRegister OBJECT: TuxxStore for which actions will be registered, needed for waitFor syntax [ALTERNATE FUNCTION: function will be directly registered to dispatcher]
159 | //@param actionsToRegister OBJECT keys are action verbs, values are callbacks to be invoked when action is dispatched
160 | ActionCategory.prototype.register = function (storeToRegister, actionsToRegister) {
161 | var categoriesToRegister = {};
162 | //register this category with passed in actions
163 | categoriesToRegister[this.__category__] = actionsToRegister;
164 | Actions.register(storeToRegister, categoriesToRegister);
165 | };
166 |
167 | //dispatchAction FUNCTION
168 | //@param actionType STRING should be bound by ActionCategory, actionType of action to be dispatched
169 | //@param source STRING should be bound by ActionCategory, source of action to be dispatched
170 | //@param body OBJECT body of action to be dispatched
171 | var dispatchAction = function (actionType, source, body) {
172 | //define action
173 | var action = {
174 | actionType: actionType,
175 | body: body
176 | };
177 | //dispatch action and source
178 | Dispatcher.dispatch({
179 | source: source,
180 | action: action
181 | });
182 | };
183 |
184 | //export Actions
185 | module.exports = Actions;
186 |
--------------------------------------------------------------------------------
/src/TuxxAnimationEasings.js:
--------------------------------------------------------------------------------
1 | //Default easing cases
2 | module.exports = {
3 | easeInQuad: 'cubic-bezier(0.550, 0.085, 0.680, 0.530)',
4 | easeInCubic: 'cubic-bezier(0.550, 0.055, 0.675, 0.190)',
5 | easeInQuart: 'cubic-bezier(0.895, 0.030, 0.685, 0.220)',
6 | easeInQuint: 'cubic-bezier(0.755, 0.050, 0.855, 0.060)',
7 | easeInSine: 'cubic-bezier(0.470, 0.000, 0.745, 0.715)',
8 | easeInExpo: 'cubic-bezier(0.950, 0.050, 0.795, 0.035)',
9 | easeInCirc: 'cubic-bezier(0.600, 0.040, 0.980, 0.335)',
10 | easeInBack: 'cubic-bezier(0.600, -0.280, 0.735, 0.045)',
11 | easeOutQuad: 'cubic-bezier(0.250, 0.460, 0.450, 0.940)',
12 | easeOutCubic: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)',
13 | easeOutQuart: 'cubic-bezier(0.230, 1.000, 0.320, 1.000)',
14 | easeOutSine: 'cubic-bezier(0.390, 0.575, 0.565, 1.000)',
15 | easeOutExpo: 'cubic-bezier(0.190, 1.000, 0.220, 1.000)',
16 | easeOutCirc: 'cubic-bezier(0.075, 0.820, 0.165, 1.000)',
17 | easeOutBack: 'cubic-bezier(0.175, 0.885, 0.320, 1.275)',
18 | easeInOutQuad: 'cubic-bezier(0.455, 0.030, 0.515, 0.955)',
19 | easeInOutCubic: 'cubic-bezier(0.645, 0.045, 0.355, 1.000)',
20 | easeInOutQuart: 'cubic-bezier(0.770, 0.000, 0.175, 1.000)',
21 | easeInOutQuint: 'cubic-bezier(0.860, 0.000, 0.070, 1.000)',
22 | easeInOutSine: 'cubic-bezier(0.445, 0.050, 0.550, 0.950)',
23 | easeInOutExpo: 'cubic-bezier(1.000, 0.000, 0.000, 1.000)',
24 | easeInOutCirc: 'cubic-bezier(0.785, 0.135, 0.150, 0.860)',
25 | easeInOutBack: 'cubic-bezier(0.680, -0.550, 0.265, 1.550)'
26 | };
27 |
--------------------------------------------------------------------------------
/src/TuxxAnimations.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('tuxx/React');
4 | var Arrival = require('arrival');
5 | var Easings = require('tuxx/src/TuxxAnimationEasings');
6 | var deepSearch = require('tuxx/src/TuxxDeepSearch');
7 | var ReactTransitionGroup = require('tuxx/React/TransitionGroup');
8 |
9 | //createAnimationClass FUNCTION: creates an animationClass which will animate based on the passed in transitions object
10 | //@param transitions OBJECT: properties that define the default animation properties for the animation component wrapper
11 | //@param customClassName STRING: sets className prop of created component
12 | var createAnimationClass = function (transitions, customClassName) {
13 | //If a second parameter is passed in as the desired class name for the animation, set this as className, othewise, use the animation's default class name
14 | var className;
15 | if (customClassName) {
16 | className = customClassName;
17 | } else {
18 | className = transitions.className;
19 | }
20 | //React.createClass FUNCTION: function to create animation component class
21 | //@param OBJECT: componentMounting and render
22 | //Required keys:
23 | // render FUNCTION: since this is an implementation of React.createClass a render method is required
24 | // componentWillEnter FUNCTION: required for animation entering
25 | // componentWillLeave FUNCTION: required for animation leaving
26 | //Additional keys
27 | // componentDidEnter FUNCTION: use to define actions to run after an animation has entry completed
28 | // componentDidLeave FUNCTION: use to define actions to run after an animation has leave completed
29 | return React.createClass({
30 | // setAnimationDomNode FUNCTION: function to handle manipulating and setting a TransitionGroup in the DOM
31 | //@param action STRING: the transition key you are looking to affect
32 | //@param callback FUNCTION: callback attribute passed in from the containing function to act on function completion
33 | setAnimationDomNode: function (action, callback) {
34 | var componentToAnimate = this.getDOMNode();
35 | var startingAction = transitions[action];
36 | var endingAction = transitions[action + '-active'];
37 | // requestAnimationFrame FUNCTION: Calls the specified function updating an animation before the next browser repaint. Defined in window
38 | window.requestAnimationFrame(function () {
39 | for (var key in startingAction) {
40 | componentToAnimate.style[key] = startingAction[key];
41 | }
42 | window.requestAnimationFrame(function () {
43 | for (var key in endingAction) {
44 | componentToAnimate.style[key] = endingAction[key];
45 | }
46 | Arrival(componentToAnimate, callback);
47 | }.bind(this));
48 | }.bind(this));
49 | },
50 |
51 | componentWillMount: function () {
52 | //Change all custom props or add them if prop not defined in default
53 | for (var action in transitions) {
54 | if (action !== 'className') {
55 | var transition = transitions[action];
56 | for (var css in transition) {
57 | //Duration prop work-----------------------------------------
58 | var duration = this.props.duration;
59 | //Use default if no duration prop defined
60 | if (duration) {
61 | //Case to accept if duration prop is a number
62 | if (typeof duration === "number") {
63 | duration = duration + "ms";
64 | }
65 | transition['transition-duration'] = duration;
66 | }
67 |
68 | //Easing prop work-------------------------------------------
69 | var easing = this.props.easing;
70 | //Use default if no easing prop defined
71 | if (easing) {
72 | //Check to see if easing exists in default Easings object from the AnimationEasings module
73 | if (easing in Easings) {
74 | //Set easing to value of correpsonding key from Easings module object
75 | easing = Easings[easing];
76 | }
77 | transition['transition-timing-function'] = easing;
78 | }
79 |
80 | //Delay prop work--------------------------------------------
81 | var delay = this.props.delay;
82 | //Use default if no delay prop defined
83 | if (delay) {
84 | //Case to accept if delay prop is a number
85 | if (typeof delay === "number") {
86 | delay = delay + "ms";
87 | }
88 | transition['transition-delay'] = delay;
89 | }
90 |
91 | //Custom prop work-------------------------------------
92 | var custom = this.props.custom;
93 | if (custom) {
94 | var enter = transitions['enter-active'];
95 | var leave =transitions['enter-active'];
96 | for (var cssElement in custom) {
97 | var cssEl = custom[cssElement];
98 | enter[cssElement] = cssEl;
99 | leave[cssElement] = cssEl;
100 | }
101 | }
102 | }//End for css in transitions[action]
103 | }//End if action === className
104 | }//End for loop for transitions
105 | },
106 |
107 | componentWillEnter: function (callback) {
108 | this.setAnimationDomNode('enter', callback);
109 | },
110 |
111 | componentWillLeave: function (callback) {
112 | this.setAnimationDomNode('leave', callback);
113 | },
114 |
115 | render: function () {
116 | //Return new React.Dom element
117 | return (
118 | React.DOM.div(
119 | {
120 | className: className
121 | },
122 | this.props.animate
123 | )
124 | );
125 | }
126 | });
127 | };
128 |
129 | // createAnimationGroup FUNCTION: wraps custom animation class
130 | // @param Animation OBJECT: animation class based on custom properties
131 | // @param customClassName STRING: className to apply to AnimationGroup
132 | // @param tagToRender STRING: what tag the animationGroup will render as in the DOM - defaults to span
133 | var createAnimationGroup = function (Animation, customClassName, tagToRender) {
134 |
135 | tagToRender = tagToRender || 'span';
136 |
137 | //React.createClass FUNCTION: function to create animation component
138 | //@param OBJECT: component setState, props, and render
139 | return React.createClass({
140 | // Set toAnimate initial state as an array that wraps custom Animation component
141 | // *** toAnimate is an array because ReactTransitionGroup only accepts multiple elements if wrapped in a single array
142 | getInitialState: function () {
143 | return {
144 | toAnimate: []
145 | };
146 | },
147 | //Used to update toAnimate
148 | componentWillReceiveProps: function (newProps) {
149 | this.setState({
150 | toAnimate: [].concat(newProps.children)
151 | });
152 | },
153 |
154 | render: function () {
155 | //store this.props.id
156 | var id = this.props.id;
157 | //store toAnimate length
158 | var stateToAnimateLength = this.state.toAnimate.length;
159 | //Wrap each component in animation because ReactTransitionGroup only accepts one element. Store wrapped components in toAnimate
160 | var toAnimate = this.state.toAnimate.map(function (el) {
161 | var key;
162 | //check if id is a string or an array
163 | if (typeof id === 'string' || Array.isArray(id)) {
164 | //check if __tuxxAnimationKey__ is defined, If it is not than build it out using deepSearch
165 | var __tuxxAnimationKey__ = this.__tuxxAnimationKey__;
166 | if (!__tuxxAnimationKey__) {
167 | //search through props of the element for the id
168 | __tuxxAnimationKey__ = deepSearch(id, el.props, 'props');
169 | //store the result at the key of __tuxxnimationKey__
170 | this.__tuxxAnimationKey__ = __tuxxAnimationKey__;
171 | }
172 | //iterate through __tuxxAnimationKey__ to find key property in el
173 | var tuxxAnimationKeyLength = __tuxxAnimationKey__.length;
174 | //start with el and search down from there
175 | key = el;
176 | for (var i = 0; i < tuxxAnimationKeyLength; i++) {
177 | key = key[__tuxxAnimationKey__[i]];
178 | }
179 |
180 | //else if id is a number then set key equal to that
181 | } else if (typeof id === 'number') {
182 | key = id;
183 |
184 | //else if stateToAnimate is one element then set the key value to 0
185 | } else if (stateToAnimateLength) {
186 | key = 0;
187 | }
188 |
189 | //Pass in props to the Animation component
190 | return
191 | }.bind(this));
192 |
193 | return (
194 |
195 | {toAnimate}
196 |
197 | );
198 | }
199 | });
200 | };
201 |
202 | // createAnimation FUNCTION: creates animation by creating a custom class based on the passed in props and transitions and wraps that class in a React Transition Group via the createAnimationGroup function
203 | // @param transitions OBJECT: properties that define the default animation properties for the created animation class
204 | // @param customClassName STRING: defines className of animation class
205 | // @param tagToRender STRING: defines the type of tag the wrapping TransitionGroup will render as in the DOM
206 | var createAnimation = function (transitions, customClassName, tagToRender) {
207 | //Create class based on defined transitions
208 | var Animation = createAnimationClass(transitions, customClassName);
209 | //Wrap created class
210 | var AnimationGroup = createAnimationGroup(Animation, customClassName, tagToRender);
211 | //Return TransitionGroup wrapped animation class
212 | return AnimationGroup;
213 | };
214 |
215 | module.exports = createAnimation;
216 |
--------------------------------------------------------------------------------
/src/TuxxArchitectStores.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var invariant = require('tuxx/src/TuxxInvariant');
4 | //architecture OBJECT: stores will register their outputs to this object so that store inputs can lookup those outputs later
5 | var architecture = {};
6 |
7 | //ArchitectChain CONSTRUCTOR FUNCTION: produces architectChain instances which allow stores to chain methods for defining their inputs and outputs
8 | //@param storeToArchitect: store OBJECT that inputs and outputs will be registered to
9 | var ArchitectChain = function (storeToArchitect) {
10 | this.storeToArchitect = storeToArchitect;
11 | };
12 |
13 | //prototype methods
14 | //itNeeds FUNCTION: registers the input(s) and/or store(s) that the store will need to waitFor, returns this architectChain instance to allow for method chaining, updates "and" method to allow chaining via conjuction
15 | //@param inputOrStore STRING: input string to be checked against the architecture object for the corresponding store or set of stores [ALTERNATE OBJECT: if a store is passed in then lookup __registerId__ on the passed in store] [ALTERNATE ARRAY: can pass in an array of inputs and/or stores to be registered, method chaining will still function properly in this case]
16 | //can accept any number of input arguments: strings, objects, or arrays of strings and/or objects
17 | ArchitectChain.prototype.itNeeds = function (inputOrStore) {
18 | var i;
19 | var argumentsLength = arguments.length;
20 | //if more than one argument was submitted and then invoke itNeeds with each argument
21 | if (argumentsLength > 1) {
22 | for (i = 0; i < argumentsLength; i++) {
23 | this.itNeeds(arguments[i]);
24 | }
25 | } else {
26 | //array of ids to register to the store
27 | var waitForIds = [];
28 | //if inputOrStore is array
29 | if (Array.isArray(inputOrStore)) {
30 | var inputOrStoreLength = inputOrStore.length;
31 | //concat each inputOrStore's __registerId__ or set of __registerId__'s
32 | for (i = 0; i < inputOrStoreLength; i++) {
33 | waitForIds = waitForIds.concat(getRegisterId(inputOrStore[i]));
34 | }
35 | } else {
36 | //if inputOrStore is not an array then concat its corresponding __registerId__ or set of __registerId__'s
37 | waitForIds = waitForIds.concat(getRegisterId(inputOrStore));
38 | }
39 | //if the storeToArchitect has already been architected at some point than add waitForIds array on to existing array
40 | var tuxxArchitecture = this.storeToArchitect.__tuxxArchitecture__;
41 | if (tuxxArchitecture) {
42 | tuxxArchitecture = tuxxArchitecture.concat(waitForIds);
43 | } else {
44 | //if it has not been architected than just use waitForIds array
45 | tuxxArchitecture = waitForIds;
46 | }
47 | //attach architecture array to storeToArchitect at the key of __tuxxArchitecture__
48 | this.storeToArchitect.__tuxxArchitecture__ = tuxxArchitecture;
49 | }
50 | //update "and" method so user can chain additional needed inputs with "and"
51 | this.and = this.itNeeds;
52 | //return this architectChain instance to enable method chaining
53 | return this;
54 | };
55 |
56 | //itOutputs FUNCTION: registers the output(s) that the store will be mapped to in the architecture OBJECT, returns this architectChain instance to allow for method chaining, updates "and" method to allow chaining via conjuction
57 | //@param output STRING: output STRING to which storeToArchitect will be mapped to on the architecture OBJECT [ALTERNATE ARRAY: can pass in an array of output STRINGs to which store will be mapped to on the architecture OBJECT]
58 | //can accept any number of input arguments: strings or arrays of strings
59 | ArchitectChain.prototype.itOutputs = function (output) {
60 | var i;
61 | var argumentsLength = arguments.length;
62 | //if more than one argument was submitted and then invoke itOutputs with each argument
63 | if (argumentsLength > 1) {
64 | for (i = 0; i < argumentsLength; i++) {
65 | this.itOutputs(arguments[i]);
66 | }
67 | } else {
68 | if (Array.isArray(output)) {
69 | //if output is an array than map each string in array to architecture OBJECT
70 | var outputLength = output.length;
71 | for (i = 0; i < outputLength; i++) {
72 | registerOutputToArchitecture(output[i], this.storeToArchitect);
73 | }
74 | } else {
75 | registerOutputToArchitecture(output, this.storeToArchitect);
76 | }
77 | }
78 | //update "and" method so user can chain additional outputs with "and"
79 | this.and = this.itOutputs;
80 | //return this architectChain instance to enable method chaining
81 | return this;
82 | };
83 |
84 | //getRegisterId FUNCTION: internal function, for getting the store or set of stores' __registerId__ or set of __registerId__'s from either the passed in store or the store/set of stores mapped to the passed in input
85 | //@param inputOrStore STRING: input string to be checked against the architecture OBJECT for the corresponding store [ALTERNATE OBJECT: if a store is passed in then lookup __registerId__ on the passed in store]
86 | var getRegisterId = function (inputOrStore) {
87 | var storeOrStoresToWaitFor, storeRegistrationIdOrIds;
88 | //if inputOrStore is a string
89 | if (typeof inputOrStore === 'string') {
90 | //lookup corresponding store or stores in the architecture object
91 | storeOrStoresToWaitFor = architecture[inputOrStore];
92 | //if no store maps to this input throw error
93 | invariant(storeOrStoresToWaitFor, 'store is waiting for an input: "%s" that no store outputs. If this store needs no inputs than only call "itOutputs" method.', inputOrStore);
94 | } else {
95 | //if inputOrStore is a store use that
96 | storeOrStoresToWaitFor = inputOrStore;
97 | }
98 | //return __registerId__ or set of __registerId__'s from storeOrStoresToWaitFor
99 | storeRegistrationIdOrIds = returnOneOrMultipleRegisterIds(storeOrStoresToWaitFor);
100 | return storeRegistrationIdOrIds;
101 | };
102 |
103 | //returnOneOrMultipleRegisterIds FUNCTION: internal function, used by getRegisterId to return one or a set registerId's from a store or set of stores
104 | //@param storeOrStoresToWaitFor ARRAY or STRING: input string or array of strings representing all stores being waited on
105 | var returnOneOrMultipleRegisterIds = function (storeOrStoresToWaitFor) {
106 | var storeRegistrationId;
107 | //error to throw if a store does not have a __registerId__ property
108 | var noRegisteredIdError = 'store is waiting for a store that has not been registered to any actions.';
109 | //if storeOrStoresToWaitFor is an array holding multiple stores
110 | if (Array.isArray(storeOrStoresToWaitFor)) {
111 | var storeRegistrationIds = [];
112 | var storeOrStoresToWaitForLength = storeOrStoresToWaitFor.length;
113 | //grab the __registerId__ from each individual store and return them as an array
114 | for (var i = 0; i < storeOrStoresToWaitForLength; i++) {
115 | storeRegistrationId = storeOrStoresToWaitFor[i].__registerId__;
116 | //if store does not have __registerId__ then it has not been registered to the dispatcher so throw an error
117 | invariant(storeRegistrationId, noRegisteredIdError);
118 | storeRegistrationIds.push(storeRegistrationId);
119 | }
120 | return storeRegistrationIds;
121 | } else {
122 | //return __registerId__ from storeOrStoresToWaitFor
123 | storeRegistrationId = storeOrStoresToWaitFor.__registerId__;
124 | //if store does not have __registerId__ then it has not been registered to the dispatcher so throw an error
125 | invariant(storeRegistrationId, noRegisteredIdError);
126 | return storeRegistrationId;
127 | }
128 | };
129 |
130 | //registerOutputToArchitecture FUNCTION: internal function, registers an output STRING key to the architecture OBJECT with the value of storeToArchitect so that the storeToArchitect can later be looked up by passing the same output STRING to itNeeds
131 | //@param output STRING: output STRING to which storeToArchitect will be mapped to on the architecture OBJECT
132 | var registerOutputToArchitecture = function (output, storeToArchitect) {
133 | var mappedStores = architecture[output];
134 | //if this output is already registered to architecture OBJECT
135 | if (mappedStores) {
136 | var outputArray = [];
137 | //add storeToArchitect as another registered output to architecture OBJECT
138 | architecture[output] = outputArray.concat(mappedStores, storeToArchitect);
139 | } else {
140 | //if this output is not already registered to architecture OBJECT, register it
141 | architecture[output] = storeToArchitect;
142 | }
143 | };
144 |
145 | //architect FUNCTION: accepts a storeToArchitect and returns an instance of architectChain which will register the inputs and outputs for the passsed in store
146 | //@param storeToArchitect OBJECT: store that needs and outputs will be mapped to by the returned architectChain instance
147 | var architect = function (storeToArchitect) {
148 | return new ArchitectChain(storeToArchitect);
149 | };
150 |
151 | module.exports = architect;
152 |
--------------------------------------------------------------------------------
/src/TuxxDeepSearch.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var invariant = require('tuxx/src/TuxxInvariant');
4 |
5 | // deepSearch FUNCTION: recursive function that creates an array of string keys to transverse the object specified by the @param mutableTrait key. Currently matches only the first path found.
6 | // @param mutableTrait STRING: trait to find the path of keys within the objectToSearch [ALTERNATE ARRAY: to specify a hierarchy of keys to search pass in an ARRAY of STRINGS. The deepSearch order will look for the strings starting with the first index in the array]
7 | // @param objectToSearch OBJECT:
8 | // expected keys ANY: this object can contain any keys, but is expected to contain the key or keys specified in mutableTrait
9 | // @param currentPath ARRAY: the current path of the search [ALTERNATE STRING will wrap the string in an array in this case] If TuxxMutableRenderMixin is invoking deepSearch, currentPath will be either the string 'props' or 'state'
10 | var deepSearch = function (mutableTrait, objectToSearch, currentPath) {
11 | if (!Array.isArray(currentPath)) {
12 | //if currentPath is a string then wrap in an array
13 | if (currentPath) {
14 | currentPath = [currentPath];
15 | } else {
16 | //start with an empty array if currentPath is undefined
17 | currentPath = [];
18 | }
19 | }
20 |
21 | //if mutableTrait is an array
22 | if (Array.isArray(mutableTrait)) {
23 | var mutableTraitLength = mutableTrait.length;
24 | //invoke deepSearch with each element in the array, build out path over each iteration
25 | var fullPath = currentPath;
26 | var iterationPath, iterationPathLength;
27 | var i,j;
28 | for (i = 0; i < mutableTraitLength; i++) {
29 | iterationPath = deepSearch(mutableTrait[i], objectToSearch);
30 | //narrow down the objectToSearch by the iterationPath
31 | iterationPathLength = iterationPath.length;
32 | for (j = 0; j < iterationPathLength; j++) {
33 | objectToSearch = objectToSearch[iterationPath[j]];
34 | }
35 |
36 | //concat iterationPath onto fullPath
37 | fullPath = fullPath.concat(iterationPath);
38 | }
39 |
40 | //after looping through return the fullPath
41 | return fullPath;
42 | }
43 |
44 | if (typeof objectToSearch === 'object' && objectToSearch !== undefined && objectToSearch !== null) {
45 | if (objectToSearch.hasOwnProperty(mutableTrait)) {
46 | // concat mutableTrait onto currentPath and return the result
47 | return currentPath.concat(mutableTrait);
48 | }
49 |
50 | // declare reused variables
51 | var key, newPath, possibleResultPath;
52 |
53 | for (key in objectToSearch) {
54 | if (objectToSearch.hasOwnProperty(key)) {
55 | // add the new key onto currentPath
56 | newPath = currentPath.concat(key);
57 | //try a recursion and catch errors so we do not throw an uncaught exception if the nested deepSearch fails
58 | try {
59 | // recurse over the item at objectToSearch[key] with newPath as the currentPath input
60 | possibleResultPath = deepSearch(mutableTrait, objectToSearch[key], newPath);
61 | } catch (e) {}
62 | // break out of loop if a single valid path has been found
63 | if (possibleResultPath) {
64 | return possibleResultPath;
65 | }
66 | }
67 | }
68 | }
69 |
70 | //throw an error if we never found the key, this error will be caught in nested searches
71 | invariant(false, 'Could not find "%s" within object', mutableTrait);
72 | };
73 |
74 | module.exports = deepSearch;
75 |
--------------------------------------------------------------------------------
/src/TuxxGetOwnerPropsMixin.js:
--------------------------------------------------------------------------------
1 | //getOwnerPropsMixin OBJECT: adds mixin to React class that will attach the nearestOwner's __tuxxOwnerProps__ under the key of nearestOwnerProps
2 | //If component is a tuxx Owner Component:
3 | //expected Key:
4 | // registerOwnerProps FUNCTION: returns an OBJECT that represents the properties the owner will expose to its direct Ownees. The function will be invoked with the context of the owner component
5 | module.exports = {
6 | //add property under componentWillMount so that other lifecycle methods will be able to access it
7 | componentWillMount: function () {
8 | var owner = this._owner;
9 | //if the owner is an OwnerComponent get its __tuxxOwnerProps__, otherwise get its nearestOwnerProps
10 | while (owner) {
11 | //approach takes advantage of cascading order of componentWillMount invocations. Since componentWillMount is called on an owner before its ownee, a component can just read from its owner to get the needed props
12 | var nearestOwnerProps = owner.nearestOwnerProps;
13 | if (owner.__tuxxIsOwnerComponent__) {
14 | this.nearestOwnerProps = owner.__tuxxOwnerProps__;
15 | break;
16 | } else if (nearestOwnerProps) {
17 | this.nearestOwnerProps = nearestOwnerProps;
18 | break;
19 | }
20 | owner = owner._owner;
21 | }
22 | //if this component is an owner component and its ownerProps have not been registered already
23 | if (this.__tuxxIsOwnerComponent__ && !this.__tuxxOwnerProps__) {
24 | //invoke registerOwnerProps after getting nearestOwnerProps so that owner can cascade down nearestOwnerProps to its direct Ownees
25 | var __tuxxOwnerProps__ = this.registerOwnerProps.call(this);
26 | //assign object to key of __tuxxOwnerProps__ on component
27 | this.__tuxxOwnerProps__ = __tuxxOwnerProps__;
28 | }
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/src/TuxxInvariant.js:
--------------------------------------------------------------------------------
1 | // Requires invariant module so that error messages are only printed in development mode.
2 | var invariant = require("react/lib/invariant");
3 |
4 | module.exports = invariant;
5 |
--------------------------------------------------------------------------------
/src/TuxxMutableClass.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var owneeClass = require('tuxx/src/TuxxOwneeClass');
4 | var pureRenderMixin = require('react/lib/ReactComponentWithPureRenderMixin');
5 | var mutableRenderMixin = require('tuxx/src/TuxxMutableRenderMixin');
6 | var assign = require('object-assign');
7 |
8 | // createMutableClass FUNCTION: creates a mutable Tuxx Class which is a type of React class designed to compare specified mutable traits to determine if a component should re-render
9 | // @param mutableClassProps OBJECT: properties that the new mutableClass will possess, should be a standard React.createClass object with properties such as componentWillMount, componentDidMount, etc. Please see http://facebook.github.io/react/docs/component-specs.html for a full list of React props
10 | // required keys:
11 | // render FUNCTION: since this is an implementation of React.createClass a render method is required
12 | // additional keys
13 | // mutableTraits OBJECT: traits will be registered [ALTERNATE ARRAY: array of objects with same keys as listed below]
14 | // expected keys:
15 | // props STRING: a trait within the components props object [ALTERNATE ARRAY of trait strings to create paths for] [ALTERNATE ARRAY of ARRAYS of trait strings to create paths for]
16 | // state STRING: a trait within the components state object [ALTERNATE ARRAY of trait strings to create paths for] [ALTERNATE ARRAY of ARRAYS of trait strings to create paths for]
17 | // eg: mutableClassProps.mutableTraits => {
18 | // props: 'text',
19 | // state: 'editing'
20 | // }
21 | // OR
22 | // eg: mutableClassProps.mutableTraits => {
23 | // props: ['text', 'timestamp'],
24 | // state: 'editing'
25 | // }
26 | // OR it is possible to specify the hierarchy of keys to search through to improve performance of the one time deep search/provide greater specificity regarding the required property
27 | // eg: mutableClassProps.mutableTraits => {
28 | // props: [
29 | // ['message', 'text'],
30 | // ['message', 'timestamp']
31 | // ],
32 | // state: 'editing'
33 | // }
34 | var mixinsToAdd, mutableTraits, mixins;
35 | var TuxxMutableClass = function (mutableClassProps) {
36 | mixinsToAdd = [];
37 |
38 | // if mutableTraits is defined than add the mutableRenderMixin, else use the pureRenderMixin
39 | mutableTraits = mutableClassProps.mutableTraits;
40 | if (mutableTraits) {
41 | mixinsToAdd.push(mutableRenderMixin);
42 | } else {
43 | mixinsToAdd.push(pureRenderMixin);
44 | }
45 |
46 | // concat any mixins passed in through mutableClassProps to mixinsToAdd. Note that mixins is concated on to mixinsToAdd (not the other way around) so that all Tuxx mixins will be invoked first
47 | mixins = mutableClassProps.mixins;
48 | if (mixins) {
49 | mixinsToAdd = mixinsToAdd.concat(mixins);
50 | }
51 |
52 | //pass in props to owneeClass to get base tuxx mixins
53 | return owneeClass(assign({}, mutableClassProps, {
54 | mixins: mixinsToAdd
55 | }));
56 | };
57 |
58 | module.exports = TuxxMutableClass;
59 |
--------------------------------------------------------------------------------
/src/TuxxMutableRenderMixin.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var invariant = require('tuxx/src/TuxxInvariant');
4 | var deepSearch = require('tuxx/src/TuxxDeepSearch');
5 |
6 | // TuxxMutableRenderMixin OBJECT: adds mixin to React class that will add componentWillMount and shouldComponentUpdate life cycle events to the component
7 | module.exports = {
8 | // componentWillMount FUNCTION: creates an array of string key paths attached to the component's constructor with a key of __tuxxMutableTraits__.
9 | // These paths are used in the shouldComponentUpdate life cycle event to compare current and next values of the specified traits to determine
10 | // if the component should re-render.
11 | componentWillMount: function () {
12 | var mutableTraits = this.mutableTraits;
13 | var mutableTraitsPaths = [];
14 | var type, isArray, isString, mutableTraitsTypeLength, i, mutableType;
15 |
16 | // if __tuxxMutableTraits__ is not defined
17 | if (!this.constructor.__tuxxMutableTraits__) {
18 | for (type in mutableTraits) {
19 | // ensure mutableTraits hasOwnProperty type, otherwise it will walk up the prototype chain
20 | if (mutableTraits.hasOwnProperty(type)) {
21 | // store result of mutableTraits[type] to prevent repetitive lookups
22 | mutableType = mutableTraits[type];
23 |
24 | isArray = Array.isArray(mutableType);
25 | isString = typeof mutableType === 'string';
26 |
27 | invariant(isArray || isString, 'mutableTraits needs to be either an array or a string.');
28 |
29 | if (isArray) {
30 | mutableTraitsTypeLength = mutableType.length;
31 |
32 | for (i = 0; i < mutableTraitsTypeLength; i++) {
33 | mutableTraitsPaths.push(deepSearch(mutableType[i], this[type], type));
34 | }
35 | } else {
36 | mutableTraitsPaths.push(deepSearch(mutableType, this[type], type));
37 | }
38 |
39 | // add the trait paths to the constructor object, so only the first component of each class has to perform the deep search
40 | this.constructor.__tuxxMutableTraits__ = mutableTraitsPaths;
41 | }
42 | }
43 | }
44 | },
45 |
46 | // shouldComponentUpdate FUNCTION: add component life cycle event to shouldComponentUpdate that returns a boolean value to determine whether or not the component should update
47 | // @param nextProps OBJECT: the updated values for the component's props that will be examined to see if it should re-render
48 | // @param nextState OBJECT: the updated values for the component's state that will be examined to see if it should re-render
49 | shouldComponentUpdate: function (nextProps, nextState) {
50 | // get mutableTraits
51 | var mutableTraits = this.constructor.__tuxxMutableTraits__;
52 |
53 | invariant(mutableTraits, 'The __tuxxMutableTraits__ property is not defined on the component.');
54 |
55 | // if mutableTraits is present
56 | if (mutableTraits) {
57 | var mutableTraitsLength = mutableTraits.length;
58 | // declare reused variables
59 | var traitPathLength, trait, traitPath, currentPathValue, nextPathValue, traitValue, mutableTraitPath;
60 |
61 | // for each trait path see if current is equal to next
62 | for (traitPath = 0; traitPath < mutableTraitsLength; traitPath++) {
63 | // calculate length of current trait path
64 | mutableTraitPath = mutableTraits[traitPath];
65 | traitPathLength = mutableTraitPath.length;
66 | currentPathValue = this;
67 | // group nextProps and nextState into the same object to allow easy transversal down key paths stored in __tuxxMutableTraits__
68 | nextPathValue = {
69 | props: nextProps,
70 | state: nextState
71 | };
72 |
73 | // walk down to the end of each path in current and next objects
74 | for (trait = 0; trait < traitPathLength; trait++) {
75 | traitValue = mutableTraitPath[trait];
76 | currentPathValue = currentPathValue[traitValue];
77 | nextPathValue = nextPathValue[traitValue];
78 | }
79 |
80 | // if the currentPathValue doesn't equal nextPathValue the component should re-render.
81 | if (currentPathValue !== nextPathValue) {
82 | return true;
83 | }
84 | }
85 | }
86 |
87 | // nothing has changed, so return false, so component doesn't re-render
88 | return false;
89 | }
90 | };
91 |
--------------------------------------------------------------------------------
/src/TuxxOwneeClass.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var getOwnerPropsMixin = require('tuxx/src/TuxxGetOwnerPropsMixin');
3 | var propTypeCheckerMixin = require('tuxx/src/TuxxPropTypeCheckerMixin');
4 | var assign = require('object-assign');
5 |
6 | //createOwneeClass FUNCTION: creates an ownee Tuxx Class which is a type of React class not designed to manage any of its own state other than, in some cases, necessary state to implement two-way data-binding for input validation
7 | //@param owneeClassProps OBJECT: properties that the new owneeClass will possess, should be a standard React.createClass object with properties such as componentWillMount, componentDidMount, etc. Please see http://facebook.github.io/react/docs/component-specs.html for a full list of React props
8 | //required keys:
9 | // render FUNCTION: since this is an implementation of React.createClass a render method is required
10 | var mixinsToAdd, mixins;
11 | var createOwneeClass = function (owneeClassProps) {
12 | //add mixins that do not need to be generated via passed in props
13 | //getOwnerPropsMixin: allows Ownee to access the ownerProps of the nearest Owner through the key nearestOwnerProps
14 | mixinsToAdd = [getOwnerPropsMixin];
15 | //if not the production environment
16 | if ("production" !== process.env.NODE_ENV) {
17 | //if the nearestOwnerPropTypes or anyPropTypes keys are defined
18 | if (owneeClassProps.nearestOwnerPropTypes || owneeClassProps.anyPropTypes) {
19 | //add the prop checker mixin to the mixinsToAdd, note that this is added after the getOwnerPropsMixin so that nearestOwnerProps will be exposed for this mixin
20 | mixinsToAdd.push(propTypeCheckerMixin);
21 | }
22 | }
23 | //concat any mixins passed in through owneeClassProps to mixinsToAdd. Note that mixins is concated on to mixinsToAdd (not the other way around) so that all Tuxx mixins will be invoked first
24 | mixins = owneeClassProps.mixins;
25 | if (mixins) {
26 | mixinsToAdd = mixinsToAdd.concat(mixins);
27 | }
28 | //return React.createClass with augmented class properties
29 | return React.createClass(assign({}, owneeClassProps, {
30 | mixins: mixinsToAdd
31 | }));
32 | };
33 |
34 | module.exports = createOwneeClass;
35 |
--------------------------------------------------------------------------------
/src/TuxxOwnerClass.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var owneeClass = require('tuxx/src/TuxxOwneeClass');
4 | var StoreMixinGenerator = require('tuxx/src/TuxxStoreMixinGenerator');
5 | var assign = require('object-assign');
6 |
7 | //createOwnerClass FUNCTION: creates an owner Tuxx Class which is a type of React class designed to manage application state, interact with stores, manage route params, and pass props into its ownee components
8 | //@param ownerClassProps OBJECT: properties that the new ownerClass will possess, should be a standard React.createClass object with properties such as componentWillMount, componentDidMount, etc. Please see http://facebook.github.io/react/docs/component-specs.html for a full list of React props
9 | //required keys:
10 | // render FUNCTION: since this is an implementation of React.createClass a render method is required
11 | //additional keys
12 | //connectOwnerToStore FUNCTION: returns an OBJECT defining the TuxxStore for which callbacks will be registered. "this" will be bound to the component in this function. [ALTERNATE ARRAY: array of functions with the component as "this" that return objects with same keys as listed below]
13 | //expected keys:
14 | // store OBJECT: store object that the event and listener should be attached to
15 | // listener FUNCTION: callback function to be invoked upon associated event [ALTERNATE ARRAY: array of callback functions]
16 | // event STRING: event type to trigger listener callback function [ALTERNATE ARRAY: array of event type strings]
17 | //registerOwnerProps FUNCTION: returns an OBJECT defining the properties the owner will expose to its direct Ownees. The function will be invoked with the context of the owner component and all top level methods in the object will be bound to the component context
18 | var connectOwnerToStore, mixinsToAdd, mixins;
19 | var createOwnerClass = function (ownerClassProps) {
20 | mixinsToAdd = [];
21 | //if connectOwnerToStore is defined than add a storeMixin with it
22 | connectOwnerToStore = ownerClassProps.connectOwnerToStore;
23 | if (connectOwnerToStore) {
24 | mixinsToAdd.push(StoreMixinGenerator(connectOwnerToStore));
25 | }
26 | //concat any mixins passed in through ownerClassProps to mixinsToAdd. Note that mixins is concated on to mixinsToAdd (not the other way around) so that all Tuxx mixins will be invoked first
27 | mixins = ownerClassProps.mixins;
28 | if (mixins) {
29 | mixinsToAdd = mixinsToAdd.concat(mixins);
30 | }
31 | //pass in props to owneeClass to get base tuxx mixins
32 | return owneeClass(assign({}, ownerClassProps, {
33 | //add marker prop to indicate this class will be an owner component
34 | __tuxxIsOwnerComponent__: true,
35 | mixins: mixinsToAdd
36 | }));
37 | };
38 |
39 | module.exports = createOwnerClass;
40 |
--------------------------------------------------------------------------------
/src/TuxxPropTypeCheckerMixin.js:
--------------------------------------------------------------------------------
1 | var assign = require('object-assign');
2 |
3 | //mixin to perform type checking on nearestOwnerProps as well as perform checking on either nearestOwnerProps or props if the user is not interested in the source of the props
4 | module.exports = {
5 | componentWillMount: function () {
6 | //get the nearestOwnerPropTypes from the component
7 | var nearestOwnerPropTypes = this.nearestOwnerPropTypes;
8 | //if nearestOwnerPropTypes is defined
9 | if (nearestOwnerPropTypes) {
10 | //check the nearestOwnerProps
11 | this._checkPropTypes(nearestOwnerPropTypes, this.nearestOwnerProps, 'nearestOwnerProps');
12 | }
13 | //get the anyPropTypes from the component
14 | var anyPropTypes = this.anyPropTypes;
15 | //if anyPropTypes is defined
16 | if (anyPropTypes) {
17 | //check both the nearestOwnerProps and props. Note that this.props is passed in last so that it overwrites matching keys with this.nearestOwnerProps
18 | this._checkPropTypes(anyPropTypes ,assign({}, this.nearestOwnerProps, this.props), 'props or nearestOwnerProps');
19 | }
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/src/TuxxStore.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // require an instance of the EventEmitter
4 | var EventEmitter = require('events').EventEmitter;
5 | var assign = require('object-assign');
6 | // the default change event string
7 | var defaultChangeEvent = 'CHANGE';
8 |
9 | // TuxxStore FUNCTION
10 | // @param methods OBJECT: key value pairs that will be added to the store being created
11 | var TuxxStore = function (methods) {
12 | // creating a TuxxStore object then adding the EventEmitter prototype, method to emit change and listeners, and user specified methods
13 | return assign({}, EventEmitter.prototype, {
14 |
15 | // emitChange FUNCTION
16 | // @param CHANGE_EVENT String: optional event type string. defaultChangeEvent will be used otherwise.
17 | emitChange: function (CHANGE_EVENT) {
18 | CHANGE_EVENT = CHANGE_EVENT || defaultChangeEvent;
19 | this.emit(CHANGE_EVENT);
20 | },
21 |
22 | // addChangeListener FUNCTION
23 | // @param callback Function: function that will executed upon the specified CHANGE_EVENT
24 | // @param CHANGE_EVENT String: optional event type string. defaultChangeEvent will be used otherwise.
25 | addChangeListener: function (callback, CHANGE_EVENT) {
26 | CHANGE_EVENT = CHANGE_EVENT || defaultChangeEvent;
27 | this.on(CHANGE_EVENT, callback);
28 | },
29 |
30 | // removeChangeListener FUNCTION
31 | // @param callback Function: function that will be removed from being executed upon the specified CHANGE_EVENT
32 | // @param CHANGE_EVENT String: optional event type string. defaultChangeEvent will be used otherwise.
33 | removeChangeListener: function (callback, CHANGE_EVENT) {
34 | CHANGE_EVENT = CHANGE_EVENT || defaultChangeEvent;
35 | this.removeListener(CHANGE_EVENT, callback);
36 | }
37 | }, methods);
38 | };
39 |
40 | // export TuxxStore
41 | module.exports = TuxxStore;
42 |
--------------------------------------------------------------------------------
/src/TuxxStoreMixinGenerator.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //TuxxStoreMixinGenerator FUNCTION: creates an object with the componentDidMount and componentWillUnmount methods that will add and remove the specified event listeners from the provided store
4 | //@param props FUNCTION: returns an OBJECT defining the TuxxStore for which callbacks will be registered. "this" will be bound to the component in this function. [ALTERNATE ARRAY: array of functions with the component bound as "this" that return objects with same keys as listed below]
5 | //expected keys:
6 | // store OBJECT: store object that the event and listener should be attached to
7 | // listener FUNCTION: callback function to be invoked upon associated event [ALTERNATE ARRAY: array of callback functions]
8 | // event STRING: event type to trigger listener callback function [ALTERNATE ARRAY: array of event type strings]
9 | var TuxxStoreMixinGenerator = function (props) {
10 | var storeConnections = {};
11 | if (Array.isArray(props)) {
12 | var propsLength = props.length;
13 |
14 | // add the events and listeners for the particular store to the componentDidMount React life cycle event
15 | storeConnections.componentDidMount = function () {
16 | //slice a copy of the props for this component
17 | var tuxxStoreMixinProps = this.__tuxxStoreMixinProps = props.slice();
18 | for (var i = 0; i < propsLength; i++) {
19 | var prop = tuxxStoreMixinProps[i];
20 | //invoke the passed in function with the component's context and store the returned object back into the array and variable
21 | if (typeof prop === 'function') {
22 | prop = tuxxStoreMixinProps[i] = prop.call(this);
23 | }
24 | // map addChangeListeners, due to the second input argument set to true, to store, listeners, and events passed in
25 | mapListenersAndEventsToStore(prop, true);
26 | }
27 | };
28 |
29 | // remove the events and listeners for the particular store to the componentWillUnmount React life cycle event
30 | storeConnections.componentWillUnmount = function () {
31 | var tuxxStoreMixinProps = this.__tuxxStoreMixinProps;
32 | for (var i = 0; i < propsLength; i++) {
33 | // map removeChangeListeners, due to the second input argument set to false, to store, listeners, and events passed in
34 | mapListenersAndEventsToStore(tuxxStoreMixinProps[i], false);
35 | }
36 | };
37 | } else if (typeof props === 'function') {
38 | storeConnections.componentDidMount = function () {
39 | if (typeof props === 'function') {
40 | //store the function on this component
41 | this.__tuxxStoreMixinProps = props.call(this);
42 | }
43 | mapListenersAndEventsToStore(this.__tuxxStoreMixinProps, true);
44 | };
45 |
46 | storeConnections.componentWillUnmount = function () {
47 | mapListenersAndEventsToStore(this.__tuxxStoreMixinProps, false);
48 | };
49 | }
50 |
51 | return storeConnections;
52 | };
53 |
54 | //mapListenersAndEventsToStore FUNCTION maps adding or removing change listeners from a store
55 | //@param storeConfig OBJECT: contains the store object that the event types and the listener callbacks will be attached to
56 | //expected keys:
57 | // store OBJECT: store object that the event and listener should be attached to
58 | // listener FUNCTION: callback function to be invoked upon associated event [ALTERNATE ARRAY: array of callback functions]
59 | // event STRING: event type to trigger listener callback function [ALTERNATE ARRAY: array of event type strings]
60 | //@param addRemoveListener BOOLEAN: true to addChangeListener, false to removeChangeListener
61 | var mapListenersAndEventsToStore = function (storeConfig, addOrRemoveListener) {
62 | addOrRemoveListener = addOrRemoveListener ? 'addChangeListener' : 'removeChangeListener';
63 |
64 | // get store object
65 | var store = storeConfig.store;
66 | // get listener/s
67 | var listener = storeConfig.listener;
68 | // deterimine if listener is an array of callbacks, if it isn't it will be converted a single element array for ease of looping if event is an array
69 | var listenerIsArray = Array.isArray(listener);
70 | // get event/s
71 | var event = storeConfig.event;
72 | //determine if event is an array of change event types, if it isn't it will be converted to a single element array for ease of looping if listener is an array
73 | var eventIsArray = Array.isArray(event);
74 |
75 | // check to see if either listener or event is an array and if one is the other will be changed to an array
76 | if (listenerIsArray || eventIsArray) {
77 | if (!listenerIsArray) {
78 | listener = [listener];
79 | } else if (!eventIsArray) {
80 | event = [event];
81 | }
82 | // pre-calculate listener and event lengths
83 | var listenerLength = listener.length;
84 | var eventLength = event.length;
85 |
86 | // determine which array is a longer length and set longerLength to that value. This is done if one is larger than the other,
87 | // so that the last element of the shorter array will be paired with the remaining elements of the longer array
88 | var longerLength = listenerLength;
89 | if (listenerLength < eventLength) {
90 | longerLength = eventLength;
91 | }
92 |
93 | // loop through and either add or remove change listeners with the specified event type from the store
94 | for (var i = 0; i < longerLength; i++) {
95 | var currentListener = listener[i] || listener[listenerLength - 1];
96 | var currentEvent = event[i] || event[eventLength - 1];
97 | store[addOrRemoveListener](currentListener, currentEvent);
98 | }
99 | } else {
100 | // if neither the listener or event was an array, the add or remove change listener can be invoked without converting them to arrays
101 | store[addOrRemoveListener](listener, event);
102 | }
103 | };
104 |
105 | // export TuxxStoreMixinGenerator
106 | module.exports = TuxxStoreMixinGenerator;
107 |
--------------------------------------------------------------------------------
/src/__tests__/TuxxActionStore-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var moduleToTest = 'tuxx/src/TuxxActionStore';
4 |
5 | jest.dontMock(moduleToTest);
6 |
7 | describe('TuxxActionStore', function () {
8 | var Actions, TuxxStore, TuxxActionStore, mockMethods, actionStore, mockActionCallbacks, context, mockRegisterFunction;
9 |
10 | beforeEach(function () {
11 | //reset required modules and mocks
12 | Actions = require('tuxx/Actions');
13 | TuxxStore = require('tuxx/Stores').createStore;
14 | TuxxActionStore = require(moduleToTest);
15 |
16 | //mock register method, returns our mockActionCallbacks and sets context for testing 'this'
17 | mockActionCallbacks = {};
18 | mockRegisterFunction = function () {
19 | context = this;
20 | return mockActionCallbacks;
21 | };
22 |
23 | //add mockRegisterFunction to mockMethods at key of register
24 | mockMethods = {
25 | register: mockRegisterFunction
26 | };
27 |
28 | //mock our TuxxStore return
29 | TuxxStore.mockReturnValue({});
30 | //call TuxxActionStore with our mockMethods
31 | actionStore = TuxxActionStore(mockMethods);
32 | });
33 |
34 | it('should invoke the TuxxStore function with the passed in object', function () {
35 | expect(TuxxStore.mock.calls[0][0]).toBe(mockMethods);
36 | });
37 |
38 | it('should invoke tuxx/Actions with the actionStore and the result of the function at the register key. The function should have a context of the actionStore', function () {
39 | //expect Actions.register to be invoked with actionStore, mockActionCallbacks
40 | expect(Actions.register.mock.calls[0][0]).toBe(actionStore);
41 | expect(Actions.register.mock.calls[0][1]).toBe(mockActionCallbacks);
42 | //expect our context to be the actionStore
43 | expect(context).toBe(actionStore);
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/src/__tests__/TuxxAnimations-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | jest.dontMock('tuxx/React');
4 | jest.dontMock('tuxx/Animations');
5 | jest.dontMock('tuxx/src/TuxxAnimations');
6 | jest.dontMock('tuxx/Animations/Fade');
7 | jest.dontMock('tuxx/Animations/Fly');
8 | jest.dontMock('tuxx/Animations/Zoom');
9 | jest.dontMock('tuxx/Animations/Rotate');
10 |
11 | describe('Animations', function () {
12 | var React, createAnimation, Idea, Fade, Fly, Zoom, Rotate, TestUtils, ideaComponent, mocks;
13 |
14 | beforeEach(function () {
15 | //Reset animation components and modules before each test
16 | React = require('tuxx/React');
17 | createAnimation = require('tuxx/Animations').createAnimation;
18 | Idea = require('tuxx/src/__tests__/testComponents');
19 | Fade = require('tuxx/Animations/Fade');
20 | Fly = require('tuxx/Animations/Fly');
21 | Zoom = require('tuxx/Animations/Zoom');
22 | Rotate = require('tuxx/Animations/Rotate');
23 | TransitionGroup = require('tuxx/React/TransitionGroup');
24 |
25 | TestUtils = require('react/lib/ReactTestUtils');
26 | TestUtils.mockComponent(TransitionGroup);
27 | //Reset mocks before each test
28 | mocks = {
29 | ideas: [
30 | {
31 | id: 1,
32 | text: 'Save the world.'
33 | },
34 | {
35 | id: 2,
36 | text: 'More saving of the world.'
37 | }
38 | ]
39 | };
40 | });
41 |
42 | it("applies a class of 'fly' for a fly transition", function () {
43 | ideaFlyComponent = TestUtils.renderIntoDocument(
44 |
45 | );
46 | //Need to use setProps here to pass in the component as a child of the animation component
47 | ideaFlyComponent.setProps({children: });
48 | var ideaFly = TestUtils.findRenderedDOMComponentWithClass(ideaFlyComponent, 'fly');
49 | expect(ideaFly).toBeDefined();
50 | });
51 |
52 | it("applies a class of 'fade' for a fade transition", function () {
53 | ideaFadeComponent = TestUtils.renderIntoDocument(
54 |
55 | );
56 | ideaFadeComponent.setProps({children: });
57 | var ideaFade = TestUtils.findRenderedDOMComponentWithClass(ideaFadeComponent, 'fade');
58 | expect(ideaFade).toBeDefined();
59 | });
60 |
61 | it("applies a class of 'zoom' for a zoom transition", function () {
62 | ideaZoomComponent = TestUtils.renderIntoDocument(
63 |
64 | );
65 | ideaZoomComponent.setProps({children: });
66 | var ideaZoom = TestUtils.findRenderedDOMComponentWithClass(ideaZoomComponent, 'zoom');
67 | expect(ideaZoom).toBeDefined();
68 | });
69 |
70 | it("applies a class of 'rotate' for a rotate transition", function () {
71 | ideaRotateComponent = TestUtils.renderIntoDocument(
72 |
73 | );
74 | ideaRotateComponent.setProps({children: });
75 | var ideaRotate = TestUtils.findRenderedDOMComponentWithClass(ideaRotateComponent, 'rotate');
76 | expect(ideaRotate).toBeDefined();
77 | });
78 |
79 | it("applies a class of 'myFade' for a myFade transition", function () {
80 | var MyFade = createAnimation(Fade, 'myFade');
81 | ideaMyFadeComponent = TestUtils.renderIntoDocument(
82 |
83 | );
84 | ideaMyFadeComponent.setProps({children: });
85 | var ideaMyFade = TestUtils.findRenderedDOMComponentWithClass(ideaMyFadeComponent, 'myFade');
86 | expect(ideaMyFade).toBeDefined();
87 | });
88 | });
89 |
--------------------------------------------------------------------------------
/src/__tests__/TuxxArchitectStores-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var moduleToTest = 'tuxx/src/TuxxArchitectStores';
4 |
5 | jest.dontMock(moduleToTest);
6 |
7 | describe('architect', function () {
8 | var architect, root1Store, branch1Store, branch2Store, leaf11Store, leaf12Store, leaf21Store;
9 |
10 | beforeEach(function () {
11 | //reset architect
12 | architect = require(moduleToTest);
13 | //chain of tuxx stores
14 | /*
15 | [root1]
16 | / \
17 | [branch1] [branch2]
18 | / \ \
19 | [leaf11] [leaf12] [leaf21]
20 | */
21 | //reset stores, need to set __registerId__ for architecting
22 | root1Store = {__registerId__: {}};
23 | branch1Store = {__registerId__: {}};
24 | branch2Store = {__registerId__: {}};
25 | leaf11Store = {__registerId__: {}};
26 | leaf12Store = {__registerId__: {}};
27 | leaf21Store = {__registerId__: {}};
28 | //generate root1Output
29 | architect(root1Store).itOutputs('root1Output');
30 | });
31 |
32 | it('should add an array of __registerId__s to any store that needs what another store outputs at the key of __tuxxArchitecture__', function () {
33 | //create dependency chain described above
34 | architect(branch1Store).itNeeds('root1Output').itOutputs('branch1Output');
35 | architect(branch2Store).itNeeds('root1Output').itOutputs('branch2Output');
36 | architect(leaf11Store).itNeeds('branch1Output');
37 | architect(leaf12Store).itNeeds('branch1Output');
38 | architect(leaf21Store).itNeeds('branch2Output');
39 |
40 | //branch1 and branch2 should be architected to wait for only root1Store
41 | expect(branch1Store.__tuxxArchitecture__[0]).toBe(root1Store.__registerId__);
42 | expect(branch1Store.__tuxxArchitecture__.length).toBe(1);
43 | expect(branch2Store.__tuxxArchitecture__[0]).toBe(root1Store.__registerId__);
44 | expect(branch2Store.__tuxxArchitecture__.length).toBe(1);
45 | //leaf11 and leaf12 should be architected to wait for only branch1
46 | expect(leaf11Store.__tuxxArchitecture__[0]).toBe(branch1Store.__registerId__);
47 | expect(leaf11Store.__tuxxArchitecture__.length).toBe(1);
48 | expect(leaf12Store.__tuxxArchitecture__[0]).toBe(branch1Store.__registerId__);
49 | expect(leaf12Store.__tuxxArchitecture__.length).toBe(1);
50 | //leaf21 should be architected to wait for only branch2
51 | expect(leaf21Store.__tuxxArchitecture__[0]).toBe(branch2Store.__registerId__);
52 | expect(leaf21Store.__tuxxArchitecture__.length).toBe(1);
53 | });
54 |
55 | it('should add the __tuxxArchitecture__ prop to any store that needs another store directly', function () {
56 | //create dependency chain described above
57 | architect(branch1Store).itNeeds(root1Store);
58 | architect(branch2Store).itNeeds(root1Store);
59 | architect(leaf11Store).itNeeds(branch1Store);
60 | architect(leaf12Store).itNeeds(branch1Store);
61 | architect(leaf21Store).itNeeds(branch2Store);
62 |
63 | //branch1 and branch2 should be architected to wait for only root1Store
64 | expect(branch1Store.__tuxxArchitecture__[0]).toBe(root1Store.__registerId__);
65 | expect(branch1Store.__tuxxArchitecture__.length).toBe(1);
66 | expect(branch2Store.__tuxxArchitecture__[0]).toBe(root1Store.__registerId__);
67 | expect(branch2Store.__tuxxArchitecture__.length).toBe(1);
68 | //leaf11 and leaf12 should be architected to wait for only branch1
69 | expect(leaf11Store.__tuxxArchitecture__[0]).toBe(branch1Store.__registerId__);
70 | expect(leaf11Store.__tuxxArchitecture__.length).toBe(1);
71 | expect(leaf12Store.__tuxxArchitecture__[0]).toBe(branch1Store.__registerId__);
72 | expect(leaf12Store.__tuxxArchitecture__.length).toBe(1);
73 | //leaf21 should be architected to wait for only branch2
74 | expect(leaf21Store.__tuxxArchitecture__[0]).toBe(branch2Store.__registerId__);
75 | expect(leaf21Store.__tuxxArchitecture__.length).toBe(1);
76 | });
77 |
78 | it('should add the __tuxxArchitecture__ props when a mix of stores and outputs is used', function () {
79 | //create dependency chain described above
80 | architect(branch1Store).itNeeds(root1Store).itOutputs('branch1Output');
81 | architect(branch2Store).itNeeds(root1Store).itOutputs('branch2Output');
82 | architect(leaf11Store).itNeeds(branch1Store);
83 | architect(leaf12Store).itNeeds('branch1Output');
84 | architect(leaf21Store).itNeeds('branch2Output');
85 |
86 | //branch1 and branch2 should be architected to wait for only root1Store
87 | expect(branch1Store.__tuxxArchitecture__[0]).toBe(root1Store.__registerId__);
88 | expect(branch1Store.__tuxxArchitecture__.length).toBe(1);
89 | expect(branch2Store.__tuxxArchitecture__[0]).toBe(root1Store.__registerId__);
90 | expect(branch2Store.__tuxxArchitecture__.length).toBe(1);
91 | //leaf11 and leaf12 should be architected to wait for only branch1
92 | expect(leaf11Store.__tuxxArchitecture__[0]).toBe(branch1Store.__registerId__);
93 | expect(leaf11Store.__tuxxArchitecture__.length).toBe(1);
94 | expect(leaf12Store.__tuxxArchitecture__[0]).toBe(branch1Store.__registerId__);
95 | expect(leaf12Store.__tuxxArchitecture__.length).toBe(1);
96 | //leaf21 should be architected to wait for only branch2
97 | expect(leaf21Store.__tuxxArchitecture__[0]).toBe(branch2Store.__registerId__);
98 | expect(leaf21Store.__tuxxArchitecture__.length).toBe(1);
99 | });
100 |
101 | it('should allow a store to have multiple outputs', function () {
102 | //create dependency chain wherein all leaves and branch2 need branch1
103 | architect(branch1Store).itOutputs(['branch1Output', 'branch1Output2'], 'branch1Output3').and('branch1Output4');
104 | architect(leaf11Store).itNeeds('branch1Output');
105 | architect(leaf12Store).itNeeds('branch1Output2');
106 | architect(leaf21Store).itNeeds('branch1Output3');
107 | architect(branch2Store).itNeeds('branch1Output4');
108 |
109 | //leaf11, leaf12, leaf21, and branch2 should be architected to wait for only branch1
110 | expect(leaf11Store.__tuxxArchitecture__[0]).toBe(branch1Store.__registerId__);
111 | expect(leaf11Store.__tuxxArchitecture__.length).toBe(1);
112 | expect(leaf12Store.__tuxxArchitecture__[0]).toBe(branch1Store.__registerId__);
113 | expect(leaf12Store.__tuxxArchitecture__.length).toBe(1);
114 | expect(leaf21Store.__tuxxArchitecture__[0]).toBe(branch1Store.__registerId__);
115 | expect(leaf21Store.__tuxxArchitecture__.length).toBe(1);
116 | expect(branch2Store.__tuxxArchitecture__[0]).toBe(branch1Store.__registerId__);
117 | expect(branch2Store.__tuxxArchitecture__.length).toBe(1);
118 | });
119 |
120 | it('should allow a store to need multiple stores and outputs', function () {
121 | //create dependency chain wherein leaf21 needs both branches, leaf11, and the root
122 | architect(branch1Store).itOutputs('branch1Output');
123 | architect(leaf21Store).itNeeds(['branch1Output', branch2Store], leaf11Store).and('root1Output');
124 |
125 | //leaf21 should be architected to wait for root1, branch1, branch2, and leaf11
126 | expect(leaf21Store.__tuxxArchitecture__[0]).toBe(branch1Store.__registerId__);
127 | expect(leaf21Store.__tuxxArchitecture__[1]).toBe(branch2Store.__registerId__);
128 | expect(leaf21Store.__tuxxArchitecture__[2]).toBe(leaf11Store.__registerId__);
129 | expect(leaf21Store.__tuxxArchitecture__[3]).toBe(root1Store.__registerId__);
130 | expect(leaf21Store.__tuxxArchitecture__.length).toBe(4);
131 | });
132 |
133 | it('should throw an error if a store is waiting for an input that no store outputs', function () {
134 | expect(function () {
135 | architect(root1Store).itNeeds('test');
136 | }).toThrow(new Error('Invariant Violation: store is waiting for an input: "test" that no store outputs. If this store needs no inputs than only call "itOutputs" method.'));
137 | });
138 |
139 | it('should throw an error if a store is waiting for a store that does not have a __registerId__', function () {
140 | delete root1Store.__registerId__;
141 | expect(function () {
142 | architect(branch1Store).itNeeds('root1Output');
143 | }).toThrow(new Error('Invariant Violation: store is waiting for a store that has not been registered to any actions.'));
144 | });
145 |
146 | it('should add multiple stores if multiple stores output what the store needs', function () {
147 | //create dependency chain
148 | architect(branch1Store).itOutputs('root2Output');
149 | architect(branch2Store).itOutputs('root2Output');
150 | architect(leaf21Store).itOutputs('root2Output');
151 | architect(leaf11Store).itNeeds('root2Output');
152 |
153 | //leaf11 store should be architected to wait for branch1, branch2, and leaf21
154 | expect(leaf11Store.__tuxxArchitecture__[0]).toBe(branch1Store.__registerId__);
155 | expect(leaf11Store.__tuxxArchitecture__[1]).toBe(branch2Store.__registerId__);
156 | expect(leaf11Store.__tuxxArchitecture__[2]).toBe(leaf21Store.__registerId__);
157 | expect(leaf11Store.__tuxxArchitecture__.length).toBe(3);
158 | });
159 | });
160 |
--------------------------------------------------------------------------------
/src/__tests__/TuxxDeepSearch-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('deepSearch', function () {
4 | var deepSearch, mockObjectToSearch, traitsToFind;
5 |
6 | beforeEach(function () {
7 | //reset mocks and require stateements
8 | deepSearch = require('tuxx/src/TuxxDeepSearch');
9 |
10 | //define complex object to search with matching property names to make sure we find the correct one
11 | mockObjectToSearch = {
12 | id: 1,
13 | subProp: {
14 | id: 2,
15 | subSubProp: {
16 | id: 3,
17 | diffId: 1
18 | }
19 | },
20 | finalProp: {
21 | id: 1
22 | }
23 | };
24 | });
25 |
26 | it('should be able to deeply search an object', function () {
27 | expect(deepSearch('diffId', mockObjectToSearch)).toEqual(['subProp', 'subSubProp', 'diffId']);
28 | });
29 |
30 | it('should find the shallowest key first', function () {
31 | expect(deepSearch('id', mockObjectToSearch)).toEqual(['id']);
32 | });
33 |
34 | it('should accept an array to restrict the search', function () {
35 | expect(deepSearch(['subProp', 'id'], mockObjectToSearch)).toEqual(['subProp', 'id']);
36 | expect(deepSearch(['subSubProp', 'id'], mockObjectToSearch)).toEqual(['subProp', 'subSubProp', 'id']);
37 | expect(deepSearch(['finalProp', 'id'], mockObjectToSearch)).toEqual(['finalProp', 'id']);
38 | });
39 |
40 | it('should throw an error if a key cannot be found', function () {
41 | expect(function () {
42 | deepSearch('unmatchedId', mockObjectToSearch);
43 | }).toThrow(new Error('Invariant Violation: Could not find "unmatchedId" within object'));
44 |
45 | expect(function () {
46 | deepSearch(['subProp', 'wrongId'], mockObjectToSearch);
47 | }).toThrow(new Error('Invariant Violation: Could not find "wrongId" within object'));
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/src/__tests__/TuxxGetOwnerPropsMixin-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var moduleToTest = 'tuxx/src/TuxxGetOwnerPropsMixin';
4 |
5 | jest.dontMock(moduleToTest);
6 |
7 | describe('getOwnerPropsMixin', function () {
8 | var getOwnerPropsMixin, root1Owner, branch1Owner, branch2Ownee, leaf11Ownee, leaf12Owner, leaf21Ownee;
9 |
10 | beforeEach(function () {
11 | //reset getOwnerPropsMixin
12 | getOwnerPropsMixin = require(moduleToTest);
13 | //construct chain of _owner objects tO = tuxxOwner
14 | /*
15 | [root1 tO]
16 | / \
17 | [branch1 tO] [branch2]
18 | / \ \
19 | [leaf11] [leaf12 tO] [leaf21]
20 | */
21 | //reset Owners and Ownees
22 | root1Owner = {
23 | __tuxxIsOwnerComponent__: true,
24 | mockOwnerProps: {},
25 | registerOwnerProps: function () {
26 | return this.mockOwnerProps;
27 | }
28 | };
29 | branch1Owner = {
30 | _owner: root1Owner,
31 | __tuxxIsOwnerComponent__: true,
32 | mockOwnerProps: {},
33 | registerOwnerProps: function () {
34 | return this.mockOwnerProps;
35 | }
36 | };
37 | branch2Ownee = {
38 | _owner: root1Owner
39 | };
40 | leaf11Ownee = {
41 | _owner: branch1Owner
42 | };
43 | leaf12Owner = {
44 | _owner: branch1Owner,
45 | __tuxxIsOwnerComponent__: true,
46 | mockOwnerProps: {},
47 | registerOwnerProps: function () {
48 | return this.mockOwnerProps;
49 | }
50 | };
51 | leaf21Ownee = {
52 | _owner: branch2Ownee
53 | };
54 | });
55 |
56 | it('should properly pass down the ownerProps to nearestOwnerProps from owner to owner and owner to ownee on componentWillMount', function () {
57 | //cascade down componentWillMount calls as functions would be invoked in React
58 | getOwnerPropsMixin.componentWillMount.call(root1Owner);
59 | getOwnerPropsMixin.componentWillMount.call(branch1Owner);
60 | getOwnerPropsMixin.componentWillMount.call(branch2Ownee);
61 | getOwnerPropsMixin.componentWillMount.call(leaf11Ownee);
62 | getOwnerPropsMixin.componentWillMount.call(leaf12Owner);
63 | getOwnerPropsMixin.componentWillMount.call(leaf21Ownee);
64 | //check for props
65 | //branches should each inherit from the root owner
66 | expect(branch1Owner.nearestOwnerProps).toBe(root1Owner.__tuxxOwnerProps__);
67 | expect(branch2Ownee.nearestOwnerProps).toBe(root1Owner.__tuxxOwnerProps__);
68 | //leaves under branch1 owner should inherit from branch1 owner
69 | expect(leaf11Ownee.nearestOwnerProps).toBe(branch1Owner.__tuxxOwnerProps__);
70 | expect(leaf12Owner.nearestOwnerProps).toBe(branch1Owner.__tuxxOwnerProps__);
71 | //leaf under branch2 ownee should inherit from root owner
72 | expect(leaf21Ownee.nearestOwnerProps).toBe(root1Owner.__tuxxOwnerProps__);
73 | });
74 |
75 | it('should be able to get the nearestOwnerProps even if an intermediary component does not have a nearestOwnerProps or __tuxxOwnerProps__ key', function () {
76 | //mount the root1Owner and leaf21Ownee
77 | getOwnerPropsMixin.componentWillMount.call(root1Owner);
78 | getOwnerPropsMixin.componentWillMount.call(leaf21Ownee);
79 | //expect the leaft21Ownee to have the root1Owner __tuxxOwnerProps__ even though branch2Ownee has not been mounted
80 | expect(leaf21Ownee.nearestOwnerProps).toBe(root1Owner.__tuxxOwnerProps__);
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/src/__tests__/TuxxMutableClass-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var moduleToTest = 'tuxx/src/TuxxMutableClass';
4 |
5 | jest.dontMock(moduleToTest);
6 |
7 | describe('TuxxMutableClass', function () {
8 | var owneeClass, createMutableClass, tuxxMutableClass, mockMutableClassProps, mockMixins, mockMutableRenderMixin, mockPureRenderMixin;
9 |
10 | beforeEach(function () {
11 | // Reset TuxxMutableClass and mocks before each test
12 | owneeClass = require('../TuxxOwneeClass');
13 | createMutableClass = require(moduleToTest);
14 | mockMutableRenderMixin = require('../TuxxMutableRenderMixin');
15 | mockPureRenderMixin = require('react/lib/ReactComponentWithPureRenderMixin');
16 | mockMixins = [{}, {}];
17 | mockMutableClassProps = {
18 | someMockProp: {}
19 | };
20 |
21 | // Create for each test
22 | tuxxMutableClass = createMutableClass(mockMutableClassProps);
23 | });
24 |
25 | describe('createMutableClass', function () {
26 | it('should invoke owneeClass with the passed in props', function () {
27 | var someMockProp = owneeClass.mock.calls[0][0].someMockProp;
28 | expect(someMockProp).toBe(mockMutableClassProps.someMockProp);
29 | });
30 |
31 | it('should invoke owneeClass with a copy of the passed in object but not the object itself', function () {
32 | var mutableClassProps = owneeClass.mock.calls[0][0];
33 | expect(mutableClassProps).not.toBe(mockMutableClassProps);
34 | });
35 |
36 | it('should add PureRenderMixin if mutableTraits is not defined', function () {
37 | var pureRenderMixin = owneeClass.mock.calls[0][0].mixins[0];
38 | expect(pureRenderMixin).toBe(mockPureRenderMixin);
39 | });
40 |
41 | it('should add MutableRenderMixin if mutableTraits is defined', function () {
42 | mockMutableClassProps.mutableTraits = {};
43 | tuxxMutableClass = createMutableClass(mockMutableClassProps);
44 | var mutableRenderMixin = owneeClass.mock.calls[1][0].mixins[0];
45 | expect(mutableRenderMixin).toBe(mockMutableRenderMixin);
46 | });
47 |
48 | it('should add any passed in mixins after the mixins it provides', function () {
49 | // Add mock passed in mixins
50 | mockMutableClassProps.mixins = mockMixins;
51 | tuxxMutableClass = createMutableClass(mockMutableClassProps);
52 | var mixins = owneeClass.mock.calls[1][0].mixins;
53 | // It should not be the original mixin array but should have its properties
54 | expect(mixins).not.toBe(mockMixins);
55 | expect(mixins[mixins.length - 2]).toBe(mockMixins[0]);
56 | expect(mixins[mixins.length - 1]).toBe(mockMixins[1]);
57 | });
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/src/__tests__/TuxxMutableRenderMixin-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var moduleToTest = 'tuxx/src/TuxxMutableRenderMixin';
4 |
5 | jest.dontMock(moduleToTest);
6 |
7 | describe('TuxxMutableRenderMixin', function () {
8 | var tuxxMutableRenderMixin, nextProps, nextState, tuxxMutableTraits;
9 |
10 | beforeEach(function () {
11 | tuxxMutableRenderMixin = require(moduleToTest);
12 | tuxxMutableRenderMixin.constructor = {};
13 | tuxxMutableRenderMixin.props = {
14 | message: {
15 | text: 'test text'
16 | },
17 | value: 2
18 | };
19 | tuxxMutableRenderMixin.state = {
20 | editing: false
21 | };
22 | tuxxMutableRenderMixin.mutableTraits = {
23 | props: ['text', 'value'],
24 | state: 'editing'
25 | };
26 | });
27 |
28 | it('should have componentWillMount and shouldComponentUpdate properties', function () {
29 | expect(tuxxMutableRenderMixin.componentWillMount).toBeDefined();
30 | expect(tuxxMutableRenderMixin.shouldComponentUpdate).toBeDefined();
31 | });
32 |
33 | describe('componentWillMount', function () {
34 | it('should add the __tuxxMutableTraits__ prop to the constructor prop', function () {
35 | expect(tuxxMutableRenderMixin.constructor.__tuxxMutableTraits__).toBeUndefined();
36 |
37 | tuxxMutableRenderMixin.componentWillMount();
38 |
39 | expect(tuxxMutableRenderMixin.constructor.__tuxxMutableTraits__).toBeDefined();
40 | });
41 |
42 | it('should add the paths to the mutableTraits specified by strings and arrays', function () {
43 | tuxxMutableRenderMixin.componentWillMount();
44 | tuxxMutableTraits = tuxxMutableRenderMixin.constructor.__tuxxMutableTraits__;
45 |
46 | expect(tuxxMutableTraits.length).toEqual(3);
47 | expect(tuxxMutableTraits[0]).toEqual(['props', 'message', 'text']);
48 | expect(tuxxMutableTraits[1]).toEqual(['props', 'value']);
49 | expect(tuxxMutableTraits[2]).toEqual(['state', 'editing']);
50 | });
51 |
52 | it('should add the paths to the mutableTraits if an array of arrays is specified', function () {
53 | tuxxMutableRenderMixin.mutableTraits.props[0] = ['message', 'text'];
54 | tuxxMutableRenderMixin.componentWillMount();
55 | tuxxMutableTraits = tuxxMutableRenderMixin.constructor.__tuxxMutableTraits__;
56 |
57 | expect(tuxxMutableTraits.length).toEqual(3);
58 | expect(tuxxMutableTraits[0]).toEqual(['props', 'message', 'text']);
59 | expect(tuxxMutableTraits[1]).toEqual(['props', 'value']);
60 | expect(tuxxMutableTraits[2]).toEqual(['state', 'editing']);
61 | });
62 |
63 | it('should not recreate mutable traits paths if the __tuxxMutableTraits__ prop exists ', function () {
64 | // Establish initial __tuxxMutableTraits__ value
65 | tuxxMutableRenderMixin.componentWillMount();
66 | tuxxMutableTraits = tuxxMutableRenderMixin.constructor.__tuxxMutableTraits__;
67 |
68 | // Create mockComponent and assign the value created during the first componentWillMount call
69 | var mockComponent = {};
70 | mockComponent.constructor = {};
71 | mockComponent.constructor.__tuxxMutableTraits__ = tuxxMutableTraits;
72 |
73 | // Call componentWillMount a second time using the mockComponent as the context.
74 | // Expect the value of __tuxxMutableTraits__ to be equal to the value from the first invocation.
75 | tuxxMutableRenderMixin.componentWillMount.call(mockComponent);
76 | expect(mockComponent.constructor.__tuxxMutableTraits__).toBe(tuxxMutableTraits);
77 | });
78 | });
79 |
80 | describe('shouldComponentUpdate', function () {
81 | beforeEach(function () {
82 | nextProps = {
83 | message: {
84 | text: 'test text'
85 | },
86 | value: 2,
87 | updated: false
88 | };
89 | nextState = {
90 | editing: false,
91 | updated: false
92 | };
93 | tuxxMutableRenderMixin.constructor = {
94 | __tuxxMutableTraits__: [['props', 'message', 'text'], ['props', 'value'], ['state', 'editing']]
95 | };
96 | });
97 |
98 | it('should throw and error when __tuxxMutableTraits__ prop is not defined on the component', function () {
99 | tuxxMutableRenderMixin.constructor.__tuxxMutableTraits__ = undefined;
100 | expect(function() {
101 | tuxxMutableRenderMixin.shouldComponentUpdate(nextProps, nextState);
102 | }).toThrow(new Error('Invariant Violation: The __tuxxMutableTraits__ property is not defined on the component.'));
103 | });
104 |
105 | it('should return false when there are no differences between current state and props and next props and state', function () {
106 | expect(tuxxMutableRenderMixin.shouldComponentUpdate(nextProps, nextState)).toBeFalsy();
107 | });
108 |
109 | it('should return true when there is a difference between current props and next props', function () {
110 | nextProps.message.text = 'updated text';
111 | expect(tuxxMutableRenderMixin.shouldComponentUpdate(nextProps, nextState)).toBeTruthy();
112 | });
113 |
114 | it('should return true when there is a difference between current state and next state', function () {
115 | nextState.editing = true;
116 | expect(tuxxMutableRenderMixin.shouldComponentUpdate(nextProps, nextState)).toBeTruthy();
117 | });
118 |
119 | it('should return false if a trait that is not being tracked changes', function () {
120 | nextProps.updated = true;
121 | nextState.updated = true;
122 | expect(tuxxMutableRenderMixin.shouldComponentUpdate(nextProps, nextState)).toBeFalsy();
123 | });
124 | });
125 | });
126 |
--------------------------------------------------------------------------------
/src/__tests__/TuxxOwneeClass-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var moduleToTest = 'tuxx/src/TuxxOwneeClass';
4 |
5 | jest.dontMock(moduleToTest);
6 | jest.mock('react');
7 |
8 | describe('TuxxOwneeClass', function () {
9 | var React, createOwneeClass, tuxxOwneeClass, mockOwneeClassProps, mockMixins, mockStoreMixin, mockGetOwnerPropsMixin, mockPropTypeCheckerMixin;
10 |
11 | beforeEach(function () {
12 | //reset TuxxOwneeClass and mocks before each test
13 | React = require('react');
14 | createOwneeClass = require(moduleToTest);
15 | mockGetOwnerPropsMixin = require('../TuxxGetOwnerPropsMixin');
16 | mockPropTypeCheckerMixin = require('../TuxxPropTypeCheckerMixin');
17 | mockStoreMixin = {};
18 | mockMixins = [{}, {}];
19 | mockOwneeClassProps = {
20 | someMockProp: {}
21 | };
22 |
23 | //create for each test
24 | tuxxOwneeClass = createOwneeClass(mockOwneeClassProps);
25 | });
26 |
27 | describe('createOwneeClass', function () {
28 | it('should invoke React.createClass with the passed in props', function () {
29 | var someMockProp = React.createClass.mock.calls[0][0].someMockProp;
30 | expect(someMockProp).toBe(mockOwneeClassProps.someMockProp);
31 | });
32 |
33 | it('should invoke React.createClass with a copy of the passed in object but not the object itself', function () {
34 | var owneeClassProps = React.createClass.mock.calls[0][0];
35 | expect(owneeClassProps).not.toBe(mockOwneeClassProps);
36 | });
37 |
38 | it('should add the getOwnerPropsMixin', function () {
39 | var getOwnerPropsMixin = React.createClass.mock.calls[0][0].mixins[0];
40 | expect(getOwnerPropsMixin).toBe(mockGetOwnerPropsMixin);
41 | });
42 |
43 | it('should add the propTypeCheckerMixin if nearestOwnerPropTypes key is defined', function () {
44 | mockOwneeClassProps.nearestOwnerPropTypes = {};
45 | createOwneeClass(mockOwneeClassProps);
46 | var propTypeCheckerMixin = React.createClass.mock.calls[1][0].mixins[1];
47 | expect(propTypeCheckerMixin).toBe(mockPropTypeCheckerMixin);
48 | });
49 |
50 | it('should add the propTypeCheckerMixin if anyPropTypes key is defined', function () {
51 | mockOwneeClassProps.anyPropTypes = {};
52 | createOwneeClass(mockOwneeClassProps);
53 | var propTypeCheckerMixin = React.createClass.mock.calls[1][0].mixins[1];
54 | expect(propTypeCheckerMixin).toBe(mockPropTypeCheckerMixin);
55 | });
56 |
57 | it('should add any passed in mixins after the mixins it provides', function () {
58 | //add mock passed in mixins
59 | mockOwneeClassProps.mixins = mockMixins;
60 | tuxxOwneeClass = createOwneeClass(mockOwneeClassProps);
61 | var mixins = React.createClass.mock.calls[1][0].mixins;
62 | //it should not be the original mixin array but should have its properties
63 | expect(mixins).not.toBe(mockMixins);
64 | expect(mixins[mixins.length - 2]).toBe(mockMixins[0]);
65 | expect(mixins[mixins.length - 1]).toBe(mockMixins[1]);
66 | });
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/src/__tests__/TuxxOwnerClass-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var moduleToTest = 'tuxx/src/TuxxOwnerClass';
4 |
5 | jest.dontMock(moduleToTest);
6 |
7 | describe('TuxxOwnerClass', function () {
8 | var owneeClass, createOwnerClass, tuxxOwnerClass, mockOwnerClassProps, mockMixins, mockConnectOwnerToStore, mockStoreMixinGenerator, mockStoreMixin;
9 |
10 | beforeEach(function () {
11 | //reset TuxxOwnerClass and mocks before each test
12 | owneeClass = require('../TuxxOwneeClass');
13 | createOwnerClass = require(moduleToTest);
14 | mockStoreMixinGenerator = require('../TuxxStoreMixinGenerator');
15 | mockStoreMixin = {};
16 | mockMixins = [{}, {}];
17 | mockConnectOwnerToStore = {};
18 | mockOwnerClassProps = {
19 | someMockProp: {}
20 | };
21 |
22 | //create for each test
23 | tuxxOwnerClass = createOwnerClass(mockOwnerClassProps);
24 | });
25 |
26 | describe('createOwnerClass', function () {
27 | it('should invoke owneeClass with the passed in props', function () {
28 | var someMockProp = owneeClass.mock.calls[0][0].someMockProp;
29 | expect(someMockProp).toBe(mockOwnerClassProps.someMockProp);
30 | });
31 |
32 | it('should invoke owneeClass with a copy of the passed in object but not the object itself', function () {
33 | var ownerClassProps = owneeClass.mock.calls[0][0];
34 | expect(ownerClassProps).not.toBe(mockOwnerClassProps);
35 | });
36 |
37 | it('should attach the __tuxxIsOwnerComponent__ prop', function () {
38 | var __tuxxIsOwnerComponent__ = owneeClass.mock.calls[0][0].__tuxxIsOwnerComponent__;
39 | expect(__tuxxIsOwnerComponent__).toBeTruthy();
40 | });
41 |
42 | it('should invoke storeMixinGenerator with the connectOwnerToStore key', function () {
43 | //mock out the store mixin generator return value
44 | mockStoreMixinGenerator.mockReturnValueOnce(mockStoreMixin);
45 | //add the mock connect owner to store property
46 | mockOwnerClassProps.connectOwnerToStore = mockConnectOwnerToStore;
47 | tuxxOwnerClass = createOwnerClass(mockOwnerClassProps);
48 | //expect the mock for store mixin generator to have been called and expect the result to have been mixed in to the return React class
49 | expect(mockStoreMixinGenerator.mock.calls[0][0]).toBe(mockConnectOwnerToStore);
50 | var mixins = owneeClass.mock.calls[1][0].mixins;
51 | expect(mixins[0]).toBe(mockStoreMixin);
52 | });
53 |
54 | it('should not invoke storeMixinGenerator if the key connectOwnerToStore is not passed in', function () {
55 | expect(mockStoreMixinGenerator).not.toBeCalled();
56 | });
57 |
58 | it('should add any passed in mixins after the mixins it provides', function () {
59 | //add the mock connect owner to store property
60 | mockOwnerClassProps.connectOwnerToStore = mockConnectOwnerToStore;
61 | //add mock passed in mixins
62 | mockOwnerClassProps.mixins = mockMixins;
63 | tuxxOwnerClass = createOwnerClass(mockOwnerClassProps);
64 | var mixins = owneeClass.mock.calls[1][0].mixins;
65 | //it should not be the original mixin array but should have its properties
66 | expect(mixins).not.toBe(mockMixins);
67 | expect(mixins[mixins.length - 2]).toBe(mockMixins[0]);
68 | expect(mixins[mixins.length - 1]).toBe(mockMixins[1]);
69 | });
70 | });
71 | });
72 |
--------------------------------------------------------------------------------
/src/__tests__/TuxxPropTypeCheckerMixin-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var moduleToTest = 'tuxx/src/TuxxPropTypeCheckerMixin';
4 |
5 | jest.dontMock(moduleToTest);
6 |
7 | describe('propTypeCheckerMixin', function () {
8 | var propTypeCheckerMixin, mockComponent;
9 | beforeEach(function () {
10 | propTypeCheckerMixin = require(moduleToTest);
11 | mockComponent = {
12 | //provide mock function to be invoked on componentWillMount
13 | _checkPropTypes: jest.genMockFn()
14 | };
15 | });
16 |
17 | it('should submit component.nearestOwnerPropTypes and nearestOwnerProps to _checkPropTypes on componentWillMount if nearestOwnerPropTypes is defined on the component', function () {
18 | //define nearestOwnerPropTypes on the component
19 | mockComponent.nearestOwnerPropTypes = {};
20 | //define nearestOwnerProps on the component
21 | mockComponent.nearestOwnerProps = {};
22 | //invoke componentWillMount passing in the mockComponent as the context
23 | propTypeCheckerMixin.componentWillMount.call(mockComponent);
24 | //check inputs to _checkPropTypes
25 | var checkPropTypesCall = mockComponent._checkPropTypes.mock.calls[0];
26 | expect(checkPropTypesCall[0]).toBe(mockComponent.nearestOwnerPropTypes);
27 | expect(checkPropTypesCall[1]).toBe(mockComponent.nearestOwnerProps);
28 | expect(checkPropTypesCall[2]).toBe('nearestOwnerProps');
29 | });
30 |
31 | it('should submit component.anyPropTypes and nearestOwnerProps extended with props to _checkPropTypes on componentWillMount if anyPropTypes is defined on the component', function () {
32 | //define anyPropTypes on the component
33 | mockComponent.anyPropTypes = {};
34 | //define nearestOwnerProps and props that can be merged together
35 | mockComponent.nearestOwnerProps = {
36 | prop1: {},
37 | prop2: {}
38 | };
39 | mockComponent.props = {
40 | //define prop2 on props as well to test that props is overwriting nearestOwnerProps
41 | prop2: {}
42 | };
43 | //invoke componentWillMount passing in the mockComponent as the context
44 | propTypeCheckerMixin.componentWillMount.call(mockComponent);
45 | //check inputs to _checkPropTypes
46 | var checkPropTypesCall = mockComponent._checkPropTypes.mock.calls[0];
47 | expect(checkPropTypesCall[0]).toBe(mockComponent.anyPropTypes);
48 | expect(checkPropTypesCall[1].prop1).toBe(mockComponent.nearestOwnerProps.prop1);
49 | //props should overwrite nearestOwnerProps
50 | expect(checkPropTypesCall[1].prop2).toBe(mockComponent.props.prop2);
51 | expect(checkPropTypesCall[2]).toBe('props or nearestOwnerProps');
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/src/__tests__/TuxxStore-test.js:
--------------------------------------------------------------------------------
1 | jest.dontMock('tuxx/src/TuxxStore');
2 |
3 | describe('TuxxStore', function () {
4 | var TuxxStore, methods, newStore, emitChange, callbackFunc, CHANGE_EVENT, NEW_CHANGE_EVENT;
5 |
6 | describe('object returned by invocation', function () {
7 |
8 | describe('with no input arguments', function () {
9 | beforeEach(function () {
10 | TuxxStore = require('../TuxxStore')();
11 | });
12 |
13 | it('should have emitChange, addChangeListener, and removeChangeListener methods', function () {
14 | expect(TuxxStore.emitChange).toBeDefined();
15 | expect(TuxxStore.addChangeListener).toBeDefined();
16 | expect(TuxxStore.removeChangeListener).toBeDefined();
17 | });
18 | });
19 |
20 | describe('with input methods object', function () {
21 | beforeEach(function () {
22 | TuxxStore = require('../TuxxStore');
23 | methods = {};
24 | });
25 |
26 | it('should add user provided key value pairs to returned object', function () {
27 | methods.destroy = function (id) {
28 | delete this._messages[id];
29 | };
30 |
31 | newStore = TuxxStore(methods);
32 | expect(newStore.destroy).toBe(methods.destroy);
33 | });
34 |
35 | it('should overwrite original methods that have the same name with input methods', function () {
36 | emitChange = function (CHANGE_EVENT) {
37 | CHANGE_EVENT = CHANGE_EVENT || 'CHANGE';
38 | this.emit(CHANGE_EVENT);
39 | };
40 |
41 | methods.emitChange = function (different) {
42 | console.log('Should be different now!');
43 | };
44 |
45 | newStore = TuxxStore(methods);
46 | expect(newStore.emitChange).not.toBe(emitChange);
47 | expect(newStore.emitChange).toBe(methods.emitChange);
48 | });
49 | });
50 |
51 | describe('emitChange, addChangeListener, and removeChangeListener methods', function () {
52 | beforeEach(function () {
53 | TuxxStore = require('../TuxxStore')();
54 | CHANGE_EVENT = 'CHANGE';
55 | NEW_CHANGE_EVENT = 'NEW_CHANGE';
56 | callbackFunc = function () {
57 | console.log('Test callback function');
58 | };
59 | });
60 |
61 | describe('emitChange', function () {
62 | it('should call emit with the default CHANGE_EVENT input', function () {
63 | TuxxStore.emitChange();
64 | expect(TuxxStore.emit).toBeCalledWith(CHANGE_EVENT);
65 | });
66 |
67 | it('should call emit with the provided CHANGE_EVENT input', function () {
68 | TuxxStore.emitChange(NEW_CHANGE_EVENT);
69 | expect(TuxxStore.emit).toBeCalledWith(NEW_CHANGE_EVENT);
70 | });
71 | });
72 |
73 | describe('addChangeListener', function () {
74 | it('should call on with default CHANGE_EVENT input and provided callback', function () {
75 | TuxxStore.addChangeListener(callbackFunc);
76 | expect(TuxxStore.on).toBeCalledWith(CHANGE_EVENT, jasmine.any(Function));
77 | });
78 |
79 | it('should call on with provided CHANGE_EVENT and provided callback', function () {
80 | TuxxStore.addChangeListener(callbackFunc, NEW_CHANGE_EVENT);
81 | expect(TuxxStore.on).toBeCalledWith(NEW_CHANGE_EVENT, jasmine.any(Function));
82 | });
83 | });
84 |
85 | describe('removeChangeListener', function () {
86 | it('should call removeListener with default CHANGE_EVENT and provided callback', function () {
87 | TuxxStore.removeChangeListener(callbackFunc);
88 | expect(TuxxStore.removeListener).toBeCalledWith(CHANGE_EVENT, jasmine.any(Function));
89 | });
90 |
91 | it('should call removeListener with provided CHANGE_EVENT and provided callback', function () {
92 | TuxxStore.removeChangeListener(callbackFunc, NEW_CHANGE_EVENT);
93 | expect(TuxxStore.removeListener).toBeCalledWith(NEW_CHANGE_EVENT, jasmine.any(Function));
94 | });
95 | });
96 | });
97 | });
98 | });
99 |
--------------------------------------------------------------------------------
/src/__tests__/testComponents.js:
--------------------------------------------------------------------------------
1 | var React = require('tuxx/React');
2 |
3 | //Creates a simple component to be used for testing
4 | var Idea = React.createClass({
5 | render: function() {
6 | return (
7 |