Box has migrated using react, webpack, and the latest version of ECMAScript for our frontend projects as of 2018. We no longer support changes, pull requests, or upgrades to this package. We appreciate all of the user contributions that you have given us over the past few years.
T3 JavaScript Framework
T3 is a client-side JavaScript framework for building large-scale web applications. Its design is based on the principles of Scalable JavaScript Application Architecture, specifically:
Enforcing loose coupling between components
Making dependencies explicit
Providing extension points to allow for unforeseen requirements
Abstracting away common pain points
Encouraging progressive enhancement
The approaches taken in T3 have been battle-hardened through continuous production use at Box since 2013, where we use T3 along with jQuery, jQuery UI, and several other third-party libraries and frameworks.
Framework Design
T3 is different from most JavaScript frameworks. It's meant to be a small piece of an overall architecture that allows you to build scalable client-side code.
No MVC Here
T3 is explicitly not an MVC framework. It's a framework that allows the creation of loosely-coupled components while letting you decide what other pieces you need for your web application. You can use T3 with other frameworks like Backbone or React, or you can use T3 by itself. If you decide you want model and views, in whatever form, you can still use them with T3.
Unopinionated by Design
T3 is made to be unopinionated while prescribing how some problems might be solved. Our goal here is not to create a single framework that can do everything for you, but rather, to provide some structure to your client-side code that allows you to make good choices. Then, you can add in other libraries and frameworks to suit your needs.
Three Component Types
T3 allows you to define functionality using just three component types:
Services are utility libraries that provide additional capabilities to your application. You can think of services as tools in a toolbox that you use to build an application. They intended to be reusable pieces of code such as cookie parsing, Ajax communication, string utilities, and so on.
Modules represent a particular DOM element on a page and manage the interaction inside of that element. It's a module's job to respond to user interaction within its boundaries. Your application is made up of a series of modules. Modules may not interact directly with other modules, but may do so indirectly.
Behaviors are mixins for modules and are used primarily to allow shared declarative event handling for modules without duplicating code. If, for instance, you use a particular attribute to indicate a link should use Ajax navigation instead of full-page navigation, you can share that functionality amongst multiple modules.
We've found that by using a combination of these three component types, we're able to create compelling, progressively-enhanced user experiences.
T3 2.0.0 was released on November 18th, 2015 with some major changes. To upgrade, please see these instructions.
Getting Started
Your T3 front-end is made up of modules, so the first step is to indicate which modules are responsible for which parts of the page. You can do that by using the data-module attribute and specifying the module ID, such as:
This example specifies the module header should manage this particular part of the page. The module header is then defined as:
Box.Application.addModule('header',function(context){return{onclick: function(event,element,elementType){if(elementType==='welcome-btn'){alert('Welcome, T3 user!');}else{alert('You clicked outside the button.');}}};});
This is a very simple module that has an onclick handler. T3 automatically wires up specified event handlers so you don't have to worry about using event delegation or removing event handlers when they are no longer needed. The onclick handler receives a DOM-normalized event object that can be used to get event details. When the button is clicked, a message is displayed. Additionally, clicking anywhere inside the module will display a different message. Event handlers are tied to the entire module area, so there's no need to attach multiple handlers of the same type.
The last step is to initialize the T3 application:
Box.Application.init();
This call starts all modules on the page (be sure to include both the T3 library and your module code before calling init()). We recommend calling init() as soon as possible after your JavaScript is loaded. Whether you do that onload, earlier, or later, is completely up to you.
There are more extensive tutorials and examples on our website.
Browser Support
T3 is tested and known to work in the following browsers:
Internet Explorer 9 and higher
Firefox (latest version)
Chrome (latest version)
Safari (latest version)
With the exception of Internet Explorer, T3 will continue to support the current and previous one version of all major browsers.
Contributing
The main purpose of sharing T3 is to continue its development, making it faster, more efficient, and easier to use.
Directory Structure
config - configuration files for the project
dist - browser bundles (this directory is updated automatically with each release)
lib - the source code as individual files
tests - the test code
Prerequisites
In order to get started contributing to T3, you'll need to be familiar and have installed:
Following the instructions in the contributor guidelines to setup a local copy of the T3 repository.
Once you clone the T3 git repository, run the following inside the t3js directory:
$ npm i
This sets up all the dependencies that the T3 build system needs to function.
Note: You'll need to do this periodically when pulling the latest code from our repository as dependencies might change. If you find unexpected errors, be sure to run npm i again to ensure your dependencies are up-to-date.
Running Tests
After that, you can run all tests by running:
$ npm test
This will start by linting the code and then running all unit tests.
Build Commands
The following build commands are available:
npm test - runs all linting and uni tests
npm run lint - runs all linting
npm run dist - creates the browser bundles and places them in /dist
Frequently Asked Questions
Can I use this with older browsers?
We provide a custom version of T3 built with jQuery that is compatible with older browsers such as IE8.
Need to contact us directly? Email [email protected] with your questions or comments.
Copyright and License
Copyright 2015 Box, Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
A few people have commented that since we use jQuery just for event handling, then it's possible to extract that out so jQuery is no longer a hard dependency. This is a great idea!
In the discussions within our team, we talked a bit about the preferred approach to doing so. #17 was proposed, but the general feeling was that we don't want to bake jQuery detection into the core of T3. So, we'd like to consider another option where we created something like this:
Recently we discovered a ~6mo old bug in our system that was able to persist because T3 silently allowed a features flip service that didn't exist to be retrieved and used as a falsey value in subsequent logic. This could have been identified if T3 had thrown upon attempting to fetch the service, or at least dumped to conosle.warn when in debug mode to at least let a developer know they're attempting to access a service that does not exist.
For code cleanliness console statements can then later be stripped out during our build process with the drop_console flag when the code is minified for production use.
I love ReactJS but it basically only renders View and I still need the other things like event handling, etc. It seems T3 has very simple and elegant idea. I tried a bit tonight with T3 + ReactJS. One thing I did notice is that when I run Box.Application.init, it expects that all the elements with data-module attributes are already added to Box.Application.
It seems I've found that componentWillMount is a good place to write Box.Application.addModule and in componentDidMount, you can run Box.Application.init.
I attached my code below. Do you think this is the proper way to use T3 and ReactJS? Thank you!
Address issues raised in #12, especially strong jQuery dependency.
Box team might have bigger fish to fry, so open a pull request here to signal someone is working on it.
Now it works, I end up doing following changes:
Abstract event system into addListener and removeListener, still prefer jQuery over native add/removeEventListener, but allow seamless fallback.
Update karma config and use mocha reporter instead of progress, this is good because progress does not show which test fails when you do sandbox.verifyAndRestore in afterEach; though generally one should avoid assert (sandbox.verify is implied assert) in afterEach because it cause mocha to abort all subsequent tests.
Update test cases so that it no longer depends on jQuery in global space, by jQuery.noConflict at the start. Note that we still test for jQuery global, see tests for on[event]()
Update travis to include node v0.12 and latest iojs
Hi guys! I miss working with you, but I'm still using T3 on a daily basis with my clients!! :)
I'm wondering if there is a way you can share some of your JSDoc configurations for Modules, Services, and Behaviors. I'm having a really hard time trying to setup the proper tags so that all of my modules, services, and behaviors get grouped together in the documentation.
Can you offer any guidance for tools, configs, and jsdoc tags that would allow me to have namespaces for "Modules", "Services", and "Behaviors", and then have those contain all of the classes of those types?
I think that may be more effective to check on circular dependencies only during instantiation of service. When an instance of service already exists we should immediately return it.
I understand Box.com might be using jQuery features extensively, but if T3's idea is to stay modular, then why not decouple jQuery as well.
Looking at the documents (apologize if I missed something), it seems jQuery would be used for data-binding, but there can be lots of approach to data-binding, not to mention virtual-dom approach (where binding is handled differently)
Seems like T3 can be done without jQuery, or at least with alternatives like DOMtastic.
Either way, I am just very excited to see a framework that takes progressive enhancement into account and I am very interested in using it for my current project.
Currently when a module is missing in debug mode an exception is thrown. That's all well and good, but it prevents T3 from continuing its initialization. We have a situation where multiple engineers will be working on one environment and sometimes they use a proxy to make a local version of their script to the page which may not have a module that the dom for that environment has listed. This is fine in non-debug mode, but when debug is enabled it completely borks the process.
Granted I realize our development process at the moment may be more at fault then how T3 functions, but it would be nice to be able to see the error but not have T3 stop the initialization process when that error occurs.
Something like that, right? Well, maybe instead of a switch an if else statements is used. Regardless, that breakdown is up to the individual who implements the module/behavior/service in question.
I pulled this straight from the docs:
Don't include application logic in the event handler. Everything the module is capable of doing should be represented as a method on the object and the event handler should call out to those methods.
This interface doesn't naturally support that. On the contrary, I feel like it encourages the bad practice of handling all cases directly inside that method.
I'm proposing an improvement to message/event handling where an object can be passed to on* as an alternative to passing a function.
This interface ensures that there's a function that handles every message instead of a single function that handles all messages. There's no noisy if or switch statements to deal with. The other thing this setup makes me think is, now that it's known what messages are being handled by the T3 construct, it's not necessary to specify messages at all because that'd just be redundant. T3 can just scrape the messages off the onmessage object.
My current proposal isn't to get rid of messages or the fact that onmessage or onclick can be a function. It's really just the addition for something like onmessage to be an object. Though, I will say that given the next "major" release, which might be a while from now, it would be good to consider the former points.
Thoughts?
opened by zephraph 8
Releases(v2.7.0)
v2.7.0(Aug 26, 2016)
Issue #159: Added setErrorHandler to override default error handling (Rey)
⚛ A comprehensive framework for server-side applications. Being built on top of TypeScript, it surprisingly supports native Web APIs. It can also be used for REST/GraphQL and many others.