-
Notifications
You must be signed in to change notification settings - Fork 140
Popover error message #2739
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Popover error message #2739
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -305,6 +305,29 @@ export function processInclude(node: MbNode, context: Context, pageSources: Page | |||||||
| return childContext; | ||||||||
| } | ||||||||
|
|
||||||||
| function createPopoverInlineErrorSlot(message: string): NodeOrText[] { | ||||||||
| return [ | ||||||||
| { | ||||||||
| type: 'tag', | ||||||||
| name: 'template', | ||||||||
| attribs: { slot: 'content' }, | ||||||||
| children: [ | ||||||||
| { | ||||||||
| type: 'tag', | ||||||||
| name: 'div', | ||||||||
| attribs: { style: 'color: red;' }, | ||||||||
| children: [ | ||||||||
| { | ||||||||
| type: 'text', | ||||||||
| data: message, | ||||||||
| }, | ||||||||
| ], | ||||||||
| }, | ||||||||
| ], | ||||||||
| }, | ||||||||
| ]; | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * PreProcesses popovers with the src attribute. | ||||||||
| * Replaces it with an error node if the specified src is invalid. | ||||||||
|
|
@@ -318,9 +341,11 @@ export function processPopoverSrc(node: MbNode, context: Context, pageSources: P | |||||||
| } | ||||||||
|
|
||||||||
| if (_.isEmpty(node.attribs.src)) { | ||||||||
| const error = new Error(`Empty src attribute in popover in: ${context.cwf}`); | ||||||||
| logger.error(error.message); | ||||||||
| cheerio(node).replaceWith(createErrorNode(node, error)); | ||||||||
| // const error = new Error(`Empty src attribute in popover in: ${context.cwf}`); | ||||||||
| const errorMsg = `Empty src attribute in popover in: ${context.cwf}`; | ||||||||
| logger.error(errorMsg); | ||||||||
| // cheerio(node).replaceWith(createErrorNode(node, error)); | ||||||||
| node.children = createPopoverInlineErrorSlot(errorMsg); | ||||||||
| return context; | ||||||||
| } | ||||||||
|
|
||||||||
|
|
@@ -334,14 +359,13 @@ export function processPopoverSrc(node: MbNode, context: Context, pageSources: P | |||||||
|
|
||||||||
| // No need to process url contents | ||||||||
| if (isUrl) { | ||||||||
| const error = new Error('URLs are not allowed in the \'src\' attribute'); | ||||||||
| logger.error(`${error.message} | ||||||||
| File: ${context.cwf} | ||||||||
| URL provided: ${node.attribs.src} | ||||||||
|
|
||||||||
| Please check the \`src\` attribute in the popover element. | ||||||||
| Ensure it doesn't contain a URL (e.g., "http://www.example.com").`); | ||||||||
| cheerio(node).replaceWith(createErrorNode(node, error)); | ||||||||
| const errorMsg | ||||||||
| = 'URLs are not allowed in the \'src\' attribute.<br>' | ||||||||
| + `File: ${context.cwf}<br>` | ||||||||
|
||||||||
| + `File: ${context.cwf}<br>` | |
| = 'URLs are not allowed in the \'src\' attribute.\n' | |
| + `File: ${context.cwf}\n` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it would be helpful to understand how the original implementation displays the error message in-place without touching the logic in the separate vue UI components
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,7 +4,12 @@ | |||||||||||||||||||
| data-mb-component-type="popover" | ||||||||||||||||||||
| tabindex="0" | ||||||||||||||||||||
| > | ||||||||||||||||||||
| <portal v-if="targetEl.id" :to="'popover:' + targetEl.id"> | ||||||||||||||||||||
|
|
||||||||||||||||||||
| <div v-if="localError" class="popover-error-message"> | ||||||||||||||||||||
| {{ localError }} | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
|
|
||||||||||||||||||||
| <portal v-if="targetEl.id && !localError" :to="'popover:' + targetEl.id"> | ||||||||||||||||||||
| <h3 v-if="hasHeader" class="popover-header"> | ||||||||||||||||||||
| <slot name="header"></slot> | ||||||||||||||||||||
| </h3> | ||||||||||||||||||||
|
|
@@ -13,7 +18,7 @@ | |||||||||||||||||||
| </div> | ||||||||||||||||||||
| </portal><!-- do not delete this comment, it is for the stray space issue (#2419) | ||||||||||||||||||||
| --><v-popover | ||||||||||||||||||||
| v-if="isMounted" | ||||||||||||||||||||
| v-if="isMounted && !localError" | ||||||||||||||||||||
| :auto-hide="!isInput" | ||||||||||||||||||||
| :triggers="triggers" | ||||||||||||||||||||
| :popper-triggers="triggers" | ||||||||||||||||||||
|
|
@@ -60,19 +65,28 @@ export default { | |||||||||||||||||||
| type: String, | ||||||||||||||||||||
| default: 'top', | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| src: { | ||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the popover expect this attribute? markbind/packages/core/src/html/includePanelProcessor.ts Lines 398 to 404 in f681976
Our processPopoverSrc upstream function deletes this attribute, would it be better that the vue component remains unaware of a potential |
||||||||||||||||||||
| type: String, | ||||||||||||||||||||
| default: '', | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| header: { | ||||||||||||||||||||
| type: String, | ||||||||||||||||||||
| default: '', | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| data() { | ||||||||||||||||||||
| return { | ||||||||||||||||||||
| targetEl: {}, | ||||||||||||||||||||
| isMounted: false, | ||||||||||||||||||||
| localError: '', | ||||||||||||||||||||
| }; | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| computed: { | ||||||||||||||||||||
| triggers() { | ||||||||||||||||||||
| return this.trigger.split(' '); | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| hasHeader() { | ||||||||||||||||||||
| return !!this.$slots.header; | ||||||||||||||||||||
| return !!this.$slots.header || this.header; | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| isInput() { | ||||||||||||||||||||
| return Boolean(this.$slots.default && this.$slots.default().some(vnode => vnode.type === 'input')); | ||||||||||||||||||||
|
|
@@ -81,6 +95,54 @@ export default { | |||||||||||||||||||
| mounted() { | ||||||||||||||||||||
| this.targetEl = this.$el; | ||||||||||||||||||||
| this.isMounted = true; | ||||||||||||||||||||
| this.$nextTick(() => { | ||||||||||||||||||||
| this.captureAndLocalizeErrors(); | ||||||||||||||||||||
| }); | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| methods: { | ||||||||||||||||||||
| captureAndLocalizeErrors() { | ||||||||||||||||||||
| // Method 1: Check for common error patterns in the DOM | ||||||||||||||||||||
| this.findAndHandleGlobalErrors(); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Method 2: Validate proactively | ||||||||||||||||||||
| this.validatePopover(); | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| findAndHandleGlobalErrors() { | ||||||||||||||||||||
| // Look for error messages that might have been injected elsewhere | ||||||||||||||||||||
| const popoverErrors = Array.from(document.querySelectorAll('.popover-error')) | ||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious, where do the elements with class popover-error originate from? I can't seem to find where such elements would be injected or modified upstream |
||||||||||||||||||||
| .filter(el => el.textContent.includes('popover') || el.textContent.includes('src')); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| popoverErrors.forEach((errorEl) => { | ||||||||||||||||||||
| if (this.$el.contains(errorEl)) { | ||||||||||||||||||||
| // Error is already in the right place | ||||||||||||||||||||
| return; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Check if this error belongs to our popover | ||||||||||||||||||||
| if (errorEl.textContent.includes(this.src) | ||||||||||||||||||||
| || errorEl.textContent.includes(this.header)) { | ||||||||||||||||||||
| this.localError = errorEl.textContent; | ||||||||||||||||||||
| errorEl.remove(); | ||||||||||||||||||||
|
||||||||||||||||||||
| errorEl.remove(); | |
| // Instead of removing the element, hide it to avoid breaking Vue's reactivity | |
| errorEl.style.display = 'none'; |
Copilot
AI
Aug 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Direct DOM querying with document.querySelectorAll breaks Vue's component encapsulation and reactive data flow. Consider using Vue's ref system or emitting events to parent components instead of global DOM manipulation.
| }); | |
| // Check for error messages within this component only | |
| const errorEl = this.$refs.popoverError; | |
| if (errorEl && (errorEl.textContent.includes('popover') || errorEl.textContent.includes('src'))) { | |
| // Optionally, further check if the error is related to this popover | |
| if (errorEl.textContent.includes(this.src) || errorEl.textContent.includes(this.header)) { | |
| this.localError = errorEl.textContent; | |
| } | |
| } |
Copilot
AI
Aug 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The immediately invoked function expression (IIFE) pattern (() => new URL(url))() is unnecessarily complex. Replace with new URL(url) directly since the constructor already throws on invalid URLs.
| (() => new URL(url))(); | |
| new URL(url); |
Copilot
AI
Aug 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The hardcoded color value #f00 is a magic number. Consider using a CSS custom property or a semantic color variable from your design system for better maintainability.
| color: #f00; | |
| color: var(--popover-error-color, #f00); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Inline styles with hardcoded colors should use CSS classes instead for consistency and maintainability. Consider using a CSS class like 'error-message' that can be styled centrally.