diff --git a/.changeset/lemon-plums-nail.md b/.changeset/lemon-plums-nail.md new file mode 100644 index 00000000..214e48f6 --- /dev/null +++ b/.changeset/lemon-plums-nail.md @@ -0,0 +1,5 @@ +--- +"@wpengine/wpgraphql-logging-wordpress-plugin": patch +--- + +UI improvements for WPGraphQL Logging plugin: refactored styles, added GraphQL query formatting, and implemented unsaved changes warning diff --git a/plugins/wpgraphql-logging/assets/css/settings/wp-graphql-logging-settings.css b/plugins/wpgraphql-logging/assets/css/settings/wp-graphql-logging-settings.css index 432d6e61..9aa6514a 100644 --- a/plugins/wpgraphql-logging/assets/css/settings/wp-graphql-logging-settings.css +++ b/plugins/wpgraphql-logging/assets/css/settings/wp-graphql-logging-settings.css @@ -1,5 +1,5 @@ -.settings_page_wpgraphql-logging #poststuff .postbox .inside h2 { - font-size: 1.3em; +.graphql-logs_page_wpgraphql-logging #poststuff .postbox .inside h2 { + font-size: 16.9px; font-weight: 600; padding-left: 0; } diff --git a/plugins/wpgraphql-logging/assets/css/view/wp-graphql-logging-view.css b/plugins/wpgraphql-logging/assets/css/view/wp-graphql-logging-view.css new file mode 100644 index 00000000..07545139 --- /dev/null +++ b/plugins/wpgraphql-logging/assets/css/view/wp-graphql-logging-view.css @@ -0,0 +1,46 @@ +.wpgraphql-logging-view-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.wpgraphql-logging-details pre.wpgraphql-logging-query, +.wpgraphql-logging-details pre.wpgraphql-logging-context, +.wpgraphql-logging-details pre.wpgraphql-logging-extra { + white-space: pre-wrap; + max-height: 540px; + overflow-y: scroll; + padding: 15px; + border: 1px solid #ddd; + border-radius: 4px; +} + +pre.wpgraphql-logging-list-table-query { + overflow-x: auto; + background: #f4f4f4; + margin-top: 0; + margin-bottom: 0; + padding: 15px; + border: 1px solid #ddd; + border-radius: 4px; + max-height: 20px; +} + +.wpgraphql-logging-filters input.wpgraphql-logging-datepicker, +.wpgraphql-logging-filters select[name="level_filter"] { + width: 120px; +} + +.wpgraphql-logging-filters { + display: inline-flex; + align-items: center; + gap: 8px; + margin-right: 10px; +} + +.wpgraphql-logging-filters .clear-all-button { + margin: 0; + margin-left: 5px; + text-decoration: none; +} diff --git a/plugins/wpgraphql-logging/assets/js/settings/wp-graphql-logging-settings.js b/plugins/wpgraphql-logging/assets/js/settings/wp-graphql-logging-settings.js index 72c11d9f..b6b18778 100644 --- a/plugins/wpgraphql-logging/assets/js/settings/wp-graphql-logging-settings.js +++ b/plugins/wpgraphql-logging/assets/js/settings/wp-graphql-logging-settings.js @@ -1,22 +1,96 @@ -document.addEventListener('DOMContentLoaded', function() { - const sanitizationMethodSelect = document.querySelector("#data_sanitization_method"); - if (!sanitizationMethodSelect) { - return; +document.addEventListener("DOMContentLoaded", function () { + function listenForSanitizationMethodChange() { + const sanitizationMethodSelect = document.querySelector( + "#data_sanitization_method" + ); + + if (!sanitizationMethodSelect) { + return; + } + + function toggleCustomFields() { + const isCustom = sanitizationMethodSelect.value === "custom"; + const customElements = document.querySelectorAll( + ".wpgraphql-logging-custom" + ); + + customElements.forEach((el) => { + if (isCustom) { + el.classList.add("block"); + } else { + el.classList.remove("block"); + } + }); + } + + toggleCustomFields(); + sanitizationMethodSelect.addEventListener("change", toggleCustomFields); } - function toggleCustomFields() { - const isCustom = sanitizationMethodSelect.value === 'custom'; - const customElements = document.querySelectorAll('.wpgraphql-logging-custom'); + function listenForUnsavedChanges() { + const formElement = document.querySelector("form"); + + function getFormState(form) { + const data = new FormData(form); + return JSON.stringify(Array.from(data.entries())); + } - customElements.forEach((el) => { - if (isCustom) { - el.classList.add('block'); - } else { - el.classList.remove('block'); + // Save the initial state of the form for later comparison + const initialState = getFormState(formElement); + + // Warn the user if they try to leave with unsaved changes + function beforeUnload(e) { + const formState = getFormState(formElement); + + if (formState !== initialState) { + e.preventDefault(); + e.returnValue = true; } + } + + window.addEventListener("beforeunload", beforeUnload); + + // Remove the warning on submit so it doesn't appear when saving + formElement.addEventListener("submit", function () { + window.removeEventListener("beforeunload", beforeUnload); }); } - toggleCustomFields(); - sanitizationMethodSelect.addEventListener('change', toggleCustomFields); + function listenForLogPointsSelection() { + const logPointsInput = document.querySelector("#event_log_selection"); + const enableCheckbox = document.querySelector( + "input[name='wpgraphql_logging_settings[basic_configuration][enabled]']" + ); + + function checkLogPointsSelection() { + const anyLogPointsSelected = logPointsInput.selectedOptions.length > 0; + + const existingDescription = + logPointsInput.parentElement.querySelector(".description"); + if (existingDescription) { + existingDescription.remove(); + } + + // If the logging is enabled and no log points are selected, show a description + if (enableCheckbox?.checked && !anyLogPointsSelected) { + const description = document.createElement("p"); + + if (!logPointsInput.parentElement.querySelector(".description")) { + description.className = "description"; + description.textContent = + "If you don't select any log points, no data will be logged."; + description.style.marginLeft = "25px"; + logPointsInput.parentElement.appendChild(description); + } + } + } + + logPointsInput.addEventListener("change", checkLogPointsSelection); + enableCheckbox.addEventListener("change", checkLogPointsSelection); + checkLogPointsSelection(); + } + + listenForSanitizationMethodChange(); + listenForUnsavedChanges(); + listenForLogPointsSelection(); }); diff --git a/plugins/wpgraphql-logging/assets/js/view/wp-graphql-logging-view.js b/plugins/wpgraphql-logging/assets/js/view/wp-graphql-logging-view.js index 771a722b..0619db8c 100644 --- a/plugins/wpgraphql-logging/assets/js/view/wp-graphql-logging-view.js +++ b/plugins/wpgraphql-logging/assets/js/view/wp-graphql-logging-view.js @@ -4,3 +4,127 @@ jQuery(document).ready(function($) { timeFormat: "HH:mm:ss" }); }); + +document.addEventListener("DOMContentLoaded", function () { + function formatGraphQLQuery(query, indentSize = 2) { + if (!query || typeof query !== "string") return ""; + + try { + let formatted = query.trim().replace(/\s+/g, " "); + let indentLevel = 0, + result = "", + inString = false, + stringChar = null, + afterClosingParen = false; + const operationTypes = ["query", "mutation", "subscription", "fragment"]; + const indent = () => "\n" + " ".repeat(indentLevel * indentSize); + + for (let i = 0; i < formatted.length; i++) { + const char = formatted[i], + prev = formatted[i - 1], + next = formatted[i + 1]; + + // Track string literals + if ((char === '"' || char === "'") && prev !== "\\") { + if (!inString) { + inString = true; + stringChar = char; + } else if (char === stringChar) { + inString = false; + stringChar = null; + } + } + + if (inString) { + result += char; + continue; + } + + // Handle braces and parentheses + if (char === "{" || char === "(") { + result = result.trimEnd() + (char === "{" ? " {" : "("); + if (next !== (char === "{" ? "}" : ")")) { + indentLevel++; + result += indent(); + } + afterClosingParen = false; + } else if (char === "}" || char === ")") { + if (prev !== (char === "}" ? "{" : "(")) { + indentLevel--; + result = result.trimEnd() + indent(); + } + result += char; + afterClosingParen = char === ")"; + } else if (char === ",") { + result += "," + indent(); + afterClosingParen = false; + } else if (char === " ") { + const trimmed = result.trimEnd(); + const lastWord = + trimmed + .split(/[\s\n{}(),]/) + .filter((w) => w) + .pop() || ""; + const lastChar = trimmed.slice(-1); + + if ( + (afterClosingParen && next === "@") || + trimmed.endsWith("...") || + lastWord === "on" || + operationTypes.includes(lastWord) || + lastChar === ":" || + lastChar === "@" || + trimmed.match(/@\w+$/) + ) { + result += char; + } else { + const isBetweenFields = + lastChar && + /[a-zA-Z0-9_]/.test(lastChar) && + next && + /[a-zA-Z_]/.test(next) && + !["{", "}", "(", ")", ",", ":", "\n", "@", "."].includes(next); + + if (isBetweenFields) { + result = result.trimEnd() + indent(); + afterClosingParen = false; + } else if (!["\n", " "].includes(result.slice(-1))) { + result += char; + } + } + } else { + if ( + afterClosingParen && + char !== "@" && + char !== "{" && + char !== "}" + ) { + result = result.trimEnd() + " "; + } + result += char; + afterClosingParen = false; + } + } + + return result + .split("\n") + .map((line) => line.trimEnd()) + .join("\n"); + } catch (error) { + console.error("GraphQL formatting error:", error); + return query; + } + } + + const queryElements = document.querySelectorAll( + "pre.wpgraphql-logging-query" + ); + + if (queryElements.length > 0) { + queryElements.forEach(function (element) { + const rawQuery = element.textContent; + const formattedQuery = formatGraphQLQuery(rawQuery); + element.textContent = formattedQuery; + }); + } +}); diff --git a/plugins/wpgraphql-logging/src/Admin/View/List/ListTable.php b/plugins/wpgraphql-logging/src/Admin/View/List/ListTable.php index 588545dc..7ad25ec4 100644 --- a/plugins/wpgraphql-logging/src/Admin/View/List/ListTable.php +++ b/plugins/wpgraphql-logging/src/Admin/View/List/ListTable.php @@ -413,7 +413,7 @@ protected function format_code(string $code): string { if ( empty( $code ) ) { return ''; } - return '
' . esc_html( $code ) . ''; + return '
' . esc_html( $code ) . ''; } /** diff --git a/plugins/wpgraphql-logging/src/Admin/View/Templates/WPGraphQLLoggerFilters.php b/plugins/wpgraphql-logging/src/Admin/View/Templates/WPGraphQLLoggerFilters.php index 169e650c..ed71f7c5 100644 --- a/plugins/wpgraphql-logging/src/Admin/View/Templates/WPGraphQLLoggerFilters.php +++ b/plugins/wpgraphql-logging/src/Admin/View/Templates/WPGraphQLLoggerFilters.php @@ -22,24 +22,22 @@ $wpgraphql_logging_log_levels = apply_filters( 'wpgraphql_logging_log_levels', $wpgraphql_logging_log_levels ); ?> -
| @@ -61,15 +61,15 @@ | ||
|---|---|---|
| - | get_query() ); ?> |
+ get_query() ); ?> |
| - | get_context(), JSON_PRETTY_PRINT ) ); ?> |
+ get_context(), JSON_PRETTY_PRINT ) ); ?> |
| - | get_extra(), JSON_PRETTY_PRINT ) ); ?> |
+ get_extra(), JSON_PRETTY_PRINT ) ); ?> |
assertStringContainsString('test code', $result);
- $this->assertStringContainsString('overflow-x: auto', $result);
}
public function test_format_code_returns_empty_string_for_empty_input(): void {