Action WCS configurables dans la cellule fiche (#75908) #252
|
@ -0,0 +1,154 @@
|
|||
env:
|
||||
browser: true
|
||||
es6: true
|
||||
ignorePatterns:
|
||||
- "combo/apps/dashboard/static/js/dashboard.js"
|
||||
- "combo/apps/dataviz/static/js/chartngcell.js"
|
||||
- "combo/apps/dataviz/static/js/combo.cubes-barchart.js"
|
||||
- "combo/apps/dataviz/static/js/combo.gauge.js"
|
||||
- "combo/apps/dataviz/static/js/gauge.min.js"
|
||||
- "combo/apps/dataviz/static/js/pygal-tooltips.js"
|
||||
- "combo/apps/family/static/js/combo.weekly_agenda.js"
|
||||
- "combo/apps/gallery/static/js/combo.gallery.js"
|
||||
- "combo/apps/lingo/static/js/tipi.js"
|
||||
- "combo/apps/maps/static/js/combo.map.js"
|
||||
- "combo/apps/maps/static/js/leaflet-gps.js"
|
||||
- "combo/apps/maps/static/js/leaflet-search.js"
|
||||
- "combo/apps/pwa/templates/combo/service-worker-registration.js"
|
||||
- "combo/apps/pwa/templates/combo/service-worker.js"
|
||||
- "combo/apps/wcs/static/js/combo.filter-cards.js"
|
||||
- "combo/apps/wcs/static/js/combo.submission.js"
|
||||
- "combo/combo/apps/maps/static/js/combo.map.js"
|
||||
- "combo/manager/static/js/combo.manager.js"
|
||||
- "combo/public/static/js/combo.public.js"
|
||||
extends: eslint:recommended
|
||||
parserOptions:
|
||||
ecmaVersion: 13
|
||||
sourceType: module
|
||||
overrides:
|
||||
- files: tests/js/**/*.js
|
||||
env:
|
||||
node: true
|
||||
rules:
|
||||
# Follow Standard JS guidelines : https://standardjs.com/rules.html, except rules
|
||||
# annotated with a 'custom' comment
|
||||
|
||||
# Linting
|
||||
array-callback-return: error
|
||||
constructor-super: error
|
||||
eqeqeq: [error, always, {null: ignore}]
|
||||
handle-callback-err: error
|
||||
no-array-constructor: error
|
||||
no-caller: error
|
||||
no-class-assign: error
|
||||
no-cond-assign: error
|
||||
no-const-assign: error
|
||||
no-control-regex: error
|
||||
no-debugger: error
|
||||
no-delete-var: error
|
||||
no-dupe-args: error
|
||||
no-dupe-class-members: error
|
||||
no-dupe-keys: error
|
||||
no-duplicate-case: error
|
||||
no-duplicate-imports: error
|
||||
no-empty-character-class: error
|
||||
no-empty-pattern: error
|
||||
no-eval: error
|
||||
no-ex-assign: error
|
||||
no-extend-native: error
|
||||
no-extra-boolean-cast: error
|
||||
no-fallthrough: error
|
||||
no-func-assign: error
|
||||
no-global-assign: error
|
||||
no-implied-eval: error
|
||||
no-inner-declarations: error
|
||||
no-invalid-regexp: error
|
||||
no-iterator: error
|
||||
no-labels: error
|
||||
no-new-func: error
|
||||
no-new-object: error
|
||||
no-new-require: error
|
||||
no-new-symbol: error
|
||||
no-new-wrappers: error
|
||||
no-new: error
|
||||
no-obj-calls: error
|
||||
no-octal-escape: error
|
||||
no-octal: error
|
||||
no-proto: error
|
||||
no-redeclare: error
|
||||
no-regex-spaces: error
|
||||
no-return-assign: error
|
||||
no-self-assign: error
|
||||
no-self-compare: error
|
||||
no-sequences: error
|
||||
no-shadow-restricted-names: error
|
||||
no-sparse-arrays: error
|
||||
no-template-curly-in-string: error
|
||||
no-this-before-super: error
|
||||
no-throw-literal: error
|
||||
no-undef: error
|
||||
no-unexpected-multiline: error
|
||||
no-unmodified-loop-condition: error
|
||||
no-unneeded-ternary: error
|
||||
no-unreachable: error
|
||||
no-unsafe-finally: error
|
||||
no-unsafe-negation: error
|
||||
no-unused-vars: error
|
||||
no-use-before-define: [error, {functions: false, variables: false, classes: false}]
|
||||
no-useless-call: error
|
||||
no-useless-computed-key: error
|
||||
no-useless-constructor: error
|
||||
no-useless-escape: error
|
||||
no-var: error
|
||||
no-with: error
|
||||
use-isnan: error
|
||||
valid-typeof: error
|
||||
|
||||
# Style / Formatting
|
||||
accessor-pairs: error
|
||||
block-spacing: error
|
||||
brace-style: [error, 1tbs, {allowSingleLine: true}]
|
||||
camelcase: error
|
||||
comma-dangle: [error, always-multiline] # custom : Adding a dangling comma make patches shorter
|
||||
comma-spacing: error
|
||||
comma-style: error
|
||||
curly: [error, multi-line]
|
||||
dot-location: [error, property]
|
||||
eol-last: [error, always]
|
||||
func-call-spacing: error
|
||||
indent: [error, 2]
|
||||
key-spacing: error
|
||||
keyword-spacing: error
|
||||
max-len: [error, {code: 110}] # custom: configured like this on python projects
|
||||
new-cap: [error, { newIsCap: true, capIsNew: false}]
|
||||
new-parens: error
|
||||
no-extra-parens: [error, functions]
|
||||
no-floating-decimal: error
|
||||
no-irregular-whitespace: error
|
||||
no-lone-blocks: error
|
||||
no-mixed-spaces-and-tabs: error
|
||||
no-multi-spaces: error
|
||||
no-multi-str: error
|
||||
no-multiple-empty-lines: error
|
||||
no-tabs: error
|
||||
no-trailing-spaces: error
|
||||
no-undef-init: error
|
||||
no-useless-rename: error
|
||||
no-whitespace-before-property: error
|
||||
object-property-newline: [error, { allowMultiplePropertiesPerLine: true }]
|
||||
one-var: [error, never]
|
||||
operator-linebreak: [error, before]
|
||||
padded-blocks: [error, never]
|
||||
quotes: [error, single]
|
||||
rest-spread-spacing: error
|
||||
semi-spacing: error
|
||||
semi: [error, never]
|
||||
space-before-function-paren: error
|
||||
space-in-parens: error
|
||||
space-infix-ops: error
|
||||
space-unary-ops: error
|
||||
spaced-comment: error
|
||||
template-curly-spacing: error
|
||||
wrap-iife: [error, any]
|
||||
yield-star-spacing: [error, {after: true, before: true}]
|
||||
|
|
@ -34,3 +34,10 @@ repos:
|
|||
rev: v0.3
|
||||
hooks:
|
||||
- id: pre-commit-debian
|
||||
- repo: https://github.com/pre-commit/mirrors-eslint
|
||||
rev: v8.23.1
|
||||
hooks:
|
||||
- id: eslint
|
||||
files: \.m?js$
|
||||
types: [file]
|
||||
args: [--fix]
|
||||
|
|
|
@ -1015,7 +1015,7 @@ class WcsCardCell(CardMixin, CellBase):
|
|||
verbose_name = _('Card(s)')
|
||||
|
||||
class Media:
|
||||
js = ('js/combo.filter-cards.js', 'xstatic/select2.min.js')
|
||||
js = ('js/combo.filter-cards.js', 'xstatic/select2.min.js', 'js/combo.wcs-trigger-button.js')
|
||||
css = {'all': ('xstatic/select2.min.css', 'css/combo.filter-cards.css')}
|
||||
|
||||
def include_filters(self):
|
||||
|
@ -1171,7 +1171,7 @@ class WcsCardCell(CardMixin, CellBase):
|
|||
self.card_slug,
|
||||
self.card_custom_view,
|
||||
)
|
||||
api_url += '?include-fields=on&include-submission=on&include-workflow=on'
|
||||
api_url += '?include-fields=on&include-submission=on&include-workflow=on&include-actions=on'
|
||||
user = self.get_concerned_user(context)
|
||||
if self.only_for_user and user and not user.is_anonymous and user.get_name_id():
|
||||
api_url += '&filter-user-uuid=%s' % user.get_name_id()
|
||||
|
@ -1262,9 +1262,7 @@ class WcsCardCell(CardMixin, CellBase):
|
|||
card_custom_view,
|
||||
card_id,
|
||||
)
|
||||
api_url += (
|
||||
'?include-files-content=off&include-evolution=off&include-roles=off&include-workflow-data=off'
|
||||
)
|
||||
api_url += '?include-files-content=off&include-evolution=off&include-roles=off&include-workflow-data=off&include-actions=on'
|
||||
user = self.get_concerned_user(context)
|
||||
if only_for_user and user and not user.is_anonymous and user.get_name_id():
|
||||
api_url += '&filter-user-uuid=%s' % user.get_name_id()
|
||||
|
@ -1646,6 +1644,23 @@ class WcsCardCell(CardMixin, CellBase):
|
|||
card_data['urls'] = {}
|
||||
if self.custom_schema:
|
||||
for item in self.get_custom_schema().get('cells') or []:
|
||||
if item.get('varname') == '@action@':
|
||||
if item.get('action_ask_confirmation', False):
|
||||
render_template(
|
||||
item=item,
|
||||
template_key='action_confirmation_template',
|
||||
template_context=custom_context,
|
||||
target_key='custom_fields',
|
||||
target_context=card_data,
|
||||
)
|
||||
render_template(
|
||||
item=item,
|
||||
template_key='action_label',
|
||||
template_context=custom_context,
|
||||
target_key='custom_fields',
|
||||
target_context=card_data,
|
||||
)
|
||||
|
||||
if item.get('varname') not in ['@custom@', '@link@']:
|
||||
continue
|
||||
if not item.get('template'):
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
class WcsTriggerButton extends HTMLElement {
|
||||
#triggerUrl
|
||||
|
||||
|
||||
connectedCallback () {
|
||||
this.addEventListener('click', this.onClick.bind(this))
|
||||
const label = this.getAttribute('label')
|
||||
this.#triggerUrl = this.getAttribute('trigger-url')
|
||||
this.innerHTML = `<button>${label}</button>`
|
||||
if (this.#triggerUrl === null) {
|
||||
fpeters
commented
Et pareil question js, .# c'est quoi ? (et détail je trouve plus lisible quand il y a un espace après le if). Et pareil question js, .# c'est quoi ?
(et détail je trouve plus lisible quand il y a un espace après le if).
csechet
commented
C.F commentaire précédent.
Je vais peut-être en profiter pour faire tourner eslint sur les nouveaux fichiers ajoutés. > Et pareil question js, .# c'est quoi ?
C.F commentaire précédent.
> (et détail je trouve plus lisible quand il y a un espace après le if).
Je vais peut-être en profiter pour faire tourner eslint sur les nouveaux fichiers ajoutés.
|
||||
const unavailableMode = this.getAttribute('unavailable-mode')
|
||||
if (unavailableMode === 'hide') {
|
||||
this.hidden = true
|
||||
} else {
|
||||
const button = this.querySelector('button')
|
||||
button.disabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onClick () {
|
||||
const confirmationMessage = this.getAttribute('confirmation-message')
|
||||
|
||||
if (confirmationMessage && !confirm(confirmationMessage)) {
|
||||
return
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
this.getAttribute('proxy-url'),
|
||||
{
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': this.getAttribute('csrf-token'),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
'trigger-url': this.#triggerUrl,
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
const json = await response.json()
|
||||
|
||||
const button = this.querySelector('button')
|
||||
if (!response.ok || json.err === 1) {
|
||||
alert(this.getAttribute('error-message'))
|
||||
button.disabled = false
|
||||
} else {
|
||||
button.disabled = true
|
||||
fpeters
commented
Passer ça dans gettext ? Passer ça dans gettext ?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('wcs-trigger-button', WcsTriggerButton)
|
||||
|
|
@ -57,6 +57,29 @@
|
|||
{% endwith %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% elif item.varname == "@action@" %}
|
||||
<div class="{{ item.cell_size|default:"" }}">
|
||||
<wcs-trigger-button
|
||||
{% if item.action_ask_confirmation %}
|
||||
{% with card.custom_fields|get:item.action_confirmation_template|force_escape as confirmation_message %}
|
||||
confirmation-message="{{ confirmation_message }}"
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
{% with card.custom_fields|get:item.action_label|force_escape as label %}
|
||||
label="{{ label }}"
|
||||
{% endwith %}
|
||||
proxy-url='{% url 'wcs-trigger-proxy' %}'
|
||||
unavailable-mode="{{ item.unavailable_action_mode }}"
|
||||
{% with card.actions|get:item.trigger_id as trigger_url %}
|
||||
{% if trigger_url %}
|
||||
trigger-url='{{ trigger_url }}'
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
error-message="{% trans 'An error occured' %}"
|
||||
csrf-token='{{ csrf_token }}'
|
||||
>
|
||||
</wcs-action-button>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if item.varname %}
|
||||
{% with fields_by_varnames|get:item.varname as field %}
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
{% if item.template and link %}
|
||||
<th>{{ item.header|default:"" }}</th>
|
||||
{% endif %}
|
||||
{% elif item.varname == "@action@" %}
|
||||
<th>{{ item.header|default:"" }}</th>
|
||||
{% else %}
|
||||
{% if item.varname %}
|
||||
{% with fields_by_varnames|get:item.varname as field %}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% spaceless %}
|
||||
{% if item.varname == "@custom@" %}
|
||||
{% if item.template %}
|
||||
|
@ -30,6 +32,29 @@
|
|||
{% endwith %}
|
||||
{% if not ul_display %}</td>{% endif %}
|
||||
{% endif %}
|
||||
{% elif item.varname == "@action@" %}
|
||||
{% if not ul_display %}<td>{% endif %}
|
||||
<wcs-trigger-button
|
||||
{% if item.action_ask_confirmation %}
|
||||
{% with card.custom_fields|get:item.action_confirmation_template|force_escape as confirmation_message %}
|
||||
confirmation-message="{{ confirmation_message }}"
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
{% with card.custom_fields|get:item.action_label|force_escape as label %}
|
||||
label="{{ label }}"
|
||||
{% endwith %}
|
||||
proxy-url='{% url 'wcs-trigger-proxy' %}'
|
||||
unavailable-mode="{{ item.unavailable_action_mode }}"
|
||||
{% with card.actions|get:item.trigger_id as trigger_url %}
|
||||
{% if trigger_url %}
|
||||
trigger-url='{{ trigger_url }}'
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
error-message="{% trans 'An error occured' %}"
|
||||
csrf-token='{{ csrf_token }}'
|
||||
>
|
||||
</wcs-action-button>
|
||||
{% if not ul_display %}</td>{% endif %}
|
||||
{% else %}
|
||||
{% if item.varname %}
|
||||
{% with fields_by_varnames|get:item.varname as field and card.fields|get:item.varname as value %}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
<option value="@info-field@">{% trans "Card information field" %}</option>
|
||||
<option value="@custom@">{% trans "Custom" %}</option>
|
||||
<option value="@link@">{% trans "Link" %}</option>
|
||||
<option value="@action@">{% trans "Action" %}</option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
|
@ -186,6 +187,47 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
{# fields group for "content type == @action@" #}
|
||||
<div data-dynamic-display-child-of="entry_type" data-dynamic-display-value="@action@">
|
||||
<p>
|
||||
<label>
|
||||
{% trans "Label:" %}
|
||||
fpeters
commented
: manquant ? : manquant ?
|
||||
<input name="action_label" />
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
{% trans "Action:" %}
|
||||
fpeters
commented
Espace à retirer avant le : Espace à retirer avant le :
|
||||
<select name="trigger_id">
|
||||
{% for id, action in card_schema.workflow.actions.items %}
|
||||
<option value="{{ id }}">{{ action.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
{% trans "Ask confirmation:" %}
|
||||
fpeters
commented
Espace à remplacer par un : Espace à remplacer par un :
|
||||
<input data-dynamic-display-parent="true" type="checkbox" value="true" name="action_ask_confirmation" style="resize: vertical;"></input>
|
||||
</label>
|
||||
</p>
|
||||
<p data-dynamic-display-child-of="action_ask_confirmation" data-dynamic-display-checked="true">
|
||||
<label>
|
||||
{% trans "Confirmation text (template):" %}
|
||||
fpeters
commented
Je retirerais la majuscule à Template, et j'ajouterais un : Je retirerais la majuscule à Template, et j'ajouterais un :
|
||||
<textarea name="action_confirmation_template" style="resize: vertical;"></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
{% trans "Unavailable action mode:" %}
|
||||
fpeters
commented
Espace à retirer devant les : Espace à retirer devant les :
|
||||
<select name="unavailable_action_mode">
|
||||
<option value="hide">{% trans "Hide action button" %}</option>
|
||||
<option value="disable">{% trans "Disable action button" %}</option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<label>
|
||||
{% trans "Size" %}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
<option value="@info-field@">{% trans "Card information field" %}</option>
|
||||
<option value="@custom@">{% trans "Custom" %}</option>
|
||||
<option value="@link@">{% trans "Link" %}</option>
|
||||
<option value="@action@">{% trans "Action" %}</option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
|
@ -151,6 +152,53 @@
|
|||
</label>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{# fields group for "content type == @action@" #}
|
||||
<div data-dynamic-display-child-of="entry_type" data-dynamic-display-value="@action@">
|
||||
<p>
|
||||
<label>
|
||||
{% trans "Header:" %}
|
||||
<input name="action_header" />
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
{% trans "Label:" %}
|
||||
fpeters
commented
cf commentaires plus haut. J'imagine que c'était galère de factoriser pour ne pas dupliquer ça, qu'il y a des petites différences ? cf commentaires plus haut.
J'imagine que c'était galère de factoriser pour ne pas dupliquer ça, qu'il y a des petites différences ?
csechet
commented
J'ai jeté un œil pour factoriser, les templates et le code dans combo.manager.js, mais vu l'existant c'est une grosse galère : j'ai préféré coller à ce qui était déjà fait, même si ça me satisfait pas vraiment non plus. J'ai jeté un œil pour factoriser, les templates et le code dans combo.manager.js, mais vu l'existant c'est une grosse galère : j'ai préféré coller à ce qui était déjà fait, même si ça me satisfait pas vraiment non plus.
|
||||
<input name="action_label" />
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
{% trans "Action:" %}
|
||||
<select name="trigger_id">
|
||||
{% for id, action in card_schema.workflow.actions.items %}
|
||||
<option value="{{ id }}">{{ action.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
{% trans "Ask confirmation:" %}
|
||||
<input data-dynamic-display-parent="true" type="checkbox" value="true" name="action_ask_confirmation" style="resize: vertical;"></input>
|
||||
</label>
|
||||
</p>
|
||||
<p data-dynamic-display-child-of="action_ask_confirmation" data-dynamic-display-checked="true">
|
||||
<label>
|
||||
{% trans "Confirmation text (template):" %}
|
||||
<textarea name="action_confirmation_template" style="resize: vertical;"></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
{% trans "Unavailable action mode:" %}
|
||||
<select name="unavailable_action_mode">
|
||||
<option value="hide">{% trans "Hide action button" %}</option>
|
||||
<option value="disable">{% trans "Disable action button" %}</option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
<template class="as-table wcs-cards-cell--grid-cell-tpl">
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
from django.urls import path, re_path
|
||||
|
||||
from .views import TrackingCodeView, redirect_crypto_url, tracking_code_search
|
||||
from .views import TrackingCodeView, TriggerProxyView, redirect_crypto_url, tracking_code_search
|
||||
|
||||
urlpatterns = [
|
||||
path('tracking-code/', TrackingCodeView.as_view(), name='wcs-tracking-code'),
|
||||
|
@ -26,4 +26,5 @@ urlpatterns = [
|
|||
redirect_crypto_url,
|
||||
name='wcs-redirect-crypto-url',
|
||||
),
|
||||
path('api/wcs/trigger/', TriggerProxyView.as_view(), name='wcs-trigger-proxy'),
|
||||
fpeters
commented
Ça peut se faire avec path() tout simplement, plutôt que re_path(). Ça peut se faire avec path() tout simplement, plutôt que re_path().
|
||||
]
|
||||
|
|
|
@ -26,6 +26,9 @@ from django.http import HttpResponseBadRequest, HttpResponseForbidden, HttpRespo
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.generic import View
|
||||
from rest_framework import authentication, permissions
|
||||
from rest_framework.generics import GenericAPIView
|
||||
from rest_framework.response import Response
|
||||
|
||||
from combo.utils import DecryptionError, aes_hex_decrypt, requests, sign_url
|
||||
from combo.utils.misc import get_known_service_for_url, is_url_from_known_service
|
||||
|
@ -154,3 +157,13 @@ def redirect_crypto_url(request, session_key, crypto_url):
|
|||
real_url += '&orig=%s' % service['orig']
|
||||
redirect_url = sign_url(real_url, service['secret'], nonce=False)
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
|
||||
|
||||
class TriggerProxyView(GenericAPIView):
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
authentication_classes = (authentication.SessionAuthentication,)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
trigger_url = request.data['trigger-url']
|
||||
response = requests.post(trigger_url, remote_service='auto', user=request.user)
|
||||
return Response(response.json())
|
||||
|
|
|
@ -431,9 +431,11 @@ $(function() {
|
|||
var sel1 = '[data-dynamic-display-child-of="' + $(this).attr('name') + '"]';
|
||||
var sel2 = '[data-dynamic-display-value="' + $(this).val() + '"]';
|
||||
var sel3 = '[data-dynamic-display-value-in*=" ' + $(this).val() + ' "]';
|
||||
fpeters
commented
Dans wcs le cas des cases à cocher est déjà géré, différemment, (cf wcs/qommon/static/js/qommon.js), ça serait bien de poser le même fonctionnement ici, pour faciliter à un moment la convergence dans gadjo. Dans wcs le cas des cases à cocher est déjà géré, différemment, (cf wcs/qommon/static/js/qommon.js), ça serait bien de poser le même fonctionnement ici, pour faciliter à un moment la convergence dans gadjo.
|
||||
var sel4 = '[data-dynamic-display-checked="' + $(this).prop('checked') + '"]';
|
||||
$(sel1).addClass('field-hidden').hide();
|
||||
$(sel1 + sel2).removeClass('field-hidden').show();
|
||||
$(sel1 + sel3).removeClass('field-hidden').show();
|
||||
$(sel1 + sel4).removeClass('field-hidden').show();
|
||||
$(sel1).trigger('change');
|
||||
});
|
||||
$('[data-dynamic-display-child-of]').addClass('field-hidden').hide();
|
||||
|
@ -667,9 +669,12 @@ Card_cell_custom.prototype = {
|
|||
// set cell text
|
||||
let schema_field = this.field_with_varname(schema_cell.varname);
|
||||
let cell_text = "";
|
||||
if (schema_field || schema_cell.varname == '@custom@' || schema_cell.varname == '@link@') {
|
||||
if (schema_field || schema_cell.varname == '@custom@' || schema_cell.varname == '@link@' || schema_cell.varname == '@action@') {
|
||||
let cell_content = "";
|
||||
if (schema_cell.varname == '@custom@') {
|
||||
if (schema_cell.varname == '@action@') {
|
||||
cell_content += $(this.grid_cell_form).find('select[name="trigger_id"] option[value="' + schema_cell.trigger_id + '"]').text();
|
||||
cell_content += ' (' + gettext('Action Button') + ')';
|
||||
} else if (schema_cell.varname == '@custom@') {
|
||||
cell_content = (schema_cell.template || '') + ' (' + gettext('Custom') + ')';
|
||||
} else if (schema_cell.varname == '@link@') {
|
||||
cell_content = (schema_cell.template || '') + ': ';
|
||||
|
@ -695,7 +700,10 @@ Card_cell_custom.prototype = {
|
|||
cell_text += $('<span/>').addClass(schema_cell.display_mode).text(cell_content).html();
|
||||
cell_text += '<span class="cell-meta">';
|
||||
let cell_display_mode_label = '';
|
||||
if (schema_cell.varname == '@custom@') {
|
||||
if(schema_cell.varname == '@action@') {
|
||||
cell_display_mode_label = undefined
|
||||
}
|
||||
else if (schema_cell.varname == '@custom@') {
|
||||
cell_display_mode_label = $(this.grid_cell_form).find('select[name="custom_display_mode"] option[value="' + (schema_cell.display_mode || 'label') + '"]').text();
|
||||
} else if (schema_cell.varname == '@link@') {
|
||||
cell_display_mode_label = $(this.grid_cell_form).find('select[name="link_display_mode"] option[value="' + schema_cell.display_mode + '"]').text();
|
||||
|
@ -748,7 +756,16 @@ Card_cell_custom.prototype = {
|
|||
}
|
||||
|
||||
if (this.display_mode === 'card') {
|
||||
if (grid_cell.dataset.varname == '@custom@') {
|
||||
if (grid_cell.dataset.varname == '@action@') {
|
||||
this.grid_cell_form.elements.entry_type.value = '@action@';
|
||||
this.grid_cell_form.elements.action_label.value = grid_cell.dataset.action_label || '';
|
||||
this.grid_cell_form.elements.trigger_id.value = grid_cell.dataset.trigger_id || '';
|
||||
this.grid_cell_form.elements.action_ask_confirmation.value = 'true';
|
||||
this.grid_cell_form.elements.action_ask_confirmation.checked = grid_cell.dataset.action_ask_confirmation == 'true';
|
||||
this.grid_cell_form.elements.action_confirmation_template.value = grid_cell.dataset.action_confirmation_template || '';
|
||||
this.grid_cell_form.elements.unavailable_action_mode.value = grid_cell.dataset.unavailable_action_mode || '';
|
||||
}
|
||||
else if (grid_cell.dataset.varname == '@custom@') {
|
||||
this.grid_cell_form.elements.entry_type.value = '@custom@';
|
||||
this.grid_cell_form.elements.custom_template.value = grid_cell.dataset.template || '';
|
||||
this.grid_cell_form.elements.custom_display_mode.value = grid_cell.dataset.display_mode || 'label';
|
||||
|
@ -791,7 +808,17 @@ Card_cell_custom.prototype = {
|
|||
this.grid_cell_form.elements.cell_size.value = grid_cell.dataset.cell_size || '';
|
||||
|
||||
} else if (this.display_mode === 'table') {
|
||||
if (grid_cell.dataset.varname == '@custom@') {
|
||||
if (grid_cell.dataset.varname == '@action@') {
|
||||
this.grid_cell_form.elements.entry_type.value = '@action@';
|
||||
this.grid_cell_form.elements.action_header.value = grid_cell.dataset.header || '';
|
||||
this.grid_cell_form.elements.action_label.value = grid_cell.dataset.action_label || '';
|
||||
this.grid_cell_form.elements.trigger_id.value = grid_cell.dataset.trigger_id || '';
|
||||
this.grid_cell_form.elements.action_ask_confirmation.value = 'true';
|
||||
this.grid_cell_form.elements.action_ask_confirmation.checked = grid_cell.dataset.action_ask_confirmation == 'true';
|
||||
this.grid_cell_form.elements.action_confirmation_template.value = grid_cell.dataset.action_confirmation_template || '';
|
||||
this.grid_cell_form.elements.unavailable_action_mode.value = grid_cell.dataset.unavailable_action_mode || '';
|
||||
}
|
||||
else if (grid_cell.dataset.varname == '@custom@') {
|
||||
this.grid_cell_form.elements.entry_type.value = '@custom@';
|
||||
if (this.display_mode == 'table') {
|
||||
this.grid_cell_form.elements.custom_header.value = grid_cell.dataset.header || '';
|
||||
|
@ -862,7 +889,15 @@ Card_cell_custom.prototype = {
|
|||
for (var data in schema_cell) delete schema_cell[data];
|
||||
|
||||
if (this.display_mode == 'card') {
|
||||
if (form_datas.entry_type == '@custom@') {
|
||||
if (form_datas.entry_type == '@action@') {
|
||||
schema_cell.varname = '@action@';
|
||||
schema_cell.trigger_id = form_datas.trigger_id;
|
||||
schema_cell.action_label = form_datas.action_label;
|
||||
schema_cell.action_ask_confirmation = form_datas.action_ask_confirmation == 'true';
|
||||
schema_cell.action_confirmation_template = form_datas.action_confirmation_template;
|
||||
schema_cell.unavailable_action_mode = form_datas.unavailable_action_mode;
|
||||
}
|
||||
else if (form_datas.entry_type == '@custom@') {
|
||||
schema_cell.varname = '@custom@';
|
||||
schema_cell.display_mode = form_datas.custom_display_mode;
|
||||
schema_cell.template = form_datas.custom_template;
|
||||
|
@ -909,7 +944,16 @@ Card_cell_custom.prototype = {
|
|||
schema_cell.cell_size = form_datas.cell_size;
|
||||
|
||||
} else if (this.display_mode == 'table') {
|
||||
if (form_datas.entry_type == '@custom@') {
|
||||
if (form_datas.entry_type == '@action@') {
|
||||
schema_cell.varname = '@action@';
|
||||
schema_cell.header = form_datas.action_header;
|
||||
schema_cell.trigger_id = form_datas.trigger_id;
|
||||
schema_cell.action_label = form_datas.action_label;
|
||||
schema_cell.action_ask_confirmation = form_datas.action_ask_confirmation == 'true';
|
||||
schema_cell.action_confirmation_template = form_datas.action_confirmation_template;
|
||||
schema_cell.unavailable_action_mode = form_datas.unavailable_action_mode;
|
||||
}
|
||||
else if (form_datas.entry_type == '@custom@') {
|
||||
schema_cell.varname = '@custom@';
|
||||
schema_cell.header = form_datas.custom_header;
|
||||
schema_cell.template = form_datas.custom_template;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { expect, test, vi} from 'vitest'
|
||||
import { expect, test } from 'vitest'
|
||||
|
||||
test('dummy test', async () => {
|
||||
expect(true).toBe(true)
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import { test } from 'vitest'
|
||||
|
||||
export const domTest = test.extend({
|
||||
// Empty {} is required by vitest
|
||||
// eslint-disable-next-line no-empty-pattern
|
||||
appendToDom: async ({}, use) => {
|
||||
const wrappers = []
|
||||
const appendToDom = (htmlContent) => {
|
||||
const wrapper = document.createElement('div')
|
||||
wrapper.innerHTML = htmlContent
|
||||
document.appendChild(wrapper)
|
||||
wrappers.push(wrapper)
|
||||
return wrapper
|
||||
}
|
||||
await use(appendToDom)
|
||||
for (const wrapper of wrappers) {
|
||||
wrapper.remove()
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export async function flushPromises () {
|
||||
await new Promise((resolve) => setTimeout(resolve))
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
import { expect, vi} from 'vitest'
|
||||
import { domTest, flushPromises } from '../test-utils.js'
|
||||
import '../../../combo/apps/wcs/static/js/combo.wcs-trigger-button.js'
|
||||
|
||||
domTest('render button label', async ({appendToDom}) => {
|
||||
let dom = appendToDom(`
|
||||
<wcs-trigger-button label="Test label"></wcs/trigger-button>
|
||||
`)
|
||||
const innerButton = dom.querySelector('button')
|
||||
expect(innerButton.innerText).toBe('Test label')
|
||||
})
|
||||
|
||||
domTest('show button if trigger-url attribute is set', async ({appendToDom}) => {
|
||||
const dom = appendToDom(`
|
||||
<wcs-trigger-button label="Test label" trigger-url="https://dummy.org"></wcs/trigger-button>
|
||||
`)
|
||||
const triggerButton = dom.querySelector('wcs-trigger-button')
|
||||
const innerButton = dom.querySelector('button')
|
||||
expect(triggerButton.hidden).toBe(false)
|
||||
expect(innerButton.disabled).toBe(false)
|
||||
})
|
||||
|
||||
domTest('hide button if trigger-url is not set and unavailable-mode is hide', async ({appendToDom}) => {
|
||||
const dom = appendToDom(`
|
||||
<wcs-trigger-button unavailable-mode="hide"></wcs/trigger-button>
|
||||
`)
|
||||
const triggerButton = dom.querySelector('wcs-trigger-button')
|
||||
const innerButton = dom.querySelector('button')
|
||||
expect(triggerButton.hidden).toBe(true)
|
||||
expect(innerButton.disabled).toBe(false)
|
||||
})
|
||||
|
||||
domTest('disable button if trigger-url is not set and unavailable-mode is disable', async ({appendToDom}) => {
|
||||
const dom = appendToDom(`
|
||||
<wcs-trigger-button unavailable-mode="disable"></wcs/trigger-button>
|
||||
`)
|
||||
const triggerButton = dom.querySelector('wcs-trigger-button')
|
||||
const innerButton = dom.querySelector('button')
|
||||
expect(triggerButton.hidden).toBe(false)
|
||||
expect(innerButton.disabled).toBe(true)
|
||||
})
|
||||
|
||||
export const triggerTest = domTest.extend({
|
||||
clickTrigger: async ({appendToDom}, use) => {
|
||||
const load = async (domContent, mockFetch) => {
|
||||
const dom = appendToDom(domContent)
|
||||
|
||||
fetch.mockImplementationOnce(mockFetch)
|
||||
|
||||
const innerButton = dom.querySelector('button')
|
||||
innerButton.dispatchEvent(new Event('click', {bubbles: true}))
|
||||
await flushPromises()
|
||||
|
||||
return dom
|
||||
}
|
||||
|
||||
const alertBackup = global.alert
|
||||
const confirmBackup = global.confirm
|
||||
const fetchBackup = global.fetch
|
||||
|
||||
global.fetch = vi.fn()
|
||||
global.alert = vi.fn()
|
||||
global.confirm = vi.fn()
|
||||
|
||||
await use(load)
|
||||
|
||||
global.fetch = fetchBackup
|
||||
global.confirm = confirmBackup
|
||||
global.alert = alertBackup
|
||||
},
|
||||
})
|
||||
|
||||
triggerTest('trigger is called on button click', async ({clickTrigger}) => {
|
||||
const dom = await clickTrigger(
|
||||
`<wcs-trigger-button
|
||||
trigger-url="https://trigger.test"
|
||||
csrf-token="test-csrf-token"
|
||||
proxy-url="https://proxy.test">
|
||||
</wcs/trigger-button>`,
|
||||
async (url, options) => {
|
||||
expect(url).toBe('https://proxy.test')
|
||||
expect(options.headers['X-CSRFToken']).toBe('test-csrf-token')
|
||||
expect(options.method).toBe('POST')
|
||||
expect(JSON.parse(options.body)).toStrictEqual({'trigger-url': 'https://trigger.test'})
|
||||
return {ok: true, json: async () => ({}) }
|
||||
},
|
||||
)
|
||||
|
||||
const innerButton = dom.querySelector('wcs-trigger-button button')
|
||||
expect(fetch).toHaveBeenCalledOnce()
|
||||
// Button should be disabled after successfull call
|
||||
expect(innerButton.disabled).toBe(true)
|
||||
})
|
||||
|
||||
triggerTest('error message is shown on unsuccessfull trigger call', async ({clickTrigger}) => {
|
||||
const dom = await clickTrigger(
|
||||
'<wcs-trigger-button error-message="Error message"></wcs/trigger-button>',
|
||||
async () => ({ok: false, json: async () => ({}) }),
|
||||
)
|
||||
|
||||
expect(fetch).toHaveBeenCalledOnce()
|
||||
expect(alert).toHaveBeenCalledWith('Error message')
|
||||
const innerButton = dom.querySelector('wcs-trigger-button button')
|
||||
expect(innerButton.disabled).toBe(false)
|
||||
})
|
||||
|
||||
triggerTest('confirmation message is shown if set', async ({clickTrigger}) => {
|
||||
confirm.mockImplementationOnce(() => true)
|
||||
await clickTrigger(
|
||||
`<wcs-trigger-button confirmation-message="Confirmation">
|
||||
</wcs/trigger-button>`,
|
||||
async () => ({ok: true, json: async () => ({}) }),
|
||||
)
|
||||
|
||||
expect(confirm).toHaveBeenCalledWith('Confirmation')
|
||||
expect(fetch).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
triggerTest('trigger is not called if confirmation is dismissed', async ({clickTrigger}) => {
|
||||
confirm.mockImplementationOnce(() => false)
|
||||
await clickTrigger(
|
||||
`<wcs-trigger-button confirmation-message="Confirmation">
|
||||
</wcs/trigger-button>`,
|
||||
)
|
||||
|
||||
expect(confirm).toHaveBeenCalledWith('Confirmation')
|
||||
expect(fetch).not.toHaveBeenCalledOnce()
|
||||
})
|
|
@ -932,7 +932,7 @@ def test_card_cell_table_mode_render(mock_send, context, app):
|
|||
assert len(requests_get.call_args_list) == 1
|
||||
assert (
|
||||
requests_get.call_args_list[0][0][0]
|
||||
== '/api/cards/card_model_1/list/foo?include-fields=on&include-submission=on&include-workflow=on&filter-internal-id=11'
|
||||
== '/api/cards/card_model_1/list/foo?include-fields=on&include-submission=on&include-workflow=on&include-actions=on&filter-internal-id=11'
|
||||
)
|
||||
assert requests_get.call_args_list[0][1]['remote_service']['url'] == 'http://127.0.0.1:8999/'
|
||||
|
||||
|
@ -944,7 +944,7 @@ def test_card_cell_table_mode_render(mock_send, context, app):
|
|||
assert len(requests_get.call_args_list) == 1
|
||||
assert (
|
||||
requests_get.call_args_list[0][0][0]
|
||||
== '/api/cards/card_model_1/list?include-fields=on&include-submission=on&include-workflow=on&filter-internal-id=11'
|
||||
== '/api/cards/card_model_1/list?include-fields=on&include-submission=on&include-workflow=on&include-actions=on&filter-internal-id=11'
|
||||
)
|
||||
assert requests_get.call_args_list[0][1]['remote_service']['url'] == 'http://127.0.0.1:8999/'
|
||||
|
||||
|
@ -1299,6 +1299,55 @@ def test_card_cell_table_mode_render_custom_schema_link_entry(mock_send, context
|
|||
assert PyQuery(result).find('table tr td a') == []
|
||||
|
||||
|
||||
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
||||
def test_card_cell_table_mode_render_custom_schema_action_entry(mock_send, context):
|
||||
page = Page.objects.create(title='xxx', template_name='standard')
|
||||
cell = WcsCardCell.objects.create(
|
||||
page=page,
|
||||
placeholder='content',
|
||||
order=0,
|
||||
carddef_reference='default:card_model_1',
|
||||
custom_schema={
|
||||
'cells': [
|
||||
{
|
||||
'varname': '@action@',
|
||||
'trigger_id': 'jump:trigger-1',
|
||||
'action_label': 'Label {{ card.fields.fielda }}',
|
||||
'action_ask_confirmation': True,
|
||||
'action_confirmation_template': 'Confirmation {{ card.fields.fielda }}',
|
||||
'unavailable_action_mode': 'hide',
|
||||
},
|
||||
]
|
||||
},
|
||||
display_mode='table',
|
||||
related_card_path='__all__',
|
||||
)
|
||||
|
||||
request = RequestFactory().get('/')
|
||||
cell.modify_global_context(context, request)
|
||||
context['synchronous'] = True # to get fresh content
|
||||
|
||||
result = cell.render(context)
|
||||
button = PyQuery(result).find('table tr:first-child td:first-child wcs-trigger-button')
|
||||
assert button.attr['label'] == 'Label <i>a</i>'
|
||||
assert button.attr['proxy-url'] == reverse('wcs-trigger-proxy')
|
||||
assert button.attr['trigger-url'] == 'https://jump.test/trigger-1'
|
||||
assert button.attr['confirmation-message'] == 'Confirmation <i>a</i>'
|
||||
assert button.attr['unavailable-mode'] == 'hide'
|
||||
|
||||
cell.custom_schema['cells'][0]['trigger_id'] = 'unavailable-trigger'
|
||||
cell.save()
|
||||
result = cell.render(context)
|
||||
button = PyQuery(result).find('table tr:first-child td:first-child wcs-trigger-button')
|
||||
assert button.attr['trigger-url'] is None
|
||||
|
||||
cell.custom_schema['cells'][0]['action_ask_confirmation'] = False
|
||||
cell.save()
|
||||
result = cell.render(context)
|
||||
button = PyQuery(result).find('table tr:first-child td:first-child wcs-trigger-button')
|
||||
assert button.attr['action_confirmation_template'] is None
|
||||
|
||||
|
||||
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
||||
@pytest.mark.parametrize('with_headers', [True, False])
|
||||
def test_card_cell_table_mode_render_with_headers(mock_send, context, with_headers):
|
||||
|
@ -1339,6 +1388,7 @@ def test_card_cell_table_mode_render_with_headers(mock_send, context, with_heade
|
|||
{'varname': 'info:last_update_time'},
|
||||
{'varname': 'info:status'},
|
||||
{'varname': 'info:text'},
|
||||
{'varname': '@action@', 'header': 'Action', 'action_label': '', 'trigger_id': ''},
|
||||
],
|
||||
},
|
||||
display_mode='table',
|
||||
|
@ -1351,7 +1401,7 @@ def test_card_cell_table_mode_render_with_headers(mock_send, context, with_heade
|
|||
|
||||
result = cell.render(context)
|
||||
if with_headers:
|
||||
assert len(PyQuery(result).find('table thead th')) == 14
|
||||
assert len(PyQuery(result).find('table thead th')) == 15
|
||||
assert PyQuery(result).find('table thead th:nth-child(1)').text() == ''
|
||||
assert PyQuery(result).find('table thead th:nth-child(2)').text() == 'My Custom Header'
|
||||
assert PyQuery(result).find('table thead th:nth-child(3)').text() == 'Field B'
|
||||
|
@ -1366,6 +1416,7 @@ def test_card_cell_table_mode_render_with_headers(mock_send, context, with_heade
|
|||
assert PyQuery(result).find('table thead th:nth-child(12)').text() == 'Last modified'
|
||||
assert PyQuery(result).find('table thead th:nth-child(13)').text() == 'Status'
|
||||
assert PyQuery(result).find('table thead th:nth-child(14)').text() == 'Text'
|
||||
assert PyQuery(result).find('table thead th:nth-child(15)').text() == 'Action'
|
||||
else:
|
||||
assert PyQuery(result).find('table thead') == []
|
||||
|
||||
|
@ -1879,7 +1930,7 @@ def test_card_cell_list_mode_render(mock_send, context, app):
|
|||
assert len(requests_get.call_args_list) == 1
|
||||
assert (
|
||||
requests_get.call_args_list[0][0][0]
|
||||
== '/api/cards/card_model_1/list/foo?include-fields=on&include-submission=on&include-workflow=on&filter-internal-id=11'
|
||||
== '/api/cards/card_model_1/list/foo?include-fields=on&include-submission=on&include-workflow=on&include-actions=on&filter-internal-id=11'
|
||||
)
|
||||
assert requests_get.call_args_list[0][1]['remote_service']['url'] == 'http://127.0.0.1:8999/'
|
||||
|
||||
|
@ -1891,7 +1942,7 @@ def test_card_cell_list_mode_render(mock_send, context, app):
|
|||
assert len(requests_get.call_args_list) == 1
|
||||
assert (
|
||||
requests_get.call_args_list[0][0][0]
|
||||
== '/api/cards/card_model_1/list?include-fields=on&include-submission=on&include-workflow=on&filter-internal-id=11'
|
||||
== '/api/cards/card_model_1/list?include-fields=on&include-submission=on&include-workflow=on&include-actions=on&filter-internal-id=11'
|
||||
)
|
||||
assert requests_get.call_args_list[0][1]['remote_service']['url'] == 'http://127.0.0.1:8999/'
|
||||
|
||||
|
@ -3220,6 +3271,56 @@ def test_card_cell_card_mode_render_custom_schema_link_entry(mock_send, context,
|
|||
assert PyQuery(result).find('.value a') == []
|
||||
|
||||
|
||||
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
||||
def test_card_cell_card_mode_render_custom_schema_action_entry(mock_send, context, app):
|
||||
page = Page.objects.create(title='xxx', template_name='standard')
|
||||
cell = WcsCardCell.objects.create(
|
||||
page=page,
|
||||
placeholder='content',
|
||||
order=0,
|
||||
carddef_reference='default:card_model_1',
|
||||
custom_schema={
|
||||
'cells': [
|
||||
{
|
||||
'varname': '@action@',
|
||||
'trigger_id': 'jump:trigger-1',
|
||||
'action_label': 'Label {{ card.fields.fielda }}',
|
||||
'action_ask_confirmation': True,
|
||||
'action_confirmation_template': 'Confirmation {{ card.fields.fielda }}',
|
||||
'unavailable_action_mode': 'hide',
|
||||
},
|
||||
]
|
||||
},
|
||||
related_card_path='',
|
||||
)
|
||||
|
||||
context['card_model_1_id'] = 11
|
||||
request = RequestFactory().get('/')
|
||||
cell.modify_global_context(context, request)
|
||||
cell.repeat_index = 0
|
||||
context['synchronous'] = True # to get fresh content
|
||||
|
||||
result = cell.render(context)
|
||||
button = PyQuery(result).find('wcs-trigger-button')
|
||||
assert button.attr['label'] == 'Label <i>a</i>'
|
||||
assert button.attr['proxy-url'] == reverse('wcs-trigger-proxy')
|
||||
assert button.attr['trigger-url'] == 'https://jump.test/trigger-1'
|
||||
assert button.attr['confirmation-message'] == 'Confirmation <i>a</i>'
|
||||
assert button.attr['unavailable-mode'] == 'hide'
|
||||
|
||||
cell.custom_schema['cells'][0]['trigger_id'] = 'unavailable-trigger'
|
||||
cell.save()
|
||||
result = cell.render(context)
|
||||
button = PyQuery(result).find('wcs-trigger-button')
|
||||
assert button.attr['trigger-url'] is None
|
||||
|
||||
cell.custom_schema['cells'][0]['action_ask_confirmation'] = False
|
||||
cell.save()
|
||||
result = cell.render(context)
|
||||
button = PyQuery(result).find('wcs-trigger-button')
|
||||
assert button.attr['action_confirmation_template'] is None
|
||||
|
||||
|
||||
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
||||
def test_card_cell_card_mode_render_all_cards(mock_send, nocache, app):
|
||||
page = Page.objects.create(title='xxx', slug='foo', template_name='standard')
|
||||
|
|
|
@ -166,6 +166,7 @@ WCS_CARDS_DATA = {
|
|||
'item_raw': 'foo',
|
||||
},
|
||||
},
|
||||
'actions': {'jump:trigger-1': 'https://jump.test/trigger-1'},
|
||||
},
|
||||
{
|
||||
'id': 12,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { fileURLToPath, URL } from 'node:url'
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
|
@ -10,7 +9,7 @@ export default defineConfig({
|
|||
all: true,
|
||||
reporter: ['cobertura', 'html'],
|
||||
},
|
||||
environment: 'happy-dom'
|
||||
}
|
||||
environment: 'happy-dom',
|
||||
},
|
||||
})
|
||||
|
||||
|
|
Désolé question javascript, ça correspond à quoi ce #triggerUrl ?
C'est des variables privées en JS (vraiment privée, ça lève une erreur de syntaxe si on y accède en dehors de la classe, ou si elle n'est pas déclarée dans la classe).
Pas vraiment d'avis définitif sur si c'est bien ou pas, c'est probablement une habitude que je tiens du C++ : je ne vois pas d'inconvénient à en faire des membres "standard".