Browse Source

Merge pull request #706 from LasLabs/feature/10.0/SMD-263-mig-web_widget_slick

[ADD] web_widget_slick: Add module
pull/729/head
Dave Lasley 7 years ago
committed by GitHub
parent
commit
ad466e2b5a
  1. 114
      web_widget_slick/README.rst
  2. 3
      web_widget_slick/__init__.py
  3. 25
      web_widget_slick/__manifest__.py
  4. BIN
      web_widget_slick/static/description/icon.png
  5. 79
      web_widget_slick/static/description/icon.svg
  6. BIN
      web_widget_slick/static/lib/slick/ajax-loader.gif
  7. 172
      web_widget_slick/static/lib/slick/slick-theme.less
  8. 2996
      web_widget_slick/static/lib/slick/slick.js
  9. 104
      web_widget_slick/static/lib/slick/slick.less
  10. 172
      web_widget_slick/static/src/js/web_widget_slick.js
  11. 74
      web_widget_slick/static/src/less/slick.less
  12. 9
      web_widget_slick/static/src/xml/web_widget_slick.xml
  13. 225
      web_widget_slick/static/tests/js/web_widget_slick.js
  14. 36
      web_widget_slick/templates/assets.xml
  15. 4
      web_widget_slick/tests/__init__.py
  16. 19
      web_widget_slick/tests/test_ui.py
  17. 48
      web_widget_slick_example/README.rst
  18. 5
      web_widget_slick_example/__init__.py
  19. 25
      web_widget_slick_example/__manifest__.py
  20. 34
      web_widget_slick_example/demo/slick_example_data.xml
  21. 5
      web_widget_slick_example/models/__init__.py
  22. 16
      web_widget_slick_example/models/slick_example.py
  23. 2
      web_widget_slick_example/security/ir.model.access.csv
  24. BIN
      web_widget_slick_example/static/description/icon.png
  25. 79
      web_widget_slick_example/static/description/icon.svg
  26. 46
      web_widget_slick_example/views/slick_example_view.xml

114
web_widget_slick/README.rst

@ -0,0 +1,114 @@
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
=====================
Slick Carousel Widget
=====================
This module provides a Slick Carousel widget for use in the Odoo backend web interface.
Usage
=====
Default usage is on a One2many attachment field, as defined below::
class SlickExample(models.Model):
_name = 'slick.example'
_description = 'Slick Example Model'
image_ids = fields.One2many(
name='Images',
comodel_name='ir.attachment',
inverse_name='res_id',
)
Assuming the above model, you would add a Slick Carousel on the
``image_ids`` column by using the following field definition in the
model's form view::
<field name="image_ids" widget="one2many_slick_images" options="{}"/>
Options
-------
The widget passes options directly through to Slick, so any `setting
available to Slick`_ is available to the widget. Additional options
specific to Odoo are:
+-----------------+--------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Name | Type | Default | Description |
+=================+==============+=====================+=============================================================================================================================================================================+
| ``fieldName`` | ``String`` | ``datas`` | Field to lookup on relation table. Defaults to ``datas``, which is the data field used in ``ir.attachment`` table. This would be used to define a custom attachment model |
+-----------------+--------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``modelName`` | ``String`` | ``ir.attachment`` | Model of attachment relation. This would be used to define a custom attachment model instead of default ``ir.attachment`` |
+-----------------+--------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
.. _setting available to Slick: http://kenwheeler.github.io/slick/#settings
Example Module
--------------
An example implementation, for instructional purposes as well as convenient
functional testing, is provided in the `web_widget_slick_example` module.
* Install `web_widget_slick_example`.
* Activate Developer Mode.
* Go to Settings / Technical / Slick, and open the record to view the widget.
To try out different Slick settings:
* Go to Settings/User Interface/Views and search for 'slick.example.view.form'.
* Open the form view record.
* Click the Edit button.
* In the Architecture editor, find `options="{'slidesToShow': 2}`, and add
any desired settings (separated by commas) inside the curly braces.
* Save the changes and browse to the widget, as described above, to see the
widget with the new settings in effect.
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/162/10.0
Known issues / Roadmap
======================
* Adding / Deleting images from a carousel is not currently supported.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/web/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.
Credits
=======
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
Contributors
------------
* Dave Lasley <dave@laslabs.com>
* Brent Hughes <brent.hughes@laslabs.com>
Do not contact contributors directly about support or help with technical issues.
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.

3
web_widget_slick/__init__.py

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2016-2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

25
web_widget_slick/__manifest__.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Copyright 2016-2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
{
"name": "Slick Carousel Widget",
"summary": "Adds SlickJS slider widget for use as a carousel on Many2one"
" attachment fields in backend form views.",
"version": "10.0.1.0.0",
"category": "Web",
"website": "https://laslabs.com/",
"author": "LasLabs, Odoo Community Association (OCA)",
"license": "LGPL-3",
"application": False,
"installable": True,
"depends": [
"web",
],
"data": [
"templates/assets.xml",
],
"qweb": [
"static/src/xml/web_widget_slick.xml",
],
}

BIN
web_widget_slick/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 9.2 KiB

79
web_widget_slick/static/description/icon.svg
File diff suppressed because it is too large
View File

BIN
web_widget_slick/static/lib/slick/ajax-loader.gif

After

Width: 32  |  Height: 32  |  Size: 4.1 KiB

172
web_widget_slick/static/lib/slick/slick-theme.less

@ -0,0 +1,172 @@
/* Copyright 2013-2016 Ken Wheeler
* Version 1.7.1
* License MIT (https://opensource.org/licenses/MIT) */
@charset "UTF-8";
// Default Variables
@slick-font-path: "./fonts/";
@slick-font-family: "slick";
@slick-loader-path: "./";
@slick-arrow-color: white;
@slick-dot-color: black;
@slick-dot-color-active: @slick-dot-color;
@slick-prev-character: "←";
@slick-next-character: "→";
@slick-dot-character: "•";
@slick-dot-size: 6px;
@slick-opacity-default: 0.75;
@slick-opacity-on-hover: 1;
@slick-opacity-not-active: 0.25;
/* Slider */
.slick-loading .slick-list{
background: #fff url('@{slick-loader-path}ajax-loader.gif') center center no-repeat;
}
/* Arrows */
.slick-prev,
.slick-next {
position: absolute;
display: block;
height: 20px;
width: 20px;
line-height: 0px;
font-size: 0px;
cursor: pointer;
background: transparent;
color: transparent;
top: 50%;
-webkit-transform: translate(0, -50%);
-ms-transform: translate(0, -50%);
transform: translate(0, -50%);
padding: 0;
border: none;
outline: none;
&:hover, &:focus {
outline: none;
background: transparent;
color: transparent;
&:before {
opacity: @slick-opacity-on-hover;
}
}
&.slick-disabled:before {
opacity: @slick-opacity-not-active;
}
}
.slick-prev:before, .slick-next:before {
font-family: @slick-font-family;
font-size: 20px;
line-height: 1;
color: @slick-arrow-color;
opacity: @slick-opacity-default;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
& when ( @slick-font-family = 'slick' ) {
/* Icons */
@font-face {
font-family: 'slick';
font-weight: normal;
font-style: normal;
src: url('@{slick-font-path}slick.eot');
src: url('@{slick-font-path}slick.eot?#iefix') format('embedded-opentype'), url('@{slick-font-path}slick.woff') format('woff'), url('@{slick-font-path}slick.ttf') format('truetype'), url('@{slick-font-path}slick.svg#slick') format('svg');
}
}
}
.slick-prev {
left: -25px;
[dir="rtl"] & {
left: auto;
right: -25px;
}
&:before {
content: @slick-prev-character;
[dir="rtl"] & {
content: @slick-next-character;
}
}
}
.slick-next {
right: -25px;
[dir="rtl"] & {
left: -25px;
right: auto;
}
&:before {
content: @slick-next-character;
[dir="rtl"] & {
content: @slick-prev-character;
}
}
}
/* Dots */
.slick-dotted .slick-slider {
margin-bottom: 30px;
}
.slick-dots {
position: absolute;
bottom: -25px;
list-style: none;
display: block;
text-align: center;
padding: 0;
margin: 0;
width: 100%;
li {
position: relative;
display: inline-block;
height: 20px;
width: 20px;
margin: 0 5px;
padding: 0;
cursor: pointer;
button {
border: 0;
background: transparent;
display: block;
height: 20px;
width: 20px;
outline: none;
line-height: 0px;
font-size: 0px;
color: transparent;
padding: 5px;
cursor: pointer;
&:hover, &:focus {
outline: none;
&:before {
opacity: @slick-opacity-on-hover;
}
}
&:before {
position: absolute;
top: 0;
left: 0;
content: @slick-dot-character;
width: 20px;
height: 20px;
font-family: @slick-font-family;
font-size: @slick-dot-size;
line-height: 20px;
text-align: center;
color: @slick-dot-color;
opacity: @slick-opacity-not-active;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
&.slick-active button:before {
color: @slick-dot-color-active;
opacity: @slick-opacity-default;
}
}
}

2996
web_widget_slick/static/lib/slick/slick.js
File diff suppressed because it is too large
View File

104
web_widget_slick/static/lib/slick/slick.less

@ -0,0 +1,104 @@
/* Copyright 2013-2016 Ken Wheeler
* Version 1.7.1
* License MIT (https://opensource.org/licenses/MIT) */
/* Slider */
.slick-slider {
position: relative;
display: block;
box-sizing: border-box;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-ms-touch-action: pan-y;
touch-action: pan-y;
-webkit-tap-highlight-color: transparent;
}
.slick-list {
position: relative;
overflow: hidden;
display: block;
margin: 0;
padding: 0;
&:focus {
outline: none;
}
&.dragging {
cursor: pointer;
cursor: hand;
}
}
.slick-slider .slick-track,
.slick-slider .slick-list {
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
-ms-transform: translate3d(0, 0, 0);
-o-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
.slick-track {
position: relative;
left: 0;
top: 0;
display: block;
margin-left: auto;
margin-right: auto;
&:before,
&:after {
content: "";
display: table;
}
&:after {
clear: both;
}
.slick-loading & {
visibility: hidden;
}
}
.slick-slide {
float: left;
height: 100%;
min-height: 1px;
[dir="rtl"] & {
float: right;
}
img {
display: block;
}
&.slick-loading img {
display: none;
}
display: none;
&.dragging img {
pointer-events: none;
}
.slick-initialized & {
display: block;
}
.slick-loading & {
visibility: hidden;
}
.slick-vertical & {
display: block;
height: auto;
border: 1px solid transparent;
}
}
.slick-arrow.slick-hidden {
display: none;
}

172
web_widget_slick/static/src/js/web_widget_slick.js

@ -0,0 +1,172 @@
/* Copyright 2016-2017 LasLabs Inc.
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). */
odoo.define('web_widget_slick', function(require) {
"use strict";
var core = require('web.core');
var AbstractManyField = require('web.form_relational').AbstractManyField;
var FieldSlickImages = AbstractManyField.extend({
widget_class: 'o_slick',
template: 'FieldSlickImages',
$slick: null,
no_rerender: true,
loading: [],
loaded: 0,
events: {
'mousedown img': function(ev) {
ev.preventDefault();
},
'touchstart img': function(ev) {
ev.preventDefault();
},
// Triggering a resize on the lazyLoaded event prevents the carousel
// from appearing empty when page loads
'lazyLoaded': function(ev) {
$(ev.target).trigger('resize');
}
},
defaults: {
lazyLoad: 'ondemand',
fieldName: 'datas',
modelName: 'ir.attachment',
slidesToShow: 3,
slidesToScroll: 1,
swipeToSlide: true,
dots: true,
infinite: true,
speed: 500,
arrows: true,
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 2,
slidesToScroll: 1
}
},
{
breakpoint: 600,
settings: {
slidesToShow: 1,
slidesToScroll: 1
}
}
// You can unslick at a given breakpoint now by adding:
// settings: "unslick"
// instead of a settings object
]
},
init: function(field_manager, node) {
this._super(field_manager, node);
this.options = _.defaults(this.options, this.defaults);
},
destroy_content: function() {
if (this.$slick) {
var $imgs = this.$el.find('img');
// Unslicking removes the carousel but re-appends any images,
// so removal of images is also required
$imgs.each($.proxy(this._slickRemove, this));
this.$slick.slick('unslick');
}
},
render_value: function() {
this._super();
this.destroy_content();
this.$el.parent('td').addClass('o_slick_cell');
this.$slick = $('<div class="slick-container"></div>');
if (this.options.arrows) {
this.$slick.addClass('slick-arrowed');
}
this.$el.append(this.$slick);
var baseUrl = '/web/image/' + this.options.modelName + '/';
var value = this.get('value');
this.loading.push.apply(value);
_.each(value, $.proxy(this._slickRender, this, [baseUrl]));
this.$slick.slick(this.options);
core.bus.on('resize', this, this._resizeCarousel);
},
_resizeCarousel: function () {
var maxWidth = this._resizeMaxWidth();
var containerWidth = maxWidth;
var $parentCell = this.$el.parent('td');
if ($parentCell.length) {
var scaledWidth = this._resizeScaledWidth($parentCell, maxWidth);
var labelWidth = this._resizeLabelWidth($parentCell);
containerWidth = scaledWidth - labelWidth;
}
var marginWidth = this._resizeMarginWidth(this.$slick);
var carouselWidth = containerWidth - marginWidth;
// Set outerWidth of carousel, with minimum size. Minimum size can cause
// overflow in some cases but prevents displaying with zero width
this.$slick.outerWidth(Math.max(carouselWidth, 150));
},
_resizeLabelWidth: function ($parentCell) {
// If the widget has a label, subtract label cell's width, plus the extra
// padding applied to the parent cell, from container width
var $labelCell = $parentCell.prev('.o_td_label');
if ($labelCell.length) {
var parentPadding = $parentCell.outerWidth() - $parentCell.width();
return $labelCell.outerWidth() + parentPadding;
}
return 0;
},
_resizeMarginWidth: function ($element) {
// Subtract container's margins so outerWidth can be set properly
return $element.outerWidth(true) - $element.outerWidth();
},
_resizeMaxWidth: function () {
// Determine the maximum possible width the widget container can occupy
var parentSelectors = ['.o_form_sheet', '.o_form_nosheet'];
var containerWidth = parentSelectors.map(function (selector) {
return this.$el.closest(selector).width();
}, this).filter(function (width) {
return width !== null;
})[0];
return containerWidth;
},
_resizeScaledWidth: function ($parentCell, maxWidth) {
// If the widget is inside a group tag, scale carousel size based on
// intended % width of parent cell
return maxWidth * parseInt($parentCell[0].style.width, 10) / 100;
},
_slickRemove: function (idx, val) {
this.$slick.slick('slickRemove', idx);
},
_slickRender: function (baseUrl, id) {
var $img = $('<img class="img img-responsive"></img>');
var $div = $('<div></div>');
$img.attr('data-lazy', baseUrl + id + '/' + this.options.fieldName);
$div.append($img);
this.$slick.append($div);
}
});
core.form_widget_registry.add("one2many_slick_images", FieldSlickImages);
return {FieldSlickImages: FieldSlickImages};
});

74
web_widget_slick/static/src/less/slick.less

@ -0,0 +1,74 @@
/* Copyright 2016-2017 LasLabs Inc.
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). */
@slick-arrow-margin: 40px;
@slick-arrow-offset: calc(-(@slick-arrow-margin - 5px));
@slick-font-family: "FontAwesome";
@slick-dot-character: "\f10c";
@slick-dot-character-active: "\f111";
@slick-dot-color: @odoo-brand-primary;
@slick-dot-offset: 35px;
@slick-dot-size: 12px;
@slick-next-character: "\f054";
@slick-prev-character: "\f053";
@slick-opacity-default: 1;
@slick-opacity-not-active: 0.5;
/* Odoo field */
.o_slick {
display: block !important;
}
/* Container */
.slick-container {
&.slick-dotted {
margin-bottom: @slick-dot-offset;
}
&.slick-arrowed {
margin-left: @slick-arrow-margin;
margin-right: @slick-arrow-margin;
.slick-prev {
left: @slick-arrow-offset;
}
.slick-next {
right: @slick-arrow-offset;
}
}
}
/* Images */
.slick-slide {
margin-left: 2px;
margin-right: 2px;
outline: none;
img {
margin-left: auto;
margin-right: auto;
}
}
/* Arrows */
.slick-arrow {
height: 35px;
width: 30px;
font-size: 0 !important;
padding: 0 !important;
.btn();
.btn-sm();
.btn-primary();
}
/* Dots */
.slick-dots {
bottom: calc(-(@slick-dot-offset));
li {
&.slick-active button:before {
content: @slick-dot-character-active;
}
}
}

9
web_widget_slick/static/src/xml/web_widget_slick.xml

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016-2017 LasLabs Inc.
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). -->
<template>
<t t-name="FieldSlickImages">
<div t-attf-class="o_form_field {{ widget.widget_class }}"></div>
</t>
</template>

225
web_widget_slick/static/tests/js/web_widget_slick.js

@ -0,0 +1,225 @@
/* Copyright 2017 LasLabs Inc.
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). */
odoo.define_section('web_widget_slick', ['web.core', 'web.form_common'], function(test) {
"use strict";
function appendWidget (core, formCommon, $fix) {
var fieldManager = new formCommon.DefaultFieldManager(null, {});
var node = {'attrs': {}};
var FieldSlickImages = core.form_widget_registry.get('one2many_slick_images');
var widget = new FieldSlickImages(fieldManager, node);
widget.appendTo($fix);
return widget;
}
function imageUrl (modelName, fieldName, id) {
return '/web/image/' + modelName + '/' + id + '/' + fieldName;
}
test('It should add a slick widget',
function(assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var widget = appendWidget(core, formCommon, $fix);
var slickContainerCount = $fix.find('.slick-container').length;
assert.strictEqual(slickContainerCount, 1);
}
);
test('.init() should add defaults to options',
function(assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var fieldManager = new formCommon.DefaultFieldManager(null, {});
var node = {'attrs': {}};
var FieldSlickImages = core.form_widget_registry.get('one2many_slick_images');
var widget = new FieldSlickImages(fieldManager, node);
widget.appendTo($fix);
widget.defaults.testing = 'tested';
widget.init(fieldManager, node);
assert.strictEqual(widget.options.testing, 'tested');
}
);
test('.destroy_content() should remove images',
function(assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var widget = appendWidget(core, formCommon, $fix);
var $slickImage = $('<div><img></div>');
widget.$slick.slick('slickAdd', $slickImage);
widget.destroy_content();
var slickImageCount = widget.$slick.find('img').length;
assert.strictEqual(slickImageCount, 0);
}
);
test('.destroy_content() should remove carousel',
function (assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var widget = appendWidget(core, formCommon, $fix);
widget.destroy_content();
var slickChildren = widget.$slick.children().length;
assert.strictEqual(slickChildren, 0);
}
);
test('.render_value() should add images corresponding to field value',
function(assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var widget = appendWidget(core, formCommon, $fix);
var fieldValues = [1, 2];
widget.set({'value': fieldValues});
widget.render_value();
var slickImages = widget.$slick.find('img');
var slickImageUrls = slickImages.map(function() {
return $(this).data('lazy');
}).get();
var modelName = widget.options.modelName;
var fieldName = widget.options.fieldName;
var expectedUrls = fieldValues.map(function(id) {
return '/web/image/' + modelName + '/' + id + '/' + fieldName;
});
assert.deepEqual(slickImageUrls, expectedUrls);
}
);
test('._resizeCarousel() should resize the widget',
function (assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var $nosheet = $('<div class="o_form_nosheet"></div>');
$fix.append($nosheet);
var widget = appendWidget(core, formCommon, $nosheet);
var setWidth = 50;
widget.$slick.outerWidth(setWidth);
widget._resizeCarousel();
assert.notStrictEqual(widget.$slick.outerWidth(), setWidth);
}
);
test('._resizeCarousel() should be called when container is resized',
function (assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var $nosheet = $('<div class="o_form_nosheet"></div>');
$fix.append($nosheet);
var widget = appendWidget(core, formCommon, $nosheet);
var setWidth = 50;
widget.$slick.outerWidth(setWidth);
core.bus.trigger('resize');
assert.notStrictEqual(widget.$slick.outerWidth(), setWidth);
}
);
test('._resizeLabelWidth() should return the width of the preceding ' +
'sibling label cell if it exists',
function (assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var widget = appendWidget(core, formCommon, $fix);
var width = 100;
var $cell = $('<td style="width:10px;"></td>');
var $labelCell = $('<td class="o_td_label"></td>');
$labelCell.outerWidth(width);
widget.$slick.append($labelCell);
widget.$slick.append($cell);
assert.strictEqual(widget._resizeLabelWidth($cell), width);
}
);
test('._resizeLabelWidth() should return 0 if the previous sibling cell ' +
' of the provided element is not a label cell',
function (assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var widget = appendWidget(core, formCommon, $fix);
var width = '100px';
var $cell = $('<td></td>');
widget.$slick.append($('<td style="width:' + width + ';"></td>'));
widget.$slick.append($cell);
assert.strictEqual(widget._resizeLabelWidth($cell), 0);
}
);
test('._resizeMarginWidth() should return the total left and right ' +
' margins of the provided element',
function (assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var widget = appendWidget(core, formCommon, $fix);
var elementStyle = 'margin-left: 12px; margin-right: 7px;';
var marginTotal = 19;
var $element = $('<div style="' + elementStyle + '"></div>');
widget.$slick.append($element);
assert.strictEqual(widget._resizeMarginWidth($element), marginTotal);
}
);
test('._resizeMaxWidth() should return the width of the closest sheet element',
function (assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var $sheet1 = $('<div class="o_form_sheet"></div>');
var $sheet2 = $('<div class="o_form_sheet"></div>');
var expectedWidth = 266;
$sheet1.width(700);
$sheet2.width(expectedWidth);
$sheet1.append($sheet2);
$fix.append($sheet1);
var widget = appendWidget(core, formCommon, $sheet2);
assert.strictEqual(widget._resizeMaxWidth(), expectedWidth);
}
);
test('._resizeMaxWidth() should return the width of the closest nosheet element',
function (assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var $nosheet1 = $('<div class="o_form_nosheet"></div>');
var $nosheet2 = $('<div class="o_form_nosheet"></div>');
var expectedWidth = 266;
$nosheet1.width(700);
$nosheet2.width(expectedWidth);
$nosheet1.append($nosheet2);
$fix.append($nosheet1);
var widget = appendWidget(core, formCommon, $nosheet2);
assert.strictEqual(widget._resizeMaxWidth(), expectedWidth);
}
);
test('._resizeScaledWidth() should return the provided integer, scaled' +
'to the % width in the provided element style attribute',
function (assert, core, formCommon) {
var $fix = $('#qunit-fixture');
var widget = appendWidget(core, formCommon, $fix);
var givenWidth = 100;
var widthPercent = 54;
var expectedWidth = widthPercent;
var $cell = $('<td style="width:' + widthPercent + '%;"></td>');
assert.strictEqual(widget._resizeScaledWidth($cell, givenWidth), expectedWidth);
}
);
});

36
web_widget_slick/templates/assets.xml

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016-2017 LasLabs Inc.
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<odoo>
<template id="assets_slick" inherit_id="web.assets_backend">
<xpath expr="//script[last()]" position="after">
<link rel="stylesheet"
type="text/less"
href="/web_widget_slick/static/lib/slick/slick.less"
/>
<link rel="stylesheet"
type="text/less"
href="/web_widget_slick/static/lib/slick/slick-theme.less"
/>
<link rel="stylesheet"
type="text/less"
href="/web_widget_slick/static/src/less/slick.less"
/>
<script type="application/javascript"
src="/web_widget_slick/static/lib/slick/slick.js"
/>
<script type="application/javascript"
src="/web_widget_slick/static/src/js/web_widget_slick.js"
/>
</xpath>
</template>
<template id="qunit_suite" inherit_id="web.qunit_suite">
<xpath expr="//t[@t-set='head']" position="inside">
<script type="application/javascript"
src="/web_widget_slick/static/tests/js/web_widget_slick.js"
/>
</xpath>
</template>
</odoo>

4
web_widget_slick/tests/__init__.py

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import test_ui

19
web_widget_slick/tests/test_ui.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from odoo.tests.common import HttpCase
class UICase(HttpCase):
post_install = True
at_install = False
def test_ui_web(self):
"""Test backend tests."""
self.phantom_js(
"/web/tests?debug=assets&module=web_widget_slick",
"",
login="admin",
)

48
web_widget_slick_example/README.rst

@ -0,0 +1,48 @@
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
=============================
Slick Carousel Widget Example
=============================
This module provides an example of how to implement the web_widget_slick module.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/web/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.
Credits
=======
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
Contributors
------------
* Dave Lasley <dave@laslabs.com>
* Brent Hughes <brent.hughes@laslabs.com>
Do not contact contributors directly about support or help with technical issues.
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.

5
web_widget_slick_example/__init__.py

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2016-2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from . import models

25
web_widget_slick_example/__manifest__.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Copyright 2016-2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
{
"name": "Slick Carousel Widget Example",
"summary": "Example usage of the web_widget_slick module",
"version": "10.0.1.0.0",
"category": "Hidden",
"website": "https://laslabs.com/",
"author": "LasLabs, Odoo Community Association (OCA)",
"license": "LGPL-3",
"application": False,
"installable": True,
"depends": [
"web_widget_slick",
],
"data": [
'views/slick_example_view.xml',
'security/ir.model.access.csv',
],
"demo": [
'demo/slick_example_data.xml',
],
}

34
web_widget_slick_example/demo/slick_example_data.xml
File diff suppressed because it is too large
View File

5
web_widget_slick_example/models/__init__.py

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2016-2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from . import slick_example

16
web_widget_slick_example/models/slick_example.py

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Copyright 2016-2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from odoo import models, fields
class SlickExample(models.Model):
_name = 'slick.example'
_description = 'Slick Example Model'
image_ids = fields.One2many(
name='Images',
comodel_name='ir.attachment',
inverse_name='res_id',
)

2
web_widget_slick_example/security/ir.model.access.csv

@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
slick_example_manager,slick.example.manager,web_widget_slick_example.model_slick_example,base.group_no_one,1,1,1,1

BIN
web_widget_slick_example/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 9.2 KiB

79
web_widget_slick_example/static/description/icon.svg
File diff suppressed because it is too large
View File

46
web_widget_slick_example/views/slick_example_view.xml

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016-2017 LasLabs Inc.
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). -->
<odoo>
<record id="slick_example_view_form" model="ir.ui.view">
<field name="name">slick.example.view.form</field>
<field name="model">slick.example</field>
<field name="arch" type="xml">
<form string="Slick Example">
<header />
<sheet>
<group>
<field name="image_ids" widget="one2many_slick_images"
options="{'slidesToShow': 2}" />
</group>
</sheet>
<footer />
</form>
</field>
</record>
<record id="slick_example_view_tree" model="ir.ui.view">
<field name="name">slick.example.view.tree</field>
<field name="model">slick.example</field>
<field name="arch" type="xml">
<tree string="Slick Example">
<field name="id" />
</tree>
</field>
</record>
<record id="slick_example_action" model="ir.actions.act_window">
<field name="name">Slick Examples</field>
<field name="res_model">slick.example</field>
<field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="slick_example_menu"
name="Slick"
parent="base.menu_custom"
action="slick_example_action"
sequence="1" />
</odoo>
Loading…
Cancel
Save