Skip to content

Commit

Permalink
UHF-10464: Update editor with language settings and lates styles
Browse files Browse the repository at this point in the history
  • Loading branch information
Arkkimaagi committed Sep 4, 2024
1 parent 88fd1ca commit 019b6c8
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 42 deletions.
59 changes: 57 additions & 2 deletions modules/hdbt_cookie_banner/assets/css/cookie-banner-admin-ui.css
Original file line number Diff line number Diff line change
@@ -1,18 +1,73 @@
#editor_holder {
margin: 16px;
/* Set max width to form */
.hdbt-cookie-banner {
max-width: 1200px;
}

/* Not all form controls should be 100% wide, for example select elements are better at auto */
.form-control {
width: auto;
}

/* Only inputs should be 100% wide */
input.form-control {
width: 100%;
}

/* Hide unused cruft */
.json-editor-btntype-deletelast,
.json-editor-btntype-deleteall,
.h3.je-object__title:has([style*="display: none;"]+.sr-only),
.btn-group.je-object__controls {
display: none;
}

/* First level wells should not have grey background */
[data-schemapath="root"]>.well {
background: transparent;
border: 0 none;
padding: 0;
box-shadow: none;
}

/* Handle button width with grandparent grid that is inherited with subgrid */
div:has( > .je-object__container) {
display: grid;
grid-template-columns: [column-1] 1fr [column-2] auto;
}

/* Add separator line between elements */
.je-object__container + .je-object__container {
border-top: 1px solid #ccc;
}

/* Inherit the grid from grandparent and set grid rows here */
:not([data-schemaid="root"]).je-object__container {
grid-column: span 2;
display: grid;
grid-template-columns: subgrid;
grid-template-rows: [row-1] 1fr [row-2] auto;
}

/* By default, take two columns on all elements */
.je-object__container > * {
grid-column: 1 / span 2;
}

/* Title should be 1 column wide */
.je-object__container > .je-object__title {
grid-column: 1 / span 1;
grid-row: 1;
}

/* Btn group should be 1 column wide and on the first row */
.je-object__container > .btn-group {
grid-column: 2 / span 1;
grid-row: 1;
margin-top: 18px;
}

/* JSON Textarea size */
textarea {
width: 100%;
height: 90dvh;
}
204 changes: 178 additions & 26 deletions modules/hdbt_cookie_banner/assets/js/cookie-banner-admin-ui.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,119 @@
(function (Drupal, drupalSettings) {
Drupal.behaviors.cookieBannerAdminUi = {
attach: function attach() {
const element = document.getElementById('editor_holder');
const textarea = document.getElementById('edit-site-settings');
let isUpdatingFromEditor = false; // Flag to prevent loop

try {
const schema = JSON.parse(drupalSettings.cookieBannerAdminUi.siteSettingsSchema);
const startval = JSON.parse(textarea.value);

const options = {
theme: 'bootstrap3',
iconlib: 'bootstrap',
show_opt_in: true,
disable_edit_json: true,
disable_properties: true,
disable_array_delete_all_rows: true,
disable_array_delete_last_row: true,
prompt_before_delete: true,
schema: schema,
startval: startval,

// Small schema to handle languages
const languageSchema = {
"title": "Supported languages",
"description": "List all languages you wish banner to support.",
"type": "array",
"format": "table",
"options": {
"disable_collapse": true
},
"items": {
"type": "object",
"properties": {
"code": {
"type": "string",
"minLength": 2,
"title": "Code (eg. \"fi\")"
},
"name": {
"type": "string",
"minLength": 1,
"title": "Name (ex. \"Finnish\")"
}
},
"required": [
"code",
"name"
],
"title": "Language"
},
"uniqueItems": true,
"minItems": 1
}
const defaultLanguages = [
{ "code": "fi", "name": "Finnish" },
{ "code": "sv", "name": "Swedish" },
{ "code": "en", "name": "English" }
];

// JSON editor options for both forms
const editorOptions = {
theme: 'bootstrap3',
iconlib: 'bootstrap',
show_opt_in: true,
disable_edit_json: true,
disable_properties: true,
disable_array_delete_all_rows: true,
disable_array_delete_last_row: true,
keep_oneof_values: false,
prompt_before_delete: true,
}

/**
* Gets the schema object for site settings
* @returns {Promise} A promise that resolves with the schema object
*/
function getSchema(){
try {
const schema = JSON.parse(drupalSettings.cookieBannerAdminUi.siteSettingsSchema);
return schema;
} catch (error) {
console.error('Error fetching the schema:', error);
}
return {};
}

/**
* Initializes the language editor and returns a reference to it
* @param {object} defaultLanguages that contains code and name for each language
* @param {object} languageSchema JSON schema for the language editor
* @param {object} editorOptions for the JSON editor
* @returns reference to the language editor
*/
function initializeLanguageEditor(defaultLanguages, languageSchema, editorOptions){
const langOptions = {
...editorOptions,
schema: languageSchema,
startval: defaultLanguages
};

// Initialize the JSON Editor
const editor = new JSONEditor(element, options);
const languageElement = document.getElementById('language_holder');
const languageEditor = new JSONEditor(languageElement, langOptions);
return languageEditor;
}

/**
* Initializes the banner editor and returns a reference to it
* @param {object} schema JSON schema of siteSettings.json for the banner editor
* @param {object} editorOptions for the JSON editor
* @returns reference to the banner editor
*/
function initializeBannerEditor(schema, editorOptions){
let isUpdatingFromEditor = false; // Flag to prevent loop
let startval = {};
let textarea = null;
try {
textarea = document.getElementById('edit-site-settings');
startval = JSON.parse(textarea.value);
} catch (error) {
console.error('Error parsing the textarea value:', error);
}

const bannerElement = document.getElementById('editor_holder');
const bannerEditor = new JSONEditor(bannerElement, {
...editorOptions,
schema,
startval
});

// Listen for changes in the JSON editor
editor.on('change', function() {
bannerEditor.on('change', function() {
if (!isUpdatingFromEditor) {
const updatedData = editor.getValue();
const updatedData = bannerEditor.getValue();
textarea.value = JSON.stringify(updatedData, null, 2);
}
});
Expand All @@ -40,16 +125,83 @@

// Prevent triggering the editor change event
isUpdatingFromEditor = true;
editor.setValue(updatedTextareaData);
bannerEditor.setValue(updatedTextareaData);
isUpdatingFromEditor = false;
} catch (e) {
console.error('Invalid JSON in textarea:', e);
}
});

} catch (error) {
console.error('Error fetching the schema:', error);
return bannerEditor;
}

/**
* Updates the schema with the new languages
* @param {object} languages JSON generated by the language editor
* @param {object} schema JSON schema of siteSettings.json
* @returns {object} updated schema with the new languages
*/
function updateSchema(languages, schema){
const newSchema = schema;

const localisedText = {
"type": "object",
"title": "Localised text",
"properties": {},
"required": [],
"additionalProperties": false
};

const fallbackLanguageEnum = languages.map(lang => lang.code);
const fallbackLanguageEnumTitles = languages.map(lang => lang.code + " (" + lang.name + ")");

languages.forEach(lang => {
localisedText.properties[lang.code] = {
"type": "string",
"title": lang.code + " (" + lang.name + ")"
};
localisedText.required.push(lang.code);
});

newSchema["$defs"].LocalisedText = localisedText;
newSchema.properties.fallbackLanguage.enum = fallbackLanguageEnum;
newSchema.properties.fallbackLanguage.options.enum_titles = fallbackLanguageEnumTitles;

return newSchema;
}

/**
* Initializes the language and banner editors
*/
async function initializeEditor(){
const languageEditor = initializeLanguageEditor(defaultLanguages, languageSchema, editorOptions);
let schema = getSchema();
let bannerEditor = initializeBannerEditor(schema, editorOptions);

const debounce = (func, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(null, args);
}, delay);
};
};

languageEditor.on('change', debounce(function() {
const errors = languageEditor.validate();
if (!errors.length) {
const languages = languageEditor.getValue();
schema = updateSchema(languages, schema);
bannerEditor.destroy();
bannerEditor = initializeBannerEditor(schema, editorOptions);
}
}, 300));
}

// Initialize the editor once the page has loaded
window.onload = initializeEditor;

}
};
})(Drupal, drupalSettings);
29 changes: 16 additions & 13 deletions modules/hdbt_cookie_banner/assets/json/siteSettings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"expiration": {
"$ref": "#/$defs/LocalisedUniversalText",
"title": "Expiration",
"description": "Describe to end user when the cookie or item expires. (eg. \"100 days\") If it does not expire (like localStorage), enter dash \"-\". If it's a session cookie, enter fi:\"Istunto\", sv,en:\"Session\".",
"description": "Describe to end user when the cookie or item expires. (eg. \"100 days\") If it does not expire (like localStorage), enter dash \"-\". If it's a session cookie, enter fi:\"Istunto\", sv:\"Session\", en:\"Session\".",
"options": {
"disable_collapse": true
}
Expand Down Expand Up @@ -155,7 +155,7 @@
},
"cookies": {
"title": "Cookies",
"headerTemplate": "Cookies for {{groupId}} {{groupI}} {{self.groupI}} {{parent.groupId}} {{self.groupId}} {{parent.parent.groupId}} {{self.parent.groupId}}",
"headerTemplate": "Cookies",
"description": "Cookies or or items toggled together in this group",
"type": "array",
"items": {
Expand All @@ -180,6 +180,11 @@
]
}
},
"title": "Site settings",
"description": "Configure HDS cookie banner settings.json to adjust allowed cookies, translations and other features.",
"options": {
"disable_collapse": true
},
"type": "object",
"properties": {
"siteName": {
Expand All @@ -193,7 +198,10 @@
"$ref": "#/$defs/UniversalLocalisedText"
},
{
"default": ""
"default": "",
"options": {
"disable_collapse": true
}
}
]
},
Expand All @@ -210,7 +218,7 @@
"monitorInterval": {
"type": "integer",
"title": "Monitor interval (ms)",
"description": "How often banner checks for rogue cookies in milliseconds. Disabled by default. 500 ms could be a good default.",
"description": "How often banner checks for unallowed cookies in milliseconds. Disabled by default. 500 ms is a good default.",
"options": {
"input_width": "calc(5ch + 28px)"
},
Expand All @@ -233,9 +241,9 @@
],
"options": {
"enum_titles": [
"fi = Finnish",
"sv = Swedish",
"en = English"
"fi (Finnish)",
"sv (Swedish)",
"en (English)"
]
},
"default": "en"
Expand Down Expand Up @@ -817,10 +825,5 @@
"requiredGroups",
"optionalGroups",
"translations"
],
"title": "HDS cookie banner",
"description": "Configure HDS cookie banner settings.json to adjust allowed cookies, translations and other features.",
"options": {
"disable_collapse": true
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ protected function getEditableConfigNames(): array {
public function buildForm(array $form, FormStateInterface $form_state): array {
$form['json_editor'] = [
'#type' => 'markup',
'#markup' => '<div id="editor_holder"></div>',
'#markup' => '<h1>HDS Cookie Consent Settings</h1><div id="language_holder"></div><div id="editor_holder"></div>',
'#attached' => [
'library' => [
'hdbt_cookie_banner/cookie_banner_admin_ui',
Expand Down

0 comments on commit 019b6c8

Please sign in to comment.