From 7236c3bd924a5cb23f7d47488560fe29a368cb39 Mon Sep 17 00:00:00 2001 From: Damir Brekalo Date: Sun, 28 Oct 2018 14:43:11 +0100 Subject: [PATCH] Update documentation files --- documentation/about.md | 16 +- documentation/core-concepts-and-api.md | 419 ++++++------------------- documentation/editing-resource.md | 50 +++ documentation/getting-started.md | 5 +- documentation/listing-resource.md | 190 +++++++++++ 5 files changed, 340 insertions(+), 340 deletions(-) create mode 100644 documentation/editing-resource.md create mode 100644 documentation/listing-resource.md diff --git a/documentation/about.md b/documentation/about.md index be4c5cae..62908e08 100644 --- a/documentation/about.md +++ b/documentation/about.md @@ -1,22 +1,22 @@ # About Trikoder Trim is user interface framework for building headless content management systems that connect to JSON API powered backend. -Craft responsive single page applications that work on all devices. -Content management systems built on top of Trikoder Trim are decoupled from server side technology stack. -UI framework works nicely with any server side technology that can process and render json api dataset compliant with [json:api specification](http://jsonapi.org/). +Content management systems built with Trikoder Trim are decoupled from server side technology stack. +Any server side technology that can process and render json api dataset compliant with [json:api specification](http://jsonapi.org/) will work with UI framework. -Trim enables you to quickly build administration CRUD (create, read, update, delete) interface for your application resources. Resulting CMS is responsive and fast - all styles and behavior for standard use cases come included - programmers job is only to define how each application resource is listed and edited. +Trim enables you to quickly build administration CRUD (create, read, update, delete) interface for your application resources. Resulting CMS is responsive and fast single page application. All styles and behavior for standard use cases come included - programmers job is only to define how each application resource is listed and edited. -Sensible dependency on standardized backend api enables us to create CMS domain specific language or api in javascript that is pretty much decoupled from JS libraries and frameworks that are used underneath. Any capable programmer should be able to define complete interface for resource in need of administration. +Sensible dependency on standardized backend api enables us to use CMS domain specific api in javascript that is pretty much decoupled from libraries and frameworks used underneath. Any capable programmer should be able to define complete interface for resource in need of administration. -## Technology and tooling +## Technology overview Trikoder Trim is built on following open source stack: * [Vue](https://vuejs.org/), [Vue Router](https://router.vuejs.org/) and [Vuex](https://vuex.vuejs.org/) are used for application views, routing and store management. * [Axios](https://github.com/axios/axios) is used as http client * [JSON api resource](https://dbrekalo.github.io/json-api-resource/) is used for querying and persisting resources * [Webpack](https://webpack.js.org/) is used for module bundling and code splitting +* [Karma](https://karma-runner.github.io/) is used as test runner and [Mocha](https://mochajs.org/) as test framework. ## Code sneek peek Lets assume your application has a simple "tag" resource and backend api for this resource is ready. @@ -66,6 +66,6 @@ export default { ``` ## Demo application -Visit [demo application](https://trikoder.github.io/trim/demo/index.html) to get a feeling how CMS built with Trikoder CMF looks and behaves. Is is completely safe to browse, edit and delete items - backend api on demo pages is running on client json api server that stores data in browser memory - so no harm can be done. Dataset can be reset by clicking "reset demo data" control in lower left corner of administration UI. Examine how everything is composed in [demo codebase](https://github.com/trikoder/trim/tree/master/demo). +Visit [demo application](https://trikoder.github.io/trim/demo/index.html) to get a feeling how CMS built with Trikoder CMF looks and behaves. Is is completely safe to browse, edit and delete items - backend api on demo pages is running on client json api server that stores data in browser memory - so no harm can be done. Dataset can be reset by clicking "reset demo data" control in lower left corner of administration UI. Examine how everything is coded in [demo codebase](https://github.com/trikoder/trim/tree/master/demo). -Feel free to browse, cut and paste from demo codebase for your CMS needs and use it as reference. +Feel free to browse, cut and paste from demo codebase and / or use it as reference. diff --git a/documentation/core-concepts-and-api.md b/documentation/core-concepts-and-api.md index 8592452c..ff16d49e 100644 --- a/documentation/core-concepts-and-api.md +++ b/documentation/core-concepts-and-api.md @@ -1,9 +1,9 @@ # Core concepts and api Understanding of how core components work is essential for building applications with Trikoder Trim. -This chapter provides insight into resource controller, resource list, resource edit, router, navigation, service container and application object. +This chapter provides insight into resource controller, router, navigation, service container and application object. ## Resource controller -Resource controller component is a central place to define user interface for given resource. +Resource controller is a central place to define user interface and logic for given resource. Here we define how resource is browsed, filtered and sorted in list, what form fields are rendered when resource is created or updated. In most use cases controller looks like a simple configuration file. @@ -27,280 +27,32 @@ export default { }; ``` +Learn how [resource is listed](listing-resource.html) and [edited](editing-resource.html). -A list of controller properties and methods is examined bellow: - - -### resourceName -Controller property where we name the resource handled (tag, article, page...) -```js -resourceName: 'tag' -``` - - -### resourceCaption -Controller property where we define resource caption mapping, default value is null. It is currently used only in mass actions component as a value for 'mapSelectedCaptionsTo' (if it is null, then it fallbacks to 'id') so it's recommended to always set resource caption value. -```js -resourceCaption: 'title' -``` - - -### createRequiresDraft -Property where we define if draft resource is needed when resource is created. -When createRequiresDraft is set to true controller will save empty resource object before creating interface is displayed. -Saved draft resource will recieve id and be able to support related objects. False is default value. -```js -createRequiresDraft: false -``` - - -### setupList -Controller method where we define how resource is browsed, filtered and sorted in list view. -Controller injects current [listHandler](#resource-list) instance when method is called. - -```js -setupList: function({list}) { - - this.addCreateControl('Create new tag'); - - list.addFilter('TextFormElement', { - name: 'title', - label: 'Title' - }); - - list.addItem('TextListItem', { - caption: 'ID', - mapTo: 'id' - }); - - list.addItem('LinkListItem', { - caption: 'Title', - mapTo: 'title', - action: 'editItem' - }); -}, -``` - -### setupEdit -Controller method where we define form fields rendered when resource is created or updated. -Controller injects current [editHandler](#resource-edit) instance when method is called, method (edit or create) and resource model when called in editing context. - -```js -setupEdit: function({edit, method, resourceModel}) { - - this.addToIndexControl().addSaveControl(); - - edit.addField('TextFormElement', { - label: 'Title', - name: 'title' - }); - -} -``` - -### openIndex -Controller instance method which opens resource index or listing. - -```js -controller.openIndex(queryParams); -``` - -### openEdit -Controller instance method which opens resource editing. - -```js -controller.openEdit(routeParams, queryParams); -``` - -### openCreate -Controller instance method which opens resource create. - -```js -controller.openCreate(queryParams); -``` - -### addCreateControl -Controller instance method which adds resource create control. - -```js -controller.addCreateControl(caption); -``` - -### addSaveControl -Controller instance method which adds resource save control. - -```js -controller.addSaveControl(caption); -``` - -### addToIndexControl -Controller instance method which adds resource list control. - -```js -controller.addToIndexControl(); -``` - -### addControl -Controller instance method which adds generic resource control. - -```js -controller.addControl({ - caption: 'myControl', - url: this.getCreateUrl(), - className: 'accented iconPlus', - action: this.openCreate -}); -``` - -### addDropdownControl -Controller instance method which adds generic dropdown resource control. - -```js -controller.addDropdownControl(params); -``` - - -## Resource list -Resource list is component responsible for handling resource browsing, filtering and sorting. -Examine [list elements](/list-elements.html) chapter to find out how each list element is configured. - - -### addItem -Method for defining what elements are displayed when resource is listed: - -```js -list.addItem('TextListItem', { - caption: 'Title', - mapTo: 'title' -}); -``` - -### addFilter -Method for adding filter form elements when resource is listed: - -```js -list.addFilter('TextFormElement', { - name: 'title', - label: 'Title' -}); -``` - -### addSort -Method for adding sort options on resource list: - -```js -list.addSort([ - { - label: 'By title', - field: '-title' - }, - { - label: 'By date', - field: '-date' - } -]); -``` - -### setTemplate -Method for choosing which template is used for resource listing ('table' and 'cards' are currently supported). - -```js -list.setTemplate('cards'); -``` - -### addMassAction -Method for adding mass actions to resource list. - -```js -list.addMassAction([ - { - caption: 'Publish', - updateAttributes: {published: true} - } -]); -``` - -## Resource edit -Resource edit is component responsible for handling how resource is created or updated. - - -### addField -Method for defining what form elements are mapped to resource attributes and relations when resource is edited or created. -Examine [form elements](/form-elements.html) chapter to find out how each form element is configured. - -```js -edit.addField('TextFormElement', { - label: 'Title', - name: 'title' -}); -``` - -### configureLayout -Method for configuring edit and create layout. Supports tabs, regions and groups (all items are auto generated from layout reference). - -```js -// assign form element to layout region (main and side regions are supported) position with layoutReference -edit.addField(TextFormElement, { - label: 'Title', - name: 'title', - layoutReference: 'mainRegion' -}); - -edit.addField(TextFormElement, { - label: 'Meta data', - name: 'meta', - layoutReference: 'sideRegion' -}); -``` -Altering layout configuration to set tab captions: - -```js -// configure tab layout caption -edit.configureLayout({ - 'mainTab.caption': 'Content and settings', - 'seoTab.caption': 'SEO and meta data' -}); - -// assign form element to layout position with layoutReference -editHandler.addField(TextFormElement, { - label: 'Title', - name: 'title', - layoutReference: 'mainTab.mainRegion' -}); -``` ## Services Application utilizes simple service container to register and locate components and services. -All form and list components are registered and retrieved from service container by default. +Form and list elements are usually registered to and retrieved from service container. -Your resource controllers should also be registered as services. -We encourage you to do so with dynamic import to utilize webpack code splitting and load controller code only when it is requested. +Your controllers should also be registered as services. +We encourage you to do so with dynamic import to utilize webpack code splitting and load controller codebase only when it is requested. -A typical service container with navigation and few registered controllers looks something like this: +A typical file with container services looks something like this: ```js +// services.js export default { - MainNavigation: () => import('./controllers/mainNavigation'), PageController: () => import('./controllers/page'), TagController: () => import('./controllers/tag') }; ``` -If you need to access service container manually somewhere in your code: -```js -import serviceContainer from 'cmf/js/library/serviceContainer'; - -serviceContainer.register('myService', () => {foo: 'bar'}); - -serviceContainer.get('myService').then(myService => { - console.log(myService.foo); // outputs bar -}); -``` ## Router -Application utilizes extended [VueJS router](https://router.vuejs.org/) for linking and routing needs. +Application utilizes extended [VueJS router](https://router.vuejs.org/) for routing and linking needs. Your application routes typically look something like this: ```js +// routes.js export default router => { router.controller('/', 'dashboard', 'Page@index'); @@ -312,24 +64,25 @@ export default router => { }; ``` -### Router.controller -Method for defining routes with controller components. +### Controller routes +Use 'controller' router method to define routes with controller components: ```js router.controller(path, routeName, controllerString); -// ... is somewhat equivalent to... +// ... is somewhat equivalent to following vue api... this.addRoutes([{ path: '/' + path, name: routeName, component: AdminDefault, props: => { - // set controller from service registry as child component of Default Admin component - // if method is defined it will be called after component is created + // set controller from service registry as child + // component of AdminDefault component. if method + // is defined it will be called after component is created } }]); ``` -### Router.resource -Router resource method is a shortcut for defining 3 most used routes for resource: +### Resource routes +Use 'resource' router method as a shortcut for defining 3 most used routes for resource: ```js router.resource('tag'); @@ -348,12 +101,10 @@ router.url('resource.tag.edit', {id: 2}, {foo: 3}) // /tag/2?foo=3; ``` ## Navigation -Navigation component is used to define main user navigation UI element. -Navigation links, user panel links, application name and current username are all defined in navigation component. - +Navigation links and UI elements are defined in navigation component. -### Code example ```js +// mainNavigation.js export default { getNavigationItems: router => [ @@ -362,31 +113,18 @@ export default { caption: 'Pages', key: 'page', url: router.url('resource.page.index'), - icon: 'Home' - }, - - { - caption: 'Articles', - key: 'article', - url: router.url('resource.article.index'), - icon: 'Copy' + icon: 'home' }, { caption: 'Misc', - icon: 'ThreeDots', + icon: 'threeDots', subItems: [ { caption: 'Categories', key: 'category', url: router.url('resource.category.index') - }, - - { - caption: 'Tags', - key: 'tag', - url: router.url('resource.tag.index') } ] @@ -399,13 +137,11 @@ export default { { caption: 'My settings', url: router.url('mySettings'), - appLink: true }, { caption: 'Public pages', - url: 'http://mySite.com'', - newTab: true + url: 'http://mySite.com'' }, { @@ -424,36 +160,32 @@ export default { ### getNavigationItems -Method "getNavigationItems" takes array of objects with following keys: +Method "getNavigationItems" should return array of objects with following structure: * caption: for item caption -* key: prefix for "Link" class name +* key: for identifying and setting selected item * url: url to point to -* icon: adds sufix to element icon classname (all current icon suffixes can be found in "/src/scss/library/_variables.scss" file, under $icons variable) - +* icon: for defining item icon (see all [available icons]()) ### getUserNavigationItems Method "getUserNavigationItems" takes array of objects with following keys: * caption: for item caption * url : url to point to -* action: if url is ommited, application calls this function with mainNavigation as argument -* appLink: Boolean value, true is in app link -* newTab: adds target="_blank" attribute to link -* icon: adds sufix to element icon classname (all current icon suffixes can be found in "/src/scss/library/_variables.scss" file, under $icons variable) - +* action: optional callback called when item is clicked with main navigation instance as argument +* appLink: Boolean, true by default, set to false for links pointing outside application ### getProjectCaption -Use method "getProjectCaption" to set CMS project name. - +Used for setting project caption. ### getUserCaption -Use method "getUserCaption" to set current user caption. +Used for setting current user caption ## Application Application object is glue that ties all CMS components and services together. It is used to connect services and routes, load translations, inject boot (config) data and start application. -### Code example +### Example ```js +// main.js import app from 'cmf/js/app'; import translations from 'cmf/js/lang/english'; import routes from './routes'; @@ -474,56 +206,83 @@ app ``` -### setBootData -Used to inject boot or config data. "baseUrl" and "baseApiUrl" are mandatory. -```js -// sometimes your data will be generated from backend to global window variable -app.setBootData(window.bootData) -``` -Boot data can later be retrieved like so: +## Configuration +Application is configured by injecting boot or config data on bootstrap. + ```js -import bootData from 'cmf/js/library/bootData'; -bootData('baseUrl'); // outputs boot data baseUrl value +// main.js +app.setBootData({ + baseUrl: '/app/' // application base url + baseApiUrl: '/api/', // api base url + usesPushState: true, +}); ``` -### registerServices -Used to register user defined services to service container. - - -### registerRoutes -Used to register user defined routes to route registry. +**Mandatory keys:** +* baseUrl: for setting application base url +* baseApiUrl: for setting api base url +**Optional keys:** +* usesPushState: for using history (Boolean true) or 'hash' (Boolean false) api for navigation +* usePatchForUpdate: for using HTTP patch method when updating resources (put is used by default) +* googleMapsApiKey: set if using map form element +* resourceToApiMap: for setting custom resource api slugs +* itemsPerPage: for setting default number of items per page on resource listing -### loadTranslations -Used to import translation data for specific locale. +If you need some data from api endpoint before admin UI is bootstrapped you do so by providing 'beforeAdminEnter' callback to application. +### Full configuration example: +```js +app + .setBootData({ + baseUrl: '/app/' // application base url + baseApiUrl: '/api/', // api base url + usesPushState: true, + usePatchForUpdate: true, + googleMapsApiKey: 'my-api-key', + itemsPerPage: 30, + resourceToApiMap: { + article: 'articles', + tag: 'tags' + } + }) + .beforeAdminEnter(() => http.get('api/boot-data').then(apiData => { + app.setBootData(apiData) + })) +``` -### setLocale -Used to set application locale. +Configuration or boot values can later be retrieved like so: ```js -app.setLocale('hr'); // en by default +import bootData from 'cmf/js/library/bootData'; +bootData('baseUrl'); // outputs boot data baseUrl value ``` -### getLocale -Used to get application locale. +## Localization +Trim includes English and Croatian translation files for common UI elements. +Localization is configured during application bootstrap. +Translations are simple key-value javascript files. + ```js +// main.js +import translations from 'cmf/js/lang/english'; + +app.loadTranslations(translations, 'en'); +app.setLocale('en'); // en by default app.getLocale() // en by default; ``` -### beforeAdminEnter -Used to set Promise function before creating admin instance. +Translation api can be used throughout application like so: ```js -app.beforeAdminEnter(() => { return Promise.resolve(); }); -``` +import translate from 'cmf/js/library/translate'; -### start -Once called application will setup router, services and main view components. +translate('translation.key'); +``` ## Authentication -To authenticating users to your app you have to implement simple authentication driver. +To authenticate users to your app you have to implement simple authentication driver. Default view for authenticating with username and password is included in Trim. Examine [base auth api](https://github.com/trikoder/trim/tree/master/src/js/library/auth.js) for full implementation details. -Simple driver implementation is shown bellow: +Example of dummy driver implementation is shown bellow: ```js import api from 'cmf/js/library/api'; diff --git a/documentation/editing-resource.md b/documentation/editing-resource.md new file mode 100644 index 00000000..ad0fbf4a --- /dev/null +++ b/documentation/editing-resource.md @@ -0,0 +1,50 @@ +# Editing resource + +## Resource edit +Resource edit is component responsible for handling how resource is created or updated. + + +### addField +Method for defining what form elements are mapped to resource attributes and relations when resource is edited or created. +Examine [form elements](/form-elements.html) chapter to find out how each form element is configured. + +```js +edit.addField('TextFormElement', { + label: 'Title', + name: 'title' +}); +``` + +### configureLayout +Method for configuring edit and create layout. Supports tabs, regions and groups (all items are auto generated from layout reference). + +```js +// assign form element to layout region (main and side regions are supported) position with layoutReference +edit.addField(TextFormElement, { + label: 'Title', + name: 'title', + layoutReference: 'mainRegion' +}); + +edit.addField(TextFormElement, { + label: 'Meta data', + name: 'meta', + layoutReference: 'sideRegion' +}); +``` +Altering layout configuration to set tab captions: + +```js +// configure tab layout caption +edit.configureLayout({ + 'mainTab.caption': 'Content and settings', + 'seoTab.caption': 'SEO and meta data' +}); + +// assign form element to layout position with layoutReference +editHandler.addField(TextFormElement, { + label: 'Title', + name: 'title', + layoutReference: 'mainTab.mainRegion' +}); +``` \ No newline at end of file diff --git a/documentation/getting-started.md b/documentation/getting-started.md index 5962b094..1e91615a 100644 --- a/documentation/getting-started.md +++ b/documentation/getting-started.md @@ -6,14 +6,14 @@ Everything explained in this chapter has concrete implementation details in demo Feel free to [browse demo codebase](https://github.com/trikoder/trim/tree/master/demo) and take what you need. ## Starter template -Starter template for bootstrapping projects built with Trikoder Trim is available at [trim-starter git repository](https://github.com/trikoder/trim-starter/). +Bootstrap your Trim application in minutes with starter template available at [trim-starter git repository](https://github.com/trikoder/trim-starter/). +Start by cloning repository. ```bash git clone git@github.com:trikoder/trim-starter.git my-cms-project ``` Make sure you have Node.js (8.x and up) and NPM installed. - ```bash cd my-cms-project npm install @@ -25,3 +25,4 @@ npm run dev ``` Your new CMS project should greet you now with welcome page running at localhost. +Starter template comes with simple, standard webpack template. Feel free to adjust your development and build configuration as you see fit. diff --git a/documentation/listing-resource.md b/documentation/listing-resource.md new file mode 100644 index 00000000..c420169d --- /dev/null +++ b/documentation/listing-resource.md @@ -0,0 +1,190 @@ +# Listing resource + +### resourceName +Controller property where we name the resource handled (tag, article, page...) +```js +resourceName: 'tag' +``` + + +### resourceCaption +Controller property where we define resource caption mapping, default value is null. It is currently used only in mass actions component as a value for 'mapSelectedCaptionsTo' (if it is null, then it fallbacks to 'id') so it's recommended to always set resource caption value. +```js +resourceCaption: 'title' +``` + + +### createRequiresDraft +Property where we define if draft resource is needed when resource is created. +When createRequiresDraft is set to true controller will save empty resource object before creating interface is displayed. +Saved draft resource will recieve id and be able to support related objects. False is default value. +```js +createRequiresDraft: false +``` + + +### setupList +Controller method where we define how resource is browsed, filtered and sorted in list view. +Controller injects current [listHandler](#resource-list) instance when method is called. + +```js +setupList: function({list}) { + + this.addCreateControl('Create new tag'); + + list.addFilter('TextFormElement', { + name: 'title', + label: 'Title' + }); + + list.addItem('TextListItem', { + caption: 'ID', + mapTo: 'id' + }); + + list.addItem('LinkListItem', { + caption: 'Title', + mapTo: 'title', + action: 'editItem' + }); +}, +``` + +### setupEdit +Controller method where we define form fields rendered when resource is created or updated. +Controller injects current [editHandler](#resource-edit) instance when method is called, method (edit or create) and resource model when called in editing context. + +```js +setupEdit: function({edit, method, resourceModel}) { + + this.addToIndexControl().addSaveControl(); + + edit.addField('TextFormElement', { + label: 'Title', + name: 'title' + }); + +} +``` + +### openIndex +Controller instance method which opens resource index or listing. + +```js +controller.openIndex(queryParams); +``` + +### openEdit +Controller instance method which opens resource editing. + +```js +controller.openEdit(routeParams, queryParams); +``` + +### openCreate +Controller instance method which opens resource create. + +```js +controller.openCreate(queryParams); +``` + +### addCreateControl +Controller instance method which adds resource create control. + +```js +controller.addCreateControl(caption); +``` + +### addSaveControl +Controller instance method which adds resource save control. + +```js +controller.addSaveControl(caption); +``` + +### addToIndexControl +Controller instance method which adds resource list control. + +```js +controller.addToIndexControl(); +``` + +### addControl +Controller instance method which adds generic resource control. + +```js +controller.addControl({ + caption: 'myControl', + url: this.getCreateUrl(), + className: 'accented iconPlus', + action: this.openCreate +}); +``` + +### addDropdownControl +Controller instance method which adds generic dropdown resource control. + +```js +controller.addDropdownControl(params); +``` + + +## Resource list +Resource list is component responsible for handling resource browsing, filtering and sorting. +Examine [list elements](/list-elements.html) chapter to find out how each list element is configured. + + +### addItem +Method for defining what elements are displayed when resource is listed: + +```js +list.addItem('TextListItem', { + caption: 'Title', + mapTo: 'title' +}); +``` + +### addFilter +Method for adding filter form elements when resource is listed: + +```js +list.addFilter('TextFormElement', { + name: 'title', + label: 'Title' +}); +``` + +### addSort +Method for adding sort options on resource list: + +```js +list.addSort([ + { + label: 'By title', + field: '-title' + }, + { + label: 'By date', + field: '-date' + } +]); +``` + +### setTemplate +Method for choosing which template is used for resource listing ('table' and 'cards' are currently supported). + +```js +list.setTemplate('cards'); +``` + +### addMassAction +Method for adding mass actions to resource list. + +```js +list.addMassAction([ + { + caption: 'Publish', + updateAttributes: {published: true} + } +]); +```