UNPKG

@salesforce-ux/eslint-plugin-slds

Version:

ESLint plugin provides custom linting rules specifically built for Salesforce Lightning Design System 2 (SLDS 2 beta)

191 lines (190 loc) 10.4 kB
"use strict"; const node_1 = require("./utils/node"); const rule_1 = require("./utils/rule"); module.exports = { meta: { type: "problem", docs: { category: "Best Practices", recommended: true, description: "Update component attributes or CSS classes for the modal close button to comply with the modal component blueprint.", url: "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#modal-close-button-issue" }, fixable: "code", schema: [], }, create(context) { function check(node) { if ((0, node_1.isAttributesEmpty)(node)) { return; } const tagName = node.name; // ✅ Scenario 1: Remove 'slds-button_icon-inverse' from <button> // (optional) when the parent of the button has class name `slds-modal` // and also button should have class `slds-modal__close` if (tagName === "button") { const classAttr = (0, node_1.findAttr)(node, "class"); if (classAttr && classAttr.value) { const classList = classAttr.value.value.split(/\s+/); // ✅ Ensure button has "slds-modal__close" before proceeding if (!classList.includes("slds-modal__close")) { return; // Stop execution if the class is missing } if (classList.includes("slds-button_icon-inverse") || classList.includes("slds-button--icon-inverse")) { const newClassList = classList .filter((cls) => (cls !== "slds-button_icon-inverse" && cls !== "slds-button--icon-inverse")) .join(" "); context.report({ node, loc: classAttr.loc, message: rule_1.messages["removeClass"], fix(fixer) { return fixer.replaceText(classAttr, // Replace the full attribute `class="${newClassList}"` // Updated class list ); }, }); } } } // ✅ Scenario 2: Fix <lightning-button-icon> and this should have class `slds-modal__close` if (tagName === "lightning-button-icon" || tagName === "lightning:buttonIcon") { const variantAttr = (0, node_1.findAttr)(node, "variant"); const sizeAttr = (0, node_1.findAttr)(node, "size"); const classAttr = (0, node_1.findAttr)(node, "class"); const iconClassAttr = (0, node_1.findAttr)(node, "icon-class"); // 🔍 Check for icon-class attribute function validateClassAttr(attribute, attrName) { if (attribute && attribute.value) { const classList = attribute.value.value.split(/\s+/); // Irrespective of whether we are checking for class or icon-class we need to check whether the attribute is present or not. // ✅ Ensure "slds-modal__close" exists before proceeding if (!classAttr?.value?.value?.includes("slds-modal__close")) { return; } // ✅ Ensure "slds-modal__close" exists before proceeding // if (!classList.includes("slds-modal__close")) { // return; // Stop execution if the class is missing // } // Remove inverse classes if (classList.includes("slds-button_icon-inverse") || classList.includes("slds-button--icon-inverse")) { const newClassList = classList .filter((cls) => cls !== "slds-button_icon-inverse" && cls !== "slds-button--icon-inverse") .join(" "); context.report({ node, loc: attribute.loc, message: rule_1.messages["removeClass"], fix(fixer) { return fixer.replaceText(attribute, // Replace the full attribute `${attrName}="${newClassList}"` // Correctly modifies the respective attribute ); }, }); } // Ensure 'slds-button' and 'slds-button_icon' exist if (!classList.includes("slds-button") || !classList.includes("slds-button_icon")) { let newClassList; if (attrName === 'icon-class') { newClassList = [ ...classList.filter((cls) => cls !== "slds-button_icon-inverse"), ].join(" "); } else { newClassList = [ "slds-button", "slds-button_icon", ...classList.filter((cls) => cls !== "slds-button_icon-inverse"), ].join(" "); } context.report({ node: attribute, loc: attribute.value.loc, message: rule_1.messages["ensureButtonClasses"], fix(fixer) { return fixer.replaceText(attribute.value, `${newClassList}`); }, }); } // Fix variant="bare-inverse" to "bare" if (variantAttr && variantAttr.value && variantAttr.value.value === "bare-inverse") { context.report({ node: variantAttr, message: rule_1.messages["changeVariant"], loc: variantAttr.value.loc, fix(fixer) { return fixer.replaceText(variantAttr.value, `bare`); }, }); } // Ensure size="large" exists // if (!sizeAttr) { // context.report({ // node, // message: messages["ensureSizeAttribute"], // fix(fixer) { // if (variantAttr) { // return fixer.insertTextAfterRange([variantAttr.range[1], variantAttr.range[1]], ' size="large"'); // } // }, // }); // } } } // ✅ Validate `class` and `icon-class` separately, maintaining their own attribute names validateClassAttr(classAttr, "class"); validateClassAttr(iconClassAttr, "icon-class"); } // ✅ Scenario 3: Fix <lightning-icon> inside <button> & the class name of the parent name as button and it should have `slds-modal__close` if ((tagName === "lightning-icon" || tagName === "lightning:icon") && node.parent?.name === "button") { const parentClassAttr = (0, node_1.findAttr)(node.parent, "class"); if (parentClassAttr && parentClassAttr.value) { const parentClassList = parentClassAttr.value.value.split(/\s+/); // ✅ Ensure the parent <button> has "slds-modal__close" before proceeding if (!parentClassList.includes("slds-modal__close")) { return; // Stop execution if the class is missing } const variantAttr = (0, node_1.findAttr)(node, "variant"); const sizeAttr = (0, node_1.findAttr)(node, "size"); // Fix variant="bare-inverse" to "bare" if (variantAttr && variantAttr.value && variantAttr.value.value === "bare-inverse") { context.report({ node: variantAttr, message: rule_1.messages["changeVariant"], loc: variantAttr.value.loc, fix(fixer) { return fixer.replaceText(variantAttr.value, `bare`); }, }); } // // Remove variant attribute completely // if (variantAttr) { // context.report({ // node: variantAttr, // messageId: "removeVariant", // fix(fixer) { // return fixer.remove(variantAttr); // }, // }); // } //Ensure size="large" is set // if (!sizeAttr) { // context.report({ // node, // message: messages["ensureSizeAttribute"], // fix(fixer) { // //return fixer.insertTextAfter(node, ' size="large"'); // if(variantAttr) // { // return fixer.insertTextAfterRange([variantAttr.range[1], variantAttr.range[1]], ' size="large"') // } // }, // }); // } } } } return { Tag: check, }; }, };