-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #801 from City-of-Helsinki/UHF-10464
UHF-10464 Cookie banner Admin UI
- Loading branch information
Showing
10 changed files
with
1,748 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
20 |
73 changes: 73 additions & 0 deletions
73
modules/hdbt_cookie_banner/assets/css/cookie-banner-admin-ui.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* 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; | ||
} |
207 changes: 207 additions & 0 deletions
207
modules/hdbt_cookie_banner/assets/js/cookie-banner-admin-ui.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
(function (Drupal, drupalSettings) { | ||
Drupal.behaviors.cookieBannerAdminUi = { | ||
attach: function attach() { | ||
|
||
// 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 | ||
}; | ||
|
||
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 | ||
bannerEditor.on('change', function() { | ||
if (!isUpdatingFromEditor) { | ||
const updatedData = bannerEditor.getValue(); | ||
textarea.value = JSON.stringify(updatedData, null, 2); | ||
} | ||
}); | ||
|
||
// Listen for manual changes in the textarea | ||
textarea.addEventListener('input', function() { | ||
try { | ||
const updatedTextareaData = JSON.parse(textarea.value); | ||
|
||
// Prevent triggering the editor change event | ||
isUpdatingFromEditor = true; | ||
bannerEditor.setValue(updatedTextareaData); | ||
isUpdatingFromEditor = false; | ||
} catch (e) { | ||
console.error('Invalid JSON in textarea:', e); | ||
} | ||
}); | ||
|
||
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); |
File renamed without changes.
Large diffs are not rendered by default.
Oops, something went wrong.
Oops, something went wrong.